mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
Merge remote-tracking branch
'origin/GP-1984_Dan_RegisterAndWatchDataTypeSettings--SQUASHED' Conflicts: Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
This commit is contained in:
commit
e999a24433
18 changed files with 540 additions and 67 deletions
|
@ -92,6 +92,12 @@
|
||||||
recorder, but they can still be populated by the user. Modifying the values of unknown
|
recorder, but they can still be populated by the user. Modifying the values of unknown
|
||||||
registers cannot affect the target. Register sets are memorized per compiler specification.</P>
|
registers cannot affect the target. Register sets are memorized per compiler specification.</P>
|
||||||
|
|
||||||
|
<H3><A name="type_settings"></A>Register Type Settings</H3>
|
||||||
|
|
||||||
|
<P>This action is available on the context menu when there is a single register selected with a
|
||||||
|
data type assigned. It permits the adjustment of that data type's settings, e.g., to display
|
||||||
|
decimal vs hexadecimal. The settings are saved to the data unit for the register.</P>
|
||||||
|
|
||||||
<H3><A name="enable_edits"></A>Enable Edits</H3>
|
<H3><A name="enable_edits"></A>Enable Edits</H3>
|
||||||
|
|
||||||
<P>This toggle is a write protector for machine state. To modify register values, this toggle
|
<P>This toggle is a write protector for machine state. To modify register values, this toggle
|
||||||
|
|
|
@ -109,6 +109,12 @@
|
||||||
listing. That is, it attempts to apply the selected data type to the evaluated address, sizing
|
listing. That is, it attempts to apply the selected data type to the evaluated address, sizing
|
||||||
it to the value's size.</P>
|
it to the value's size.</P>
|
||||||
|
|
||||||
|
<H3><A name="type_settings"></A>Watch Type Settings</H3>
|
||||||
|
|
||||||
|
<P>This action is available on the context menu when there is a single watch selected with a
|
||||||
|
data type assigned. It permits the adjustment of that data type's settings, e.g., to display
|
||||||
|
decimal vs. hexadecimal.</P>
|
||||||
|
|
||||||
<H3><A name="select_addresses"></A>Select Range</H3>
|
<H3><A name="select_addresses"></A>Select Range</H3>
|
||||||
|
|
||||||
<P>This action is available when there's an active trace, and at least one watch with memory
|
<P>This action is available when there's an active trace, and at least one watch with memory
|
||||||
|
|
|
@ -2385,4 +2385,12 @@ public interface DebuggerResources {
|
||||||
String DESCRIPTION_CHOOSE_MORE_PLATFORMS =
|
String DESCRIPTION_CHOOSE_MORE_PLATFORMS =
|
||||||
"Choose from more platforms to use with the current trace";
|
"Choose from more platforms to use with the current trace";
|
||||||
|
|
||||||
|
String NAME_CLEAR_REGISTER_TYPE = "Clear Register Type";
|
||||||
|
String DESCRIPTION_CLEAR_REGISTER_TYPE = "Clear the register's data type";
|
||||||
|
|
||||||
|
String NAME_REGISTER_TYPE_SETTINGS = "Register Type Settings";
|
||||||
|
String DESCRIPTION_REGISTER_TYPE_SETTINGS = "Set the register's data type settings";
|
||||||
|
|
||||||
|
String NAME_WATCH_TYPE_SETTINGS = "Watch Type Settings";
|
||||||
|
String DESCRIPTION_WATCH_TYPE_SETTINGS = "Set the watch's data type settings";
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import docking.actions.PopupActionProvider;
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
import docking.widgets.table.ColumnSortState.SortDirection;
|
import docking.widgets.table.ColumnSortState.SortDirection;
|
||||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||||
|
import ghidra.app.plugin.core.data.DataSettingsDialog;
|
||||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerProvider;
|
import ghidra.app.plugin.core.debug.gui.DebuggerProvider;
|
||||||
|
@ -53,18 +54,19 @@ import ghidra.base.widgets.table.DataTypeTableCellEditor;
|
||||||
import ghidra.dbg.error.DebuggerModelAccessException;
|
import ghidra.dbg.error.DebuggerModelAccessException;
|
||||||
import ghidra.dbg.target.TargetRegisterBank;
|
import ghidra.dbg.target.TargetRegisterBank;
|
||||||
import ghidra.dbg.target.TargetThread;
|
import ghidra.dbg.target.TargetThread;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
import ghidra.framework.model.DomainObjectChangeRecord;
|
import ghidra.framework.model.DomainObjectChangeRecord;
|
||||||
import ghidra.framework.options.AutoOptions;
|
import ghidra.framework.options.AutoOptions;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.options.annotation.*;
|
import ghidra.framework.options.annotation.*;
|
||||||
import ghidra.framework.plugintool.AutoService;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
|
||||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.data.DataTypeEncodeException;
|
import ghidra.program.model.data.DataTypeEncodeException;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
import ghidra.trace.model.*;
|
import ghidra.trace.model.*;
|
||||||
import ghidra.trace.model.Trace.*;
|
import ghidra.trace.model.Trace.*;
|
||||||
|
@ -74,8 +76,7 @@ import ghidra.trace.model.memory.TraceMemoryState;
|
||||||
import ghidra.trace.model.program.TraceProgramView;
|
import ghidra.trace.model.program.TraceProgramView;
|
||||||
import ghidra.trace.model.thread.TraceThread;
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
import ghidra.trace.util.*;
|
import ghidra.trace.util.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.*;
|
||||||
import ghidra.util.Swing;
|
|
||||||
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
|
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
|
||||||
import ghidra.util.database.UndoableTransaction;
|
import ghidra.util.database.UndoableTransaction;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
@ -87,6 +88,50 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||||
implements DebuggerProvider, PopupActionProvider {
|
implements DebuggerProvider, PopupActionProvider {
|
||||||
private static final String KEY_DEBUGGER_COORDINATES = "DebuggerCoordinates";
|
private static final String KEY_DEBUGGER_COORDINATES = "DebuggerCoordinates";
|
||||||
|
|
||||||
|
interface ClearRegisterType {
|
||||||
|
String NAME = DebuggerResources.NAME_CLEAR_REGISTER_TYPE;
|
||||||
|
String DESCRIPTION = DebuggerResources.DESCRIPTION_CLEAR_REGISTER_TYPE;
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegisterTypeSettings {
|
||||||
|
String NAME = DebuggerResources.NAME_REGISTER_TYPE_SETTINGS;
|
||||||
|
String DESCRIPTION = DebuggerResources.DESCRIPTION_REGISTER_TYPE_SETTINGS;
|
||||||
|
String HELP_ANCHOR = "type_settings";
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION)
|
||||||
|
.popupMenuPath(NAME)
|
||||||
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This only exists so that tests can access it
|
||||||
|
*/
|
||||||
|
protected static class RegisterDataSettingsDialog extends DataSettingsDialog {
|
||||||
|
public RegisterDataSettingsDialog(Data data) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Settings getSettings() {
|
||||||
|
return super.getSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void okCallback() {
|
||||||
|
super.okCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected enum RegisterTableColumns
|
protected enum RegisterTableColumns
|
||||||
implements EnumeratedTableColumn<RegisterTableColumns, RegisterRow> {
|
implements EnumeratedTableColumn<RegisterTableColumns, RegisterRow> {
|
||||||
FAV("Fav", Boolean.class, RegisterRow::isFavorite, RegisterRow::setFavorite, //
|
FAV("Fav", Boolean.class, RegisterRow::isFavorite, RegisterRow::setFavorite, //
|
||||||
|
@ -429,7 +474,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||||
|
|
||||||
GhidraTable regsTable;
|
GhidraTable regsTable;
|
||||||
RegistersTableModel regsTableModel = new RegistersTableModel();
|
RegistersTableModel regsTableModel = new RegistersTableModel();
|
||||||
private GhidraTableFilterPanel<RegisterRow> regsFilterPanel;
|
GhidraTableFilterPanel<RegisterRow> regsFilterPanel;
|
||||||
Map<Register, RegisterRow> regMap = new HashMap<>();
|
Map<Register, RegisterRow> regMap = new HashMap<>();
|
||||||
|
|
||||||
private final DebuggerAvailableRegistersDialog availableRegsDialog;
|
private final DebuggerAvailableRegistersDialog availableRegsDialog;
|
||||||
|
@ -438,6 +483,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||||
DockingAction actionCreateSnapshot;
|
DockingAction actionCreateSnapshot;
|
||||||
ToggleDockingAction actionEnableEdits;
|
ToggleDockingAction actionEnableEdits;
|
||||||
DockingAction actionClearDataType;
|
DockingAction actionClearDataType;
|
||||||
|
DockingAction actionDataTypeSettings;
|
||||||
|
|
||||||
DebuggerRegisterActionContext myActionContext;
|
DebuggerRegisterActionContext myActionContext;
|
||||||
AddressSetView viewKnown;
|
AddressSetView viewKnown;
|
||||||
|
@ -618,11 +664,16 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||||
.onAction(c -> {
|
.onAction(c -> {
|
||||||
})
|
})
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
actionClearDataType = new ActionBuilder("Clear Register Type", plugin.getName())
|
actionClearDataType = ClearRegisterType.builder(plugin)
|
||||||
.enabledWhen(c -> current.getThread() != null)
|
.enabledWhen(c -> current.getThread() != null)
|
||||||
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0))
|
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0))
|
||||||
.onAction(c -> clearDataTypeActivated())
|
.onAction(c -> clearDataTypeActivated())
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
|
actionDataTypeSettings = RegisterTypeSettings.builder(plugin)
|
||||||
|
.withContext(DebuggerRegisterActionContext.class)
|
||||||
|
.enabledWhen(this::contextHasSingleRegisterWithType)
|
||||||
|
.onAction(this::dataTypeSettingsActivated)
|
||||||
|
.buildAndInstallLocal(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectRegistersActivated() {
|
private void selectRegistersActivated() {
|
||||||
|
@ -653,6 +704,22 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||||
row.setDataType(null);
|
row.setDataType(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean contextHasSingleRegisterWithType(DebuggerRegisterActionContext ctx) {
|
||||||
|
return ctx.getSelected() != null && ctx.getSelected().getData() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dataTypeSettingsActivated(DebuggerRegisterActionContext ctx) {
|
||||||
|
RegisterRow row = ctx.getSelected();
|
||||||
|
if (row == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Data data = row.getData();
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tool.showDialog(new RegisterDataSettingsDialog(data));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: "Refresh" action to flush cache and re-fetch selected registers
|
// TODO: "Refresh" action to flush cache and re-fetch selected registers
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Objects;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.Language;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public class RegisterRow {
|
public class RegisterRow {
|
||||||
|
@ -84,6 +85,10 @@ public class RegisterRow {
|
||||||
return provider.getRegisterValue(register);
|
return provider.getRegisterValue(register);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Data getData() {
|
||||||
|
return provider.getRegisterData(register);
|
||||||
|
}
|
||||||
|
|
||||||
public void setDataType(DataType dataType) {
|
public void setDataType(DataType dataType) {
|
||||||
provider.writeRegisterDataType(register, dataType);
|
provider.writeRegisterDataType(register, dataType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,24 +24,23 @@ import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
|
|
||||||
@PluginInfo( //
|
@PluginInfo(
|
||||||
shortDescription = "Debugger watches manager", //
|
shortDescription = "Debugger watches manager",
|
||||||
description = "GUI to watch values of expressions", //
|
description = "GUI to watch values of expressions",
|
||||||
category = PluginCategoryNames.DEBUGGER, //
|
category = PluginCategoryNames.DEBUGGER,
|
||||||
packageName = DebuggerPluginPackage.NAME, //
|
packageName = DebuggerPluginPackage.NAME,
|
||||||
status = PluginStatus.RELEASED, //
|
status = PluginStatus.RELEASED,
|
||||||
eventsConsumed = {
|
eventsConsumed = {
|
||||||
TraceActivatedPluginEvent.class, //
|
TraceActivatedPluginEvent.class,
|
||||||
}, //
|
},
|
||||||
servicesRequired = { //
|
servicesRequired = {
|
||||||
DebuggerModelService.class, //
|
DebuggerModelService.class,
|
||||||
DebuggerTraceManagerService.class, //
|
DebuggerTraceManagerService.class,
|
||||||
DataTypeManagerService.class, // For DataType selection field
|
DataTypeManagerService.class, // For DataType selection field
|
||||||
} //
|
})
|
||||||
)
|
|
||||||
public class DebuggerWatchesPlugin extends AbstractDebuggerPlugin {
|
public class DebuggerWatchesPlugin extends AbstractDebuggerPlugin {
|
||||||
|
|
||||||
private DebuggerWatchesProvider provider;
|
DebuggerWatchesProvider provider;
|
||||||
|
|
||||||
public DebuggerWatchesPlugin(PluginTool tool) {
|
public DebuggerWatchesPlugin(PluginTool tool) {
|
||||||
super(tool);
|
super(tool);
|
||||||
|
|
|
@ -29,14 +29,18 @@ import javax.swing.*;
|
||||||
import javax.swing.table.TableColumn;
|
import javax.swing.table.TableColumn;
|
||||||
import javax.swing.table.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
|
|
||||||
|
import org.jdom.Element;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.WindowPosition;
|
import docking.WindowPosition;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
import docking.action.ToggleDockingAction;
|
import docking.action.ToggleDockingAction;
|
||||||
|
import docking.action.builder.ActionBuilder;
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||||
import ghidra.app.context.ListingActionContext;
|
import ghidra.app.context.ListingActionContext;
|
||||||
import ghidra.app.context.ProgramLocationActionContext;
|
import ghidra.app.context.ProgramLocationActionContext;
|
||||||
|
import ghidra.app.plugin.core.data.AbstractSettingsDialog;
|
||||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||||
|
@ -47,14 +51,13 @@ import ghidra.app.services.*;
|
||||||
import ghidra.async.AsyncDebouncer;
|
import ghidra.async.AsyncDebouncer;
|
||||||
import ghidra.async.AsyncTimer;
|
import ghidra.async.AsyncTimer;
|
||||||
import ghidra.base.widgets.table.DataTypeTableCellEditor;
|
import ghidra.base.widgets.table.DataTypeTableCellEditor;
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.*;
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
import ghidra.framework.model.DomainObjectChangeRecord;
|
import ghidra.framework.model.DomainObjectChangeRecord;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.options.annotation.AutoOptionDefined;
|
import ghidra.framework.options.annotation.AutoOptionDefined;
|
||||||
import ghidra.framework.options.annotation.HelpInfo;
|
import ghidra.framework.options.annotation.HelpInfo;
|
||||||
import ghidra.framework.plugintool.AutoService;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
|
||||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||||
import ghidra.pcode.exec.trace.TraceSleighUtils;
|
import ghidra.pcode.exec.trace.TraceSleighUtils;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
@ -71,16 +74,64 @@ import ghidra.trace.model.Trace.TraceMemoryStateChangeType;
|
||||||
import ghidra.trace.model.program.TraceProgramView;
|
import ghidra.trace.model.program.TraceProgramView;
|
||||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||||
import ghidra.trace.util.TraceAddressSpace;
|
import ghidra.trace.util.TraceAddressSpace;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.*;
|
||||||
import ghidra.util.Swing;
|
|
||||||
import ghidra.util.database.UndoableTransaction;
|
import ghidra.util.database.UndoableTransaction;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.table.GhidraTable;
|
import ghidra.util.table.GhidraTable;
|
||||||
import ghidra.util.table.GhidraTableFilterPanel;
|
import ghidra.util.table.GhidraTableFilterPanel;
|
||||||
import ghidra.util.table.column.AbstractGColumnRenderer;
|
import ghidra.util.table.column.AbstractGColumnRenderer;
|
||||||
|
|
||||||
public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
private static final String KEY_EXPRESSION_LIST = "expressionList";
|
private static final String KEY_ROW_COUNT = "rowCount";
|
||||||
private static final String KEY_TYPE_LIST = "typeList";
|
private static final String PREFIX_ROW = "row";
|
||||||
|
|
||||||
|
interface WatchTypeSettings {
|
||||||
|
String NAME = DebuggerResources.NAME_WATCH_TYPE_SETTINGS;
|
||||||
|
String DESCRIPTION = DebuggerResources.DESCRIPTION_WATCH_TYPE_SETTINGS;
|
||||||
|
String HELP_ANCHOR = "type_settings";
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION)
|
||||||
|
.popupMenuPath(NAME)
|
||||||
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class WatchDataSettingsDialog extends AbstractSettingsDialog {
|
||||||
|
private final WatchRow row;
|
||||||
|
|
||||||
|
public WatchDataSettingsDialog(WatchRow row) {
|
||||||
|
super("Data Type Settings", row.getDataType().getSettingsDefinitions(),
|
||||||
|
row.getSettings());
|
||||||
|
this.row = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Settings getSettings() {
|
||||||
|
return super.getSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void okCallback() {
|
||||||
|
super.okCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
|
||||||
|
if (!settingsDefinition.supportsSuggestedValues()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return settingsDefinition.getSuggestedValues(row.getSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applySettings() throws CancelledException {
|
||||||
|
copySettings(getSettings(), row.getSettings(), getSettingsDefinitions());
|
||||||
|
row.settingsChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected enum WatchTableColumns implements EnumeratedTableColumn<WatchTableColumns, WatchRow> {
|
protected enum WatchTableColumns implements EnumeratedTableColumn<WatchTableColumns, WatchRow> {
|
||||||
EXPRESSION("Expression", String.class, WatchRow::getExpression, WatchRow::setExpression),
|
EXPRESSION("Expression", String.class, WatchRow::getExpression, WatchRow::setExpression),
|
||||||
|
@ -151,6 +202,12 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static void copySettings(Settings src, Settings dst, SettingsDefinition[] defs) {
|
||||||
|
for (SettingsDefinition sd : defs) {
|
||||||
|
sd.copySetting(src, dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
|
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
|
||||||
if (!Objects.equals(a.getTrace(), b.getTrace())) {
|
if (!Objects.equals(a.getTrace(), b.getTrace())) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -299,6 +356,7 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
DockingAction actionSelectAllReads;
|
DockingAction actionSelectAllReads;
|
||||||
DockingAction actionAdd;
|
DockingAction actionAdd;
|
||||||
DockingAction actionRemove;
|
DockingAction actionRemove;
|
||||||
|
DockingAction actionDataTypeSettings;
|
||||||
|
|
||||||
DockingAction actionAddFromLocation;
|
DockingAction actionAddFromLocation;
|
||||||
DockingAction actionAddFromRegister;
|
DockingAction actionAddFromRegister;
|
||||||
|
@ -442,6 +500,12 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
.onAction(this::activatedRemove)
|
.onAction(this::activatedRemove)
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
|
|
||||||
|
actionDataTypeSettings = WatchTypeSettings.builder(plugin)
|
||||||
|
.withContext(DebuggerWatchActionContext.class)
|
||||||
|
.enabledWhen(this::selIsOneWithDataType)
|
||||||
|
.onAction(this::activatedDataTypeSettings)
|
||||||
|
.buildAndInstallLocal(this);
|
||||||
|
|
||||||
// Pop-up context actions
|
// Pop-up context actions
|
||||||
actionAddFromLocation = WatchAction.builder(plugin)
|
actionAddFromLocation = WatchAction.builder(plugin)
|
||||||
.withContext(ProgramLocationActionContext.class)
|
.withContext(ProgramLocationActionContext.class)
|
||||||
|
@ -491,6 +555,11 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean selIsOneWithDataType(DebuggerWatchActionContext ctx) {
|
||||||
|
WatchRow row = ctx.getWatchRow();
|
||||||
|
return row != null && row.getDataType() != null;
|
||||||
|
}
|
||||||
|
|
||||||
private void activatedApplyDataType(DebuggerWatchActionContext context) {
|
private void activatedApplyDataType(DebuggerWatchActionContext context) {
|
||||||
if (current.getTrace() == null) {
|
if (current.getTrace() == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -526,7 +595,8 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
UndoableTransaction.start(current.getTrace(), "Apply Watch Data Type")) {
|
UndoableTransaction.start(current.getTrace(), "Apply Watch Data Type")) {
|
||||||
try {
|
try {
|
||||||
listing.clearCodeUnits(row.getAddress(), row.getRange().getMaxAddress(), false);
|
listing.clearCodeUnits(row.getAddress(), row.getRange().getMaxAddress(), false);
|
||||||
listing.createData(address, dataType, size);
|
Data data = listing.createData(address, dataType, size);
|
||||||
|
copySettings(row.getSettings(), data, dataType.getSettingsDefinitions());
|
||||||
}
|
}
|
||||||
catch (CodeUnitInsertionException e) {
|
catch (CodeUnitInsertionException e) {
|
||||||
errs.add(address + " " + dataType + "(" + size + "): " + e.getMessage());
|
errs.add(address + " " + dataType + "(" + size + "): " + e.getMessage());
|
||||||
|
@ -579,6 +649,18 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
watchTableModel.deleteWith(context.getWatchRows()::contains);
|
watchTableModel.deleteWith(context.getWatchRows()::contains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void activatedDataTypeSettings(DebuggerWatchActionContext context) {
|
||||||
|
WatchRow row = context.getWatchRow();
|
||||||
|
if (row == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DataType type = row.getDataType();
|
||||||
|
if (type == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tool.showDialog(new WatchDataSettingsDialog(row));
|
||||||
|
}
|
||||||
|
|
||||||
private ProgramLocation getDynamicLocation(ProgramLocation someLoc) {
|
private ProgramLocation getDynamicLocation(ProgramLocation someLoc) {
|
||||||
if (someLoc == null) {
|
if (someLoc == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -797,26 +879,30 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
|
|
||||||
public void writeConfigState(SaveState saveState) {
|
public void writeConfigState(SaveState saveState) {
|
||||||
List<WatchRow> rows = List.copyOf(watchTableModel.getModelData());
|
List<WatchRow> rows = List.copyOf(watchTableModel.getModelData());
|
||||||
String[] expressions = rows.stream().map(WatchRow::getExpression).toArray(String[]::new);
|
saveState.putInt(KEY_ROW_COUNT, rows.size());
|
||||||
String[] types = rows.stream().map(WatchRow::getTypePath).toArray(String[]::new);
|
for (int i = 0; i < rows.size(); i++) {
|
||||||
saveState.putStrings(KEY_EXPRESSION_LIST, expressions);
|
WatchRow row = rows.get(i);
|
||||||
saveState.putStrings(KEY_TYPE_LIST, types);
|
String stateName = PREFIX_ROW + i;
|
||||||
|
SaveState rowState = new SaveState();
|
||||||
|
row.writeConfigState(rowState);
|
||||||
|
saveState.putXmlElement(stateName, rowState.saveToXml());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readConfigState(SaveState saveState) {
|
public void readConfigState(SaveState saveState) {
|
||||||
String[] expressions = saveState.getStrings(KEY_EXPRESSION_LIST, new String[] {});
|
int rowCount = saveState.getInt(KEY_ROW_COUNT, 0);
|
||||||
String[] types = saveState.getStrings(KEY_TYPE_LIST, new String[] {});
|
|
||||||
if (expressions.length != types.length) {
|
|
||||||
Msg.error(this, "Watch provider config error. Unequal number of expressions and types");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int len = expressions.length;
|
|
||||||
List<WatchRow> rows = new ArrayList<>();
|
List<WatchRow> rows = new ArrayList<>();
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < rowCount; i++) {
|
||||||
WatchRow r = new WatchRow(this, expressions[i]);
|
String stateName = PREFIX_ROW + i;
|
||||||
r.setTypePath(types[i]);
|
Element rowElement = saveState.getXmlElement(stateName);
|
||||||
rows.add(r);
|
if (rowElement != null) {
|
||||||
|
WatchRow r = new WatchRow(this, "");
|
||||||
|
SaveState rowState = new SaveState(rowElement);
|
||||||
|
r.readConfigState(rowState);
|
||||||
|
rows.add(r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
watchTableModel.clear();
|
||||||
watchTableModel.addAll(rows);
|
watchTableModel.addAll(rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.debug.gui.watch;
|
||||||
|
|
||||||
|
import ghidra.docking.settings.*;
|
||||||
|
import ghidra.framework.options.SaveState;
|
||||||
|
import ghidra.program.model.data.TypeDefSettingsDefinition;
|
||||||
|
|
||||||
|
public class SavedSettings {
|
||||||
|
private final Settings settings;
|
||||||
|
private SaveState state;
|
||||||
|
|
||||||
|
public SavedSettings(Settings settings) {
|
||||||
|
this.settings = settings;
|
||||||
|
setState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(SaveState state) {
|
||||||
|
if (state == null) {
|
||||||
|
state = new SaveState("Settings");
|
||||||
|
}
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaveState getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(SettingsDefinition[] definitions, Settings defaultSettings) {
|
||||||
|
for (SettingsDefinition sd : definitions) {
|
||||||
|
if (sd.hasSameValue(settings, defaultSettings)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sd instanceof BooleanSettingsDefinition bsd) {
|
||||||
|
state.putBoolean(sd.getStorageKey(), bsd.getValue(settings));
|
||||||
|
}
|
||||||
|
else if (sd instanceof EnumSettingsDefinition esd) {
|
||||||
|
state.putInt(sd.getStorageKey(), esd.getChoice(settings));
|
||||||
|
}
|
||||||
|
else if (sd instanceof NumberSettingsDefinition nsd) {
|
||||||
|
state.putLong(sd.getStorageKey(), nsd.getValue(settings));
|
||||||
|
}
|
||||||
|
else if (sd instanceof StringSettingsDefinition ssd) {
|
||||||
|
state.putString(sd.getStorageKey(), ssd.getValue(settings));
|
||||||
|
}
|
||||||
|
else if (sd instanceof TypeDefSettingsDefinition tdsd) {
|
||||||
|
// Toss this on the floor
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void read(SettingsDefinition[] definitions, Settings defaultSettings) {
|
||||||
|
for (SettingsDefinition sd : definitions) {
|
||||||
|
if (!state.hasValue(sd.getStorageKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sd instanceof BooleanSettingsDefinition bsd) {
|
||||||
|
bsd.setValue(settings, state.getBoolean(sd.getStorageKey(), false));
|
||||||
|
}
|
||||||
|
else if (sd instanceof EnumSettingsDefinition esd) {
|
||||||
|
esd.setChoice(settings, state.getInt(sd.getStorageKey(), 0));
|
||||||
|
}
|
||||||
|
else if (sd instanceof NumberSettingsDefinition nsd) {
|
||||||
|
nsd.setValue(settings, state.getLong(sd.getStorageKey(), 0));
|
||||||
|
}
|
||||||
|
else if (sd instanceof StringSettingsDefinition ssd) {
|
||||||
|
ssd.setValue(settings, state.getString(sd.getStorageKey(), null));
|
||||||
|
}
|
||||||
|
else if (sd instanceof TypeDefSettingsDefinition tdsd) {
|
||||||
|
// Toss this on the floor
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,9 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
import ghidra.app.services.DebuggerStateEditingService;
|
import ghidra.app.services.DebuggerStateEditingService;
|
||||||
import ghidra.app.services.DebuggerStateEditingService.StateEditor;
|
import ghidra.app.services.DebuggerStateEditingService.StateEditor;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.docking.settings.SettingsImpl;
|
import ghidra.docking.settings.SettingsImpl;
|
||||||
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.pcode.exec.*;
|
import ghidra.pcode.exec.*;
|
||||||
import ghidra.pcode.exec.trace.TraceBytesPcodeExecutorState;
|
import ghidra.pcode.exec.trace.TraceBytesPcodeExecutorState;
|
||||||
import ghidra.pcode.exec.trace.TraceSleighUtils;
|
import ghidra.pcode.exec.trace.TraceSleighUtils;
|
||||||
|
@ -51,6 +53,9 @@ import ghidra.util.*;
|
||||||
|
|
||||||
public class WatchRow {
|
public class WatchRow {
|
||||||
public static final int TRUNCATE_BYTES_LENGTH = 64;
|
public static final int TRUNCATE_BYTES_LENGTH = 64;
|
||||||
|
private static final String KEY_EXPRESSION = "expression";
|
||||||
|
private static final String KEY_DATA_TYPE = "dataType";
|
||||||
|
private static final String KEY_SETTINGS = "settings";
|
||||||
|
|
||||||
private final DebuggerWatchesProvider provider;
|
private final DebuggerWatchesProvider provider;
|
||||||
private Trace trace;
|
private Trace trace;
|
||||||
|
@ -63,6 +68,8 @@ public class WatchRow {
|
||||||
private String expression;
|
private String expression;
|
||||||
private String typePath;
|
private String typePath;
|
||||||
private DataType dataType;
|
private DataType dataType;
|
||||||
|
private SettingsImpl settings = new SettingsImpl();
|
||||||
|
private SavedSettings savedSettings = new SavedSettings(settings);
|
||||||
|
|
||||||
private PcodeExpression compiled;
|
private PcodeExpression compiled;
|
||||||
private TraceMemoryState state;
|
private TraceMemoryState state;
|
||||||
|
@ -150,7 +157,7 @@ public class WatchRow {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
MemBuffer buffer = new ByteMemBufferImpl(address, value, language.isBigEndian());
|
MemBuffer buffer = new ByteMemBufferImpl(address, value, language.isBigEndian());
|
||||||
return dataType.getRepresentation(buffer, SettingsImpl.NO_SETTINGS, value.length);
|
return dataType.getRepresentation(buffer, settings, value.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: DataType settings
|
// TODO: DataType settings
|
||||||
|
@ -296,18 +303,23 @@ public class WatchRow {
|
||||||
|
|
||||||
protected void updateType() {
|
protected void updateType() {
|
||||||
dataType = null;
|
dataType = null;
|
||||||
if (trace == null || typePath == null) {
|
if (typePath == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dataType = trace.getDataTypeManager().getDataType(typePath);
|
// Try from the trace first
|
||||||
if (dataType != null) {
|
if (trace != null) {
|
||||||
return;
|
dataType = trace.getDataTypeManager().getDataType(typePath);
|
||||||
|
if (dataType != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Either we have no trace, or the trace doesn't have the type.
|
||||||
|
// Try built-ins
|
||||||
DataTypeManagerService dtms = provider.getTool().getService(DataTypeManagerService.class);
|
DataTypeManagerService dtms = provider.getTool().getService(DataTypeManagerService.class);
|
||||||
if (dtms == null) {
|
if (dtms != null) {
|
||||||
return;
|
dataType = dtms.getBuiltInDataTypesManager().getDataType(typePath);
|
||||||
}
|
}
|
||||||
dataType = dtms.getBuiltInDataTypesManager().getDataType(typePath);
|
// We're out of things to try, let null be null
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTypePath(String typePath) {
|
public void setTypePath(String typePath) {
|
||||||
|
@ -325,12 +337,37 @@ public class WatchRow {
|
||||||
valueString = parseAsDataTypeStr();
|
valueString = parseAsDataTypeStr();
|
||||||
valueObj = parseAsDataTypeObj();
|
valueObj = parseAsDataTypeObj();
|
||||||
provider.contextChanged();
|
provider.contextChanged();
|
||||||
|
settings.setDefaultSettings(dataType == null ? null : dataType.getDefaultSettings());
|
||||||
|
if (dataType != null) {
|
||||||
|
savedSettings.read(dataType.getSettingsDefinitions(), dataType.getDefaultSettings());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataType getDataType() {
|
public DataType getDataType() {
|
||||||
return dataType;
|
return dataType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the row's (mutable) data type settings
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* After mutating these settings, the client must call {@link #settingsChanged()} to update the
|
||||||
|
* row's display and save state.
|
||||||
|
*
|
||||||
|
* @return the settings
|
||||||
|
*/
|
||||||
|
public Settings getSettings() {
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void settingsChanged() {
|
||||||
|
if (dataType != null) {
|
||||||
|
savedSettings.write(dataType.getSettingsDefinitions(), dataType.getDefaultSettings());
|
||||||
|
}
|
||||||
|
valueString = parseAsDataTypeStr();
|
||||||
|
provider.watchTableModel.fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
public Address getAddress() {
|
public Address getAddress() {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
@ -552,4 +589,20 @@ public class WatchRow {
|
||||||
}
|
}
|
||||||
return !Arrays.equals(value, prevValue);
|
return !Arrays.equals(value, prevValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void writeConfigState(SaveState saveState) {
|
||||||
|
saveState.putString(KEY_EXPRESSION, expression);
|
||||||
|
saveState.putString(KEY_DATA_TYPE, typePath);
|
||||||
|
saveState.putSaveState(KEY_SETTINGS, savedSettings.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void readConfigState(SaveState saveState) {
|
||||||
|
setExpression(saveState.getString(KEY_EXPRESSION, ""));
|
||||||
|
setTypePath(saveState.getString(KEY_DATA_TYPE, null));
|
||||||
|
|
||||||
|
savedSettings.setState(saveState.getSaveState(KEY_SETTINGS));
|
||||||
|
if (dataType != null) {
|
||||||
|
savedSettings.read(dataType.getSettingsDefinitions(), dataType.getDefaultSettings());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,11 +33,14 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||||
import ghidra.app.plugin.core.debug.gui.action.LocationTrackingSpec;
|
import ghidra.app.plugin.core.debug.gui.action.LocationTrackingSpec;
|
||||||
import ghidra.app.plugin.core.debug.gui.action.NoneLocationTrackingSpec;
|
import ghidra.app.plugin.core.debug.gui.action.NoneLocationTrackingSpec;
|
||||||
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
|
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
|
||||||
|
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider.RegisterDataSettingsDialog;
|
||||||
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider.RegisterTableColumns;
|
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider.RegisterTableColumns;
|
||||||
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
|
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
|
||||||
import ghidra.app.services.DebuggerStateEditingService;
|
import ghidra.app.services.DebuggerStateEditingService;
|
||||||
import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
|
import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
|
||||||
import ghidra.app.services.TraceRecorder;
|
import ghidra.app.services.TraceRecorder;
|
||||||
|
import ghidra.docking.settings.FormatSettingsDefinition;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
|
@ -460,6 +463,43 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
||||||
assertTypeEquals(PointerDataType.dataType, data.getDataType());
|
assertTypeEquals(PointerDataType.dataType, data.getDataType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyTypeSettingsAffectsTrace() throws Exception {
|
||||||
|
traceManager.openTrace(tb.trace);
|
||||||
|
|
||||||
|
TraceThread thread = addThread();
|
||||||
|
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||||
|
tb.exec(0, 0, thread, List.of("pc = 100;"));
|
||||||
|
}
|
||||||
|
traceManager.activateThread(thread);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
RegisterRow row = findRegisterRow(pc);
|
||||||
|
row.setDataType(LongLongDataType.dataType);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
DBTraceCodeRegisterSpace regCode =
|
||||||
|
tb.trace.getCodeManager().getCodeRegisterSpace(thread, false);
|
||||||
|
assertNotNull(regCode);
|
||||||
|
TraceData data = regCode.data().getForRegister(0L, pc);
|
||||||
|
assertTypeEquals(LongLongDataType.dataType, data.getDataType());
|
||||||
|
assertEquals("64h", row.getRepresentation());
|
||||||
|
|
||||||
|
registersProvider.regsFilterPanel.setSelectedItem(row);
|
||||||
|
waitForSwing();
|
||||||
|
performEnabledAction(registersProvider, registersProvider.actionDataTypeSettings, false);
|
||||||
|
RegisterDataSettingsDialog dialog =
|
||||||
|
waitForDialogComponent(RegisterDataSettingsDialog.class);
|
||||||
|
Settings settings = dialog.getSettings();
|
||||||
|
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
|
||||||
|
format.setChoice(settings, FormatSettingsDefinition.DECIMAL);
|
||||||
|
runSwing(() -> dialog.okCallback());
|
||||||
|
|
||||||
|
// The data is the settings. Wonderful :/
|
||||||
|
assertEquals(FormatSettingsDefinition.DECIMAL, format.getChoice(data));
|
||||||
|
assertEquals("100", row.getRepresentation());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testModifySubRegTypeAffectsTrace() throws Exception {
|
public void testModifySubRegTypeAffectsTrace() throws Exception {
|
||||||
traceManager.openTrace(tb.trace);
|
traceManager.openTrace(tb.trace);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import static org.junit.Assert.*;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
@ -33,15 +34,20 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||||
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
|
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
|
||||||
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider;
|
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider;
|
||||||
import ghidra.app.plugin.core.debug.gui.register.*;
|
import ghidra.app.plugin.core.debug.gui.register.*;
|
||||||
|
import ghidra.app.plugin.core.debug.gui.watch.DebuggerWatchesProvider.WatchDataSettingsDialog;
|
||||||
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
|
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
|
||||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
|
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
|
import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
|
||||||
import ghidra.dbg.model.TestTargetRegisterBankInThread;
|
import ghidra.dbg.model.TestTargetRegisterBankInThread;
|
||||||
|
import ghidra.docking.settings.FormatSettingsDefinition;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.lang.RegisterValue;
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.model.mem.Memory;
|
import ghidra.program.model.mem.Memory;
|
||||||
import ghidra.program.model.symbol.SourceType;
|
import ghidra.program.model.symbol.SourceType;
|
||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
@ -173,6 +179,76 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||||
assertEquals(TraceRegisterUtils.rangeForRegister(r0), row.getRange());
|
assertEquals(TraceRegisterUtils.rangeForRegister(r0), row.getRange());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActionApplyDataType() {
|
||||||
|
setRegisterValues(thread);
|
||||||
|
WatchRow row = watchesProvider.addWatch("*:4 r0");
|
||||||
|
row.setDataType(LongDataType.dataType);
|
||||||
|
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
|
||||||
|
format.setChoice(row.getSettings(), FormatSettingsDefinition.DECIMAL);
|
||||||
|
|
||||||
|
traceManager.openTrace(tb.trace);
|
||||||
|
traceManager.activateThread(thread);
|
||||||
|
watchesProvider.watchFilterPanel.setSelectedItem(row);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
performEnabledAction(watchesProvider, watchesProvider.actionApplyDataType, true);
|
||||||
|
|
||||||
|
Data u400000 = tb.trace.getCodeManager().data().getAt(0, tb.addr(0x00400000));
|
||||||
|
assertTrue(LongDataType.dataType.isEquivalent(u400000.getDataType()));
|
||||||
|
assertEquals(FormatSettingsDefinition.DECIMAL, format.getChoice(u400000));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWatchWithDataTypeSettings() {
|
||||||
|
setRegisterValues(thread);
|
||||||
|
|
||||||
|
performAction(watchesProvider.actionAdd);
|
||||||
|
WatchRow row = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
|
||||||
|
row.setExpression("r0");
|
||||||
|
row.setDataType(LongLongDataType.dataType);
|
||||||
|
|
||||||
|
traceManager.openTrace(tb.trace);
|
||||||
|
traceManager.activateThread(thread);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
assertEquals("0x400000", row.getRawValueString());
|
||||||
|
assertEquals("400000h", row.getValueString());
|
||||||
|
assertNoErr(row);
|
||||||
|
|
||||||
|
Settings settings = row.getSettings();
|
||||||
|
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
|
||||||
|
runSwing(() -> format.setChoice(settings, FormatSettingsDefinition.DECIMAL));
|
||||||
|
assertEquals("4194304", row.getValueString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActionDataTypeSettings() {
|
||||||
|
setRegisterValues(thread);
|
||||||
|
|
||||||
|
performAction(watchesProvider.actionAdd);
|
||||||
|
WatchRow row = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
|
||||||
|
row.setExpression("r0");
|
||||||
|
row.setDataType(LongLongDataType.dataType);
|
||||||
|
|
||||||
|
traceManager.openTrace(tb.trace);
|
||||||
|
traceManager.activateThread(thread);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
watchesProvider.watchFilterPanel.setSelectedItem(row);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
performEnabledAction(watchesProvider, watchesProvider.actionDataTypeSettings, false);
|
||||||
|
WatchDataSettingsDialog dialog = waitForDialogComponent(WatchDataSettingsDialog.class);
|
||||||
|
|
||||||
|
Settings settings = dialog.getSettings();
|
||||||
|
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
|
||||||
|
format.setChoice(settings, FormatSettingsDefinition.DECIMAL);
|
||||||
|
runSwing(() -> dialog.okCallback());
|
||||||
|
|
||||||
|
assertEquals("4194304", row.getValueString());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstantWatch() {
|
public void testConstantWatch() {
|
||||||
setRegisterValues(thread);
|
setRegisterValues(thread);
|
||||||
|
@ -677,4 +753,42 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||||
|
|
||||||
assertEquals(symbol, row.getSymbol());
|
assertEquals(symbol, row.getSymbol());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSaveConfigState() throws Throwable {
|
||||||
|
// Setup some state
|
||||||
|
WatchRow row0 = watchesProvider.addWatch("r0");
|
||||||
|
WatchRow row1 = watchesProvider.addWatch("*:4 r1");
|
||||||
|
|
||||||
|
row0.setDataType(LongLongDataType.dataType);
|
||||||
|
Settings settings = row0.getSettings();
|
||||||
|
FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
|
||||||
|
format.setChoice(settings, FormatSettingsDefinition.DECIMAL);
|
||||||
|
row0.settingsChanged();
|
||||||
|
|
||||||
|
// Save the state
|
||||||
|
SaveState saveState = new SaveState();
|
||||||
|
watchesPlugin.writeConfigState(saveState);
|
||||||
|
|
||||||
|
// Change some things
|
||||||
|
row1.setDataType(Pointer64DataType.dataType);
|
||||||
|
WatchRow row2 = watchesProvider.addWatch("r2");
|
||||||
|
waitForSwing();
|
||||||
|
assertEquals(Set.of(row0, row1, row2),
|
||||||
|
Set.copyOf(watchesProvider.watchTableModel.getModelData()));
|
||||||
|
|
||||||
|
// Restore saved state
|
||||||
|
watchesPlugin.readConfigState(saveState);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
// Assert the older state
|
||||||
|
Map<String, WatchRow> rows = watchesProvider.watchTableModel.getModelData()
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toMap(r -> r.getExpression(), r -> r));
|
||||||
|
assertEquals(2, rows.size());
|
||||||
|
|
||||||
|
WatchRow rRow0 = rows.get("r0");
|
||||||
|
assertTrue(LongLongDataType.dataType.isEquivalent(rRow0.getDataType()));
|
||||||
|
assertEquals(FormatSettingsDefinition.DECIMAL, format.getChoice(rRow0.getSettings()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ import ghidra.trace.database.map.*;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||||
import ghidra.trace.model.thread.TraceThread;
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
import ghidra.trace.util.TraceAddressSpace;
|
|
||||||
import ghidra.util.database.*;
|
import ghidra.util.database.*;
|
||||||
import ghidra.util.database.annot.*;
|
import ghidra.util.database.annot.*;
|
||||||
import ghidra.util.exception.VersionException;
|
import ghidra.util.exception.VersionException;
|
||||||
|
@ -224,11 +223,6 @@ public class DBTraceDataSettingsAdapter
|
||||||
dataFactory);
|
dataFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DBTraceDataSettingsSpace get(TraceAddressSpace space, boolean createIfAbsent) {
|
|
||||||
return (DBTraceDataSettingsSpace) super.get(space, createIfAbsent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DBTraceDataSettingsSpace getForSpace(AddressSpace space, boolean createIfAbsent) {
|
public DBTraceDataSettingsSpace getForSpace(AddressSpace space, boolean createIfAbsent) {
|
||||||
return (DBTraceDataSettingsSpace) super.getForSpace(space, createIfAbsent);
|
return (DBTraceDataSettingsSpace) super.getForSpace(space, createIfAbsent);
|
||||||
|
|
|
@ -231,7 +231,7 @@ public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedData
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DBTraceDataSettingsSpace getSettingsSpace(boolean createIfAbsent) {
|
public DBTraceDataSettingsSpace getSettingsSpace(boolean createIfAbsent) {
|
||||||
return root.getSettingsSpace(createIfAbsent);
|
return (DBTraceDataSettingsSpace) root.getSettingsSpace(createIfAbsent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -25,7 +25,7 @@ import ghidra.program.model.address.AddressSpace;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.Language;
|
||||||
import ghidra.trace.database.DBTraceUtils;
|
import ghidra.trace.database.DBTraceUtils;
|
||||||
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
|
import ghidra.trace.database.data.DBTraceDataSettingsOperations;
|
||||||
import ghidra.trace.database.guest.InternalTracePlatform;
|
import ghidra.trace.database.guest.InternalTracePlatform;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||||
import ghidra.trace.model.guest.TracePlatform;
|
import ghidra.trace.model.guest.TracePlatform;
|
||||||
|
@ -243,8 +243,9 @@ public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DBTraceDataSettingsSpace getSettingsSpace(boolean createIfAbsent) {
|
public DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent) {
|
||||||
return getTrace().getDataSettingsAdapter().get(space, createIfAbsent);
|
return (DBTraceDataSettingsOperations) getTrace().getDataSettingsAdapter()
|
||||||
|
.get(space, createIfAbsent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -276,7 +276,8 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent) {
|
public DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent) {
|
||||||
return getTrace().getDataSettingsAdapter().get(this, createIfAbsent);
|
return (DBTraceDataSettingsOperations) getTrace().getDataSettingsAdapter()
|
||||||
|
.get(this, createIfAbsent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -258,7 +258,7 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
|
||||||
* @param settingsDefinition string settings definition
|
* @param settingsDefinition string settings definition
|
||||||
* @return suggested string value (may be empty array or null)
|
* @return suggested string value (may be empty array or null)
|
||||||
*/
|
*/
|
||||||
abstract String[] getSuggestedValues(StringSettingsDefinition settingsDefinition);
|
protected abstract String[] getSuggestedValues(StringSettingsDefinition settingsDefinition);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply changes to settings. This method must be ov
|
* Apply changes to settings. This method must be ov
|
||||||
|
|
|
@ -336,7 +336,7 @@ public class DataSettingsDialog extends AbstractSettingsDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
|
protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
|
||||||
if (!settingsDefinition.supportsSuggestedValues()) {
|
if (!settingsDefinition.supportsSuggestedValues()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,7 @@ public class DataTypeSettingsDialog extends AbstractSettingsDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
|
protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
|
||||||
if (settingsDefinition.supportsSuggestedValues()) {
|
if (settingsDefinition.supportsSuggestedValues()) {
|
||||||
return settingsDefinition.getSuggestedValues(getOriginalSettings());
|
return settingsDefinition.getSuggestedValues(getOriginalSettings());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue