mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GP-1560: Add 'Watch' memory and register context actions
This commit is contained in:
parent
4830d035b3
commit
adeefc58c8
14 changed files with 473 additions and 89 deletions
|
@ -119,6 +119,18 @@
|
||||||
<P>This action is always available. It adds a blank watch to the table. Modify the expression
|
<P>This action is always available. It adds a blank watch to the table. Modify the expression
|
||||||
to make the entry useful.</P>
|
to make the entry useful.</P>
|
||||||
|
|
||||||
|
<H3><A name="watch"></A>Watch</H3>
|
||||||
|
|
||||||
|
<P>This action is available in context menus for addresses, selections, or registers. It adds
|
||||||
|
the selected item(s) to the watch table. If the context is derived from a static view, it first
|
||||||
|
attempts to map it to the current trace. For selections, it adds a typeless watch for each
|
||||||
|
range. For single-address locations derived from a listing, it adds a typed watch on the
|
||||||
|
current code unit. For other single-address locations, it adds a 1-byte typeless watch on the
|
||||||
|
address. For registers, it adds a watch on that register. Note that <CODE>contextreg</CODE> and
|
||||||
|
its children cannot be watched, due to a limitation in Sleigh.</P>
|
||||||
|
|
||||||
|
<P>If the context</P>
|
||||||
|
|
||||||
<H3><A name="remove"></A>Remove</H3>
|
<H3><A name="remove"></A>Remove</H3>
|
||||||
|
|
||||||
<P>This action is available when at least one watch is selected. It removes those watches.</P>
|
<P>This action is available when at least one watch is selected. It removes those watches.</P>
|
||||||
|
|
|
@ -374,6 +374,7 @@ public interface DebuggerResources {
|
||||||
String GROUP_TRACE_CLOSE = "Dbg7.b. Trace Close";
|
String GROUP_TRACE_CLOSE = "Dbg7.b. Trace Close";
|
||||||
String GROUP_MAINTENANCE = "Dbg8. Maintenance";
|
String GROUP_MAINTENANCE = "Dbg8. Maintenance";
|
||||||
String GROUP_MAPPING = "Dbg9. Map Modules/Sections";
|
String GROUP_MAPPING = "Dbg9. Map Modules/Sections";
|
||||||
|
String GROUP_WATCHES = "DbgA. Watches";
|
||||||
String GROUP_DIFF_NAV = "DiffNavigate";
|
String GROUP_DIFF_NAV = "DiffNavigate";
|
||||||
|
|
||||||
static void tableRowActivationAction(GTable table, Runnable runnable) {
|
static void tableRowActivationAction(GTable table, Runnable runnable) {
|
||||||
|
@ -1552,7 +1553,7 @@ public interface DebuggerResources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NewMemoryAction{
|
interface NewMemoryAction {
|
||||||
String NAME = "New Memory View";
|
String NAME = "New Memory View";
|
||||||
String DESCRIPTION = "Open a new memory bytes view";
|
String DESCRIPTION = "Open a new memory bytes view";
|
||||||
String GROUP = GROUP_TRANSIENT_VIEWS;
|
String GROUP = GROUP_TRANSIENT_VIEWS;
|
||||||
|
@ -1561,7 +1562,7 @@ public interface DebuggerResources {
|
||||||
|
|
||||||
static ActionBuilder builder(Plugin owner) {
|
static ActionBuilder builder(Plugin owner) {
|
||||||
String ownerName = owner.getName();
|
String ownerName = owner.getName();
|
||||||
return new ActionBuilder(NAME,ownerName)
|
return new ActionBuilder(NAME, ownerName)
|
||||||
.description(DESCRIPTION)
|
.description(DESCRIPTION)
|
||||||
.menuGroup(GROUP)
|
.menuGroup(GROUP)
|
||||||
.menuIcon(ICON)
|
.menuIcon(ICON)
|
||||||
|
@ -1932,6 +1933,24 @@ public interface DebuggerResources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WatchAction {
|
||||||
|
String NAME = "Watch";
|
||||||
|
String DESCRIPTION = "Watch the selected item";
|
||||||
|
String GROUP = GROUP_WATCHES;
|
||||||
|
Icon ICON = ICON_PROVIDER_WATCHES;
|
||||||
|
String HELP_ANCHOR = "watch";
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION)
|
||||||
|
.popupMenuPath(NAME)
|
||||||
|
.popupMenuGroup(GROUP)
|
||||||
|
.popupMenuIcon(ICON)
|
||||||
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface HideScratchSnapshotsAction {
|
interface HideScratchSnapshotsAction {
|
||||||
String NAME = "Hide Scratch";
|
String NAME = "Hide Scratch";
|
||||||
String DESCRIPTION = "Hide negative snaps, typically used as emulation scratch space";
|
String DESCRIPTION = "Hide negative snaps, typically used as emulation scratch space";
|
||||||
|
@ -2042,6 +2061,7 @@ public interface DebuggerResources {
|
||||||
public String getToolTip() {
|
public String getToolTip() {
|
||||||
return "A connected debugger client";
|
return "A connected debugger client";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T> Function<Throwable, T> showError(Component parent, String message) {
|
static <T> Function<Throwable, T> showError(Component parent, String message) {
|
||||||
|
|
|
@ -1109,6 +1109,14 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||||
return loadRegistersAndValues();
|
return loadRegistersAndValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RegisterRow getRegisterRow(Register register) {
|
||||||
|
return regMap.get(register);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectedRow(RegisterRow row) {
|
||||||
|
regsFilterPanel.setSelectedItem(row);
|
||||||
|
}
|
||||||
|
|
||||||
public DebuggerRegistersProvider cloneAsDisconnected() {
|
public DebuggerRegistersProvider cloneAsDisconnected() {
|
||||||
DebuggerRegistersProvider clone = plugin.createNewDisconnectedProvider();
|
DebuggerRegistersProvider clone = plugin.createNewDisconnectedProvider();
|
||||||
clone.coordinatesActivated(current); // This should also enact the same selection
|
clone.coordinatesActivated(current); // This should also enact the same selection
|
||||||
|
|
|
@ -18,10 +18,12 @@ package ghidra.app.plugin.core.debug.gui.watch;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Map.Entry;
|
||||||
import java.util.function.*;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.table.TableColumn;
|
import javax.swing.table.TableColumn;
|
||||||
|
@ -33,12 +35,16 @@ import docking.action.DockingAction;
|
||||||
import docking.action.ToggleDockingAction;
|
import docking.action.ToggleDockingAction;
|
||||||
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.ProgramLocationActionContext;
|
||||||
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;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||||
import ghidra.app.services.DebuggerListingService;
|
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegisterActionContext;
|
||||||
import ghidra.app.services.DebuggerTraceManagerService;
|
import ghidra.app.plugin.core.debug.gui.register.RegisterRow;
|
||||||
|
import ghidra.app.services.*;
|
||||||
|
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
||||||
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;
|
||||||
|
@ -51,16 +57,19 @@ import ghidra.framework.options.annotation.HelpInfo;
|
||||||
import ghidra.framework.plugintool.AutoService;
|
import ghidra.framework.plugintool.AutoService;
|
||||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
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.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.DataTypeConflictException;
|
import ghidra.program.model.data.DataTypeConflictException;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.listing.Listing;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.trace.model.*;
|
import ghidra.trace.model.*;
|
||||||
import ghidra.trace.model.Trace.TraceMemoryBytesChangeType;
|
import ghidra.trace.model.Trace.TraceMemoryBytesChangeType;
|
||||||
import ghidra.trace.model.Trace.TraceMemoryStateChangeType;
|
import ghidra.trace.model.Trace.TraceMemoryStateChangeType;
|
||||||
|
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.Msg;
|
||||||
|
@ -243,6 +252,8 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
// TODO: Allow address marking
|
// TODO: Allow address marking
|
||||||
@AutoServiceConsumed
|
@AutoServiceConsumed
|
||||||
private DebuggerTraceManagerService traceManager; // For goto time (emu mods)
|
private DebuggerTraceManagerService traceManager; // For goto time (emu mods)
|
||||||
|
@AutoServiceConsumed
|
||||||
|
private DebuggerStaticMappingService mappingService; // For listing action
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private final AutoService.Wiring autoServiceWiring;
|
private final AutoService.Wiring autoServiceWiring;
|
||||||
|
|
||||||
|
@ -285,6 +296,9 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
DockingAction actionAdd;
|
DockingAction actionAdd;
|
||||||
DockingAction actionRemove;
|
DockingAction actionRemove;
|
||||||
|
|
||||||
|
DockingAction actionAddFromLocation;
|
||||||
|
DockingAction actionAddFromRegister;
|
||||||
|
|
||||||
private DebuggerWatchActionContext myActionContext;
|
private DebuggerWatchActionContext myActionContext;
|
||||||
|
|
||||||
public DebuggerWatchesProvider(DebuggerWatchesPlugin plugin) {
|
public DebuggerWatchesProvider(DebuggerWatchesPlugin plugin) {
|
||||||
|
@ -423,6 +437,18 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
.enabledWhen(ctx -> !ctx.getWatchRows().isEmpty())
|
.enabledWhen(ctx -> !ctx.getWatchRows().isEmpty())
|
||||||
.onAction(this::activatedRemove)
|
.onAction(this::activatedRemove)
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
|
|
||||||
|
// Pop-up context actions
|
||||||
|
actionAddFromLocation = WatchAction.builder(plugin)
|
||||||
|
.withContext(ProgramLocationActionContext.class)
|
||||||
|
.enabledWhen(this::hasDynamicLocation)
|
||||||
|
.onAction(this::activatedAddFromLocation)
|
||||||
|
.buildAndInstall(tool);
|
||||||
|
actionAddFromRegister = WatchAction.builder(plugin)
|
||||||
|
.withContext(DebuggerRegisterActionContext.class)
|
||||||
|
.enabledWhen(this::hasValidWatchRegister)
|
||||||
|
.onAction(this::activatedAddFromRegister)
|
||||||
|
.buildAndInstall(tool);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean selHasDataType(DebuggerWatchActionContext ctx) {
|
protected boolean selHasDataType(DebuggerWatchActionContext ctx) {
|
||||||
|
@ -549,10 +575,135 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||||
watchTableModel.deleteWith(context.getWatchRows()::contains);
|
watchTableModel.deleteWith(context.getWatchRows()::contains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ProgramLocation getDynamicLocation(ProgramLocation someLoc) {
|
||||||
|
if (someLoc.getProgram() instanceof TraceProgramView) {
|
||||||
|
return someLoc;
|
||||||
|
}
|
||||||
|
return mappingService.getDynamicLocationFromStatic(current.getView(), someLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AddressSetView getDynamicAddresses(Program program, AddressSetView set) {
|
||||||
|
if (program instanceof TraceProgramView) {
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
if (set == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AddressSet result = new AddressSet();
|
||||||
|
for (Entry<TraceSpan, Collection<MappedAddressRange>> ent : mappingService
|
||||||
|
.getOpenMappedViews(program, set)
|
||||||
|
.entrySet()) {
|
||||||
|
if (ent.getKey().getTrace() != current.getTrace()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!ent.getKey().getSpan().contains(current.getSnap())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (MappedAddressRange rng : ent.getValue()) {
|
||||||
|
result.add(rng.getDestinationAddressRange());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasDynamicLocation(ProgramLocationActionContext context) {
|
||||||
|
ProgramLocation dynLoc = getDynamicLocation(context.getLocation());
|
||||||
|
return dynLoc != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryForSelection(ProgramLocationActionContext context) {
|
||||||
|
AddressSetView dynSel = getDynamicAddresses(context.getProgram(), context.getSelection());
|
||||||
|
if (dynSel == null || dynSel.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (AddressRange rng : dynSel) {
|
||||||
|
addWatch(TraceSleighUtils
|
||||||
|
.generateExpressionForRange(current.getTrace().getBaseLanguage(), rng));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryForDataInListing(ProgramLocationActionContext context) {
|
||||||
|
if (!(context instanceof ListingActionContext)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ListingActionContext lac = (ListingActionContext) context;
|
||||||
|
CodeUnit cu = lac.getCodeUnit();
|
||||||
|
if (cu == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AddressSet cuAs = new AddressSet();
|
||||||
|
cuAs.add(cu.getMinAddress(), cu.getMaxAddress());
|
||||||
|
AddressSetView dynCuAs = getDynamicAddresses(context.getProgram(), cuAs);
|
||||||
|
|
||||||
|
// Verify mapping is complete and contiguous
|
||||||
|
if (dynCuAs.getNumAddressRanges() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AddressRange dynCuRng = dynCuAs.getFirstRange();
|
||||||
|
if (dynCuRng.getLength() != cu.getLength()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchRow row = addWatch(TraceSleighUtils
|
||||||
|
.generateExpressionForRange(current.getTrace().getBaseLanguage(), dynCuRng));
|
||||||
|
if (cu instanceof Data) {
|
||||||
|
Data data = (Data) cu;
|
||||||
|
// TODO: Problems may arise if trace and program have different data organizations
|
||||||
|
row.setDataType(data.getDataType());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean trySingleAddress(ProgramLocationActionContext context) {
|
||||||
|
ProgramLocation dynLoc = getDynamicLocation(context.getLocation());
|
||||||
|
if (dynLoc == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
addWatch(TraceSleighUtils.generateExpressionForRange(current.getTrace().getBaseLanguage(),
|
||||||
|
new AddressRangeImpl(dynLoc.getAddress(), dynLoc.getAddress())));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void activatedAddFromLocation(ProgramLocationActionContext context) {
|
||||||
|
if (tryForSelection(context)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tryForDataInListing(context)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
trySingleAddress(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasValidWatchRegister(DebuggerRegisterActionContext context) {
|
||||||
|
RegisterRow row = context.getSelected();
|
||||||
|
if (row == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (row.getRegister().isProcessorContext()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void activatedAddFromRegister(DebuggerRegisterActionContext context) {
|
||||||
|
RegisterRow regRow = context.getSelected();
|
||||||
|
if (regRow == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Register reg = regRow.getRegister();
|
||||||
|
if (reg.isProcessorContext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WatchRow watchRow = addWatch(reg.getName());
|
||||||
|
watchRow.setDataType(regRow.getDataType());
|
||||||
|
}
|
||||||
|
|
||||||
public WatchRow addWatch(String expression) {
|
public WatchRow addWatch(String expression) {
|
||||||
WatchRow row = new WatchRow(this, expression);
|
WatchRow row = new WatchRow(this, "");
|
||||||
row.setCoordinates(current);
|
row.setCoordinates(current);
|
||||||
watchTableModel.add(row);
|
watchTableModel.add(row);
|
||||||
|
row.setExpression(expression);
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,8 +135,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
return staticRange.getMinAddress();
|
return staticRange.getMinAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TraceSnap getTraceSnap() {
|
public TraceSpan getTraceSpan() {
|
||||||
return new DefaultTraceSnap(mapping.getTrace(), mapping.getStartSnap());
|
return new DefaultTraceSpan(mapping.getTrace(), mapping.getLifespan());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TraceAddressSnapRange getTraceAddressSnapRange() {
|
public TraceAddressSnapRange getTraceAddressSnapRange() {
|
||||||
|
@ -499,7 +499,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void collectOpenMappedViews(AddressRange rng,
|
protected void collectOpenMappedViews(AddressRange rng,
|
||||||
Map<TraceSnap, Collection<MappedAddressRange>> result) {
|
Map<TraceSpan, Collection<MappedAddressRange>> result) {
|
||||||
for (Entry<MappingEntry, Address> inPreceeding : inbound.headMapByValue(
|
for (Entry<MappingEntry, Address> inPreceeding : inbound.headMapByValue(
|
||||||
rng.getMaxAddress(), true).entrySet()) {
|
rng.getMaxAddress(), true).entrySet()) {
|
||||||
Address start = inPreceeding.getValue();
|
Address start = inPreceeding.getValue();
|
||||||
|
@ -513,14 +513,14 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
|
|
||||||
AddressRange srcRange = me.staticRange.intersect(rng);
|
AddressRange srcRange = me.staticRange.intersect(rng);
|
||||||
AddressRange dstRange = me.mapProgramRangeToTrace(rng);
|
AddressRange dstRange = me.mapProgramRangeToTrace(rng);
|
||||||
result.computeIfAbsent(me.getTraceSnap(), p -> new TreeSet<>())
|
result.computeIfAbsent(me.getTraceSpan(), p -> new TreeSet<>())
|
||||||
.add(new MappedAddressRange(srcRange, dstRange));
|
.add(new MappedAddressRange(srcRange, dstRange));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(
|
public Map<TraceSpan, Collection<MappedAddressRange>> getOpenMappedViews(
|
||||||
AddressSetView set) {
|
AddressSetView set) {
|
||||||
Map<TraceSnap, Collection<MappedAddressRange>> result = new HashMap<>();
|
Map<TraceSpan, Collection<MappedAddressRange>> result = new HashMap<>();
|
||||||
for (AddressRange rng : set) {
|
for (AddressRange rng : set) {
|
||||||
collectOpenMappedViews(rng, result);
|
collectOpenMappedViews(rng, result);
|
||||||
}
|
}
|
||||||
|
@ -922,7 +922,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
|
public Map<TraceSpan, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
|
||||||
AddressSetView set) {
|
AddressSetView set) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
InfoPerProgram info = requireTrackedInfo(program);
|
InfoPerProgram info = requireTrackedInfo(program);
|
||||||
|
|
|
@ -355,7 +355,7 @@ public interface DebuggerStaticMappingService {
|
||||||
* @param set the destination address set, from which we are mapping back
|
* @param set the destination address set, from which we are mapping back
|
||||||
* @return a map of source traces to corresponding computed source address ranges
|
* @return a map of source traces to corresponding computed source address ranges
|
||||||
*/
|
*/
|
||||||
Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
|
Map<TraceSpan, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
|
||||||
AddressSetView set);
|
AddressSetView set);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -38,9 +38,13 @@ import org.junit.rules.TestName;
|
||||||
import org.junit.rules.TestWatcher;
|
import org.junit.rules.TestWatcher;
|
||||||
import org.junit.runner.Description;
|
import org.junit.runner.Description;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.ActionContextProvider;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
import docking.widgets.tree.GTree;
|
import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import generic.Unique;
|
import generic.Unique;
|
||||||
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.app.plugin.core.debug.gui.action.*;
|
import ghidra.app.plugin.core.debug.gui.action.*;
|
||||||
import ghidra.app.plugin.core.debug.mapping.*;
|
import ghidra.app.plugin.core.debug.mapping.*;
|
||||||
import ghidra.app.plugin.core.debug.service.model.*;
|
import ghidra.app.plugin.core.debug.service.model.*;
|
||||||
|
@ -58,8 +62,7 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
import ghidra.program.util.*;
|
||||||
import ghidra.program.util.ProgramLocation;
|
|
||||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
import ghidra.test.TestEnv;
|
import ghidra.test.TestEnv;
|
||||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||||
|
@ -439,6 +442,23 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static void assertDisabled(ActionContextProvider provider, DockingActionIf action) {
|
||||||
|
ActionContext context = provider.getActionContext(null);
|
||||||
|
assertFalse(action.isEnabledForContext(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void assertEnabled(ActionContextProvider provider, DockingActionIf action) {
|
||||||
|
ActionContext context = provider.getActionContext(null);
|
||||||
|
assertTrue(action.isEnabledForContext(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void performEnabledAction(ActionContextProvider provider,
|
||||||
|
DockingActionIf action, boolean wait) {
|
||||||
|
ActionContext context = provider.getActionContext(null);
|
||||||
|
waitForCondition(() -> action.isEnabledForContext(context));
|
||||||
|
performAction(action, context, wait);
|
||||||
|
}
|
||||||
|
|
||||||
protected static void goTo(ListingPanel listingPanel, ProgramLocation location) {
|
protected static void goTo(ListingPanel listingPanel, ProgramLocation location) {
|
||||||
waitForPass(() -> {
|
waitForPass(() -> {
|
||||||
runSwing(() -> listingPanel.goTo(location));
|
runSwing(() -> listingPanel.goTo(location));
|
||||||
|
@ -448,6 +468,18 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void select(Navigatable nav, Address min, Address max) {
|
||||||
|
select(nav, new ProgramSelection(min, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void select(Navigatable nav, AddressSetView set) {
|
||||||
|
select(nav, new ProgramSelection(set));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void select(Navigatable nav, ProgramSelection sel) {
|
||||||
|
runSwing(() -> nav.setSelection(sel));
|
||||||
|
}
|
||||||
|
|
||||||
protected static LocationTrackingSpec getLocationTrackingSpec(String name) {
|
protected static LocationTrackingSpec getLocationTrackingSpec(String name) {
|
||||||
return LocationTrackingSpec.fromConfigName(name);
|
return LocationTrackingSpec.fromConfigName(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ import org.junit.Test;
|
||||||
|
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
|
|
||||||
import docking.ActionContext;
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import generic.Unique;
|
import generic.Unique;
|
||||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||||
|
@ -43,7 +42,6 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
|
||||||
import ghidra.test.ToyProgramBuilder;
|
import ghidra.test.ToyProgramBuilder;
|
||||||
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||||
import ghidra.trace.model.DefaultTraceLocation;
|
import ghidra.trace.model.DefaultTraceLocation;
|
||||||
|
@ -68,29 +66,20 @@ public class DebuggerCopyActionsPluginTest extends AbstractGhidraHeadedDebuggerG
|
||||||
listingProvider = waitForComponentProvider(DebuggerListingProvider.class);
|
listingProvider = waitForComponentProvider(DebuggerListingProvider.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void select(Address min, Address max) {
|
|
||||||
select(new ProgramSelection(min, max));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void select(AddressSetView set) {
|
|
||||||
select(new ProgramSelection(set));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void select(ProgramSelection sel) {
|
|
||||||
runSwing(() -> {
|
|
||||||
listingProvider.setSelection(sel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void assertDisabled(DockingActionIf action) {
|
protected void assertDisabled(DockingActionIf action) {
|
||||||
ActionContext context = listingProvider.getActionContext(null);
|
assertDisabled(listingProvider, action);
|
||||||
assertFalse(action.isEnabledForContext(context));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void performEnabledAction(DockingActionIf action) {
|
protected void performEnabledAction(DockingActionIf action) {
|
||||||
ActionContext context = listingProvider.getActionContext(null);
|
performEnabledAction(listingProvider, action, false);
|
||||||
waitForCondition(() -> action.isEnabledForContext(context));
|
}
|
||||||
performAction(action, context, false);
|
|
||||||
|
protected void select(Address min, Address max) {
|
||||||
|
select(listingProvider, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void select(AddressSetView set) {
|
||||||
|
select(listingProvider, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -23,9 +23,6 @@ import java.util.List;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import docking.ActionContext;
|
|
||||||
import docking.action.ActionContextProvider;
|
|
||||||
import docking.action.DockingActionIf;
|
|
||||||
import docking.widgets.dialogs.InputDialog;
|
import docking.widgets.dialogs.InputDialog;
|
||||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
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;
|
||||||
|
@ -92,23 +89,6 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||||
// Timestamp is left unchecked, since default is current time
|
// Timestamp is left unchecked, since default is current time
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void assertDisabled(ActionContextProvider provider, DockingActionIf action) {
|
|
||||||
ActionContext context = provider.getActionContext(null);
|
|
||||||
assertFalse(action.isEnabledForContext(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void assertEnabled(ActionContextProvider provider, DockingActionIf action) {
|
|
||||||
ActionContext context = provider.getActionContext(null);
|
|
||||||
assertTrue(action.isEnabledForContext(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void performEnabledAction(ActionContextProvider provider,
|
|
||||||
DockingActionIf action, boolean wait) {
|
|
||||||
ActionContext context = provider.getActionContext(null);
|
|
||||||
waitForCondition(() -> action.isEnabledForContext(context));
|
|
||||||
performAction(action, context, wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // TODO: Technically, this is a plugin action.... Different test case?
|
@Test // TODO: Technically, this is a plugin action.... Different test case?
|
||||||
public void testActionRenameSnapshot() throws Exception {
|
public void testActionRenameSnapshot() throws Exception {
|
||||||
// More often than not, this action will be used from the dynamic listing
|
// More often than not, this action will be used from the dynamic listing
|
||||||
|
|
|
@ -19,30 +19,38 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
|
||||||
import generic.Unique;
|
import generic.Unique;
|
||||||
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
|
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
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.register.*;
|
||||||
|
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
|
||||||
import ghidra.app.services.TraceRecorder;
|
import ghidra.app.services.TraceRecorder;
|
||||||
import ghidra.async.AsyncTestUtils;
|
import ghidra.async.AsyncTestUtils;
|
||||||
import ghidra.dbg.model.TestTargetRegisterBankInThread;
|
import ghidra.dbg.model.TestTargetRegisterBankInThread;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.address.AddressRangeImpl;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.LongDataType;
|
|
||||||
import ghidra.program.model.data.LongLongDataType;
|
|
||||||
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.mem.Memory;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.trace.model.DefaultTraceLocation;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
import ghidra.trace.model.memory.TraceMemoryOperations;
|
import ghidra.trace.model.memory.*;
|
||||||
import ghidra.trace.model.memory.TraceMemoryRegisterSpace;
|
|
||||||
import ghidra.trace.model.thread.TraceThread;
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
import ghidra.trace.util.TraceRegisterUtils;
|
import ghidra.trace.util.TraceRegisterUtils;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.database.UndoableTransaction;
|
import ghidra.util.database.UndoableTransaction;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUITest
|
public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUITest
|
||||||
implements AsyncTestUtils {
|
implements AsyncTestUtils {
|
||||||
|
@ -57,6 +65,9 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||||
protected DebuggerWatchesPlugin watchesPlugin;
|
protected DebuggerWatchesPlugin watchesPlugin;
|
||||||
protected DebuggerWatchesProvider watchesProvider;
|
protected DebuggerWatchesProvider watchesProvider;
|
||||||
protected DebuggerListingPlugin listingPlugin;
|
protected DebuggerListingPlugin listingPlugin;
|
||||||
|
protected DebuggerListingProvider listingProvider;
|
||||||
|
protected DebuggerStaticMappingServicePlugin mappingService;
|
||||||
|
protected CodeViewerProvider codeViewerProvider;
|
||||||
|
|
||||||
protected Register r0;
|
protected Register r0;
|
||||||
protected Register r1;
|
protected Register r1;
|
||||||
|
@ -67,9 +78,15 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUpWatchesProviderTest() throws Exception {
|
public void setUpWatchesProviderTest() throws Exception {
|
||||||
|
// Do this before listing, because DebuggerListing also implements CodeViewer
|
||||||
|
addPlugin(tool, CodeBrowserPlugin.class);
|
||||||
|
codeViewerProvider = waitForComponentProvider(CodeViewerProvider.class);
|
||||||
|
|
||||||
watchesPlugin = addPlugin(tool, DebuggerWatchesPlugin.class);
|
watchesPlugin = addPlugin(tool, DebuggerWatchesPlugin.class);
|
||||||
watchesProvider = waitForComponentProvider(DebuggerWatchesProvider.class);
|
watchesProvider = waitForComponentProvider(DebuggerWatchesProvider.class);
|
||||||
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
|
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
|
||||||
|
listingProvider = waitForComponentProvider(DebuggerListingProvider.class);
|
||||||
|
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
|
||||||
|
|
||||||
createTrace();
|
createTrace();
|
||||||
r0 = tb.language.getRegister("r0");
|
r0 = tb.language.getRegister("r0");
|
||||||
|
@ -404,4 +421,149 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||||
|
|
||||||
assertFalse(bank.regVals.containsKey("r1"));
|
assertFalse(bank.regVals.containsKey("r1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setupUnmappedDataSection() throws Throwable {
|
||||||
|
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||||
|
TraceMemoryOperations mem = tb.trace.getMemoryManager();
|
||||||
|
mem.createRegion("Memory[bin:.data]", 0, tb.range(0x00600000, 0x0060ffff),
|
||||||
|
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
|
||||||
|
}
|
||||||
|
waitForDomainObject(tb.trace);
|
||||||
|
|
||||||
|
traceManager.openTrace(tb.trace);
|
||||||
|
traceManager.activateTrace(tb.trace);
|
||||||
|
waitForSwing();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupMappedDataSection() throws Throwable {
|
||||||
|
createProgramFromTrace();
|
||||||
|
intoProject(tb.trace);
|
||||||
|
intoProject(program);
|
||||||
|
|
||||||
|
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||||
|
TraceMemoryOperations mem = tb.trace.getMemoryManager();
|
||||||
|
mem.createRegion("Memory[bin:.data]", 0, tb.range(0x55750000, 0x5575ffff),
|
||||||
|
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
|
||||||
|
}
|
||||||
|
waitForDomainObject(tb.trace);
|
||||||
|
|
||||||
|
traceManager.openTrace(tb.trace);
|
||||||
|
traceManager.activateTrace(tb.trace);
|
||||||
|
programManager.openProgram(program);
|
||||||
|
|
||||||
|
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
|
||||||
|
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add block", true)) {
|
||||||
|
Memory mem = program.getMemory();
|
||||||
|
mem.createInitializedBlock(".data", tb.addr(stSpace, 0x00600000), 0x10000,
|
||||||
|
(byte) 0, TaskMonitor.DUMMY, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultTraceLocation tloc =
|
||||||
|
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x55750000));
|
||||||
|
ProgramLocation ploc = new ProgramLocation(program, tb.addr(stSpace, 0x00600000));
|
||||||
|
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||||
|
mappingService.addMapping(tloc, ploc, 0x10000, false);
|
||||||
|
}
|
||||||
|
waitForValue(() -> mappingService.getOpenMappedLocation(tloc));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActionWatchViaListingDynamicSelection() throws Throwable {
|
||||||
|
setupUnmappedDataSection();
|
||||||
|
|
||||||
|
select(listingProvider,
|
||||||
|
tb.set(tb.range(0x00600000, 0x0060000f), tb.range(0x00600020, 0x0060002f)));
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
performEnabledAction(listingProvider, watchesProvider.actionAddFromLocation, true);
|
||||||
|
|
||||||
|
List<WatchRow> watches = new ArrayList<>(watchesProvider.watchTableModel.getModelData());
|
||||||
|
watches.sort(Comparator.comparing(WatchRow::getExpression));
|
||||||
|
assertEquals(2, watches.size());
|
||||||
|
assertEquals("*:16 0x00600000:8", watches.get(0).getExpression());
|
||||||
|
assertEquals("*:16 0x00600020:8", watches.get(1).getExpression());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActionWatchViaListingStaticSelection() throws Throwable {
|
||||||
|
setupMappedDataSection();
|
||||||
|
|
||||||
|
select(codeViewerProvider,
|
||||||
|
tb.set(tb.range(0x00600000, 0x0060000f), tb.range(0x00600020, 0x0060002f)));
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
performEnabledAction(codeViewerProvider, watchesProvider.actionAddFromLocation, true);
|
||||||
|
|
||||||
|
List<WatchRow> watches = new ArrayList<>(watchesProvider.watchTableModel.getModelData());
|
||||||
|
watches.sort(Comparator.comparing(WatchRow::getExpression));
|
||||||
|
assertEquals(2, watches.size());
|
||||||
|
assertEquals("*:16 0x55750000:8", watches.get(0).getExpression());
|
||||||
|
assertEquals("*:16 0x55750020:8", watches.get(1).getExpression());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActionWatchViaListingDynamicDataUnit() throws Throwable {
|
||||||
|
setupUnmappedDataSection();
|
||||||
|
|
||||||
|
Structure structDt = new StructureDataType("myStruct", 0);
|
||||||
|
structDt.add(DWordDataType.dataType, "field0", "");
|
||||||
|
structDt.add(DWordDataType.dataType, "field4", "");
|
||||||
|
|
||||||
|
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||||
|
tb.trace.getCodeManager()
|
||||||
|
.definedData()
|
||||||
|
.create(Range.atLeast(0L), tb.addr(0x00600000), structDt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Test with expanded structure?
|
||||||
|
|
||||||
|
performEnabledAction(listingProvider, watchesProvider.actionAddFromLocation, true);
|
||||||
|
|
||||||
|
WatchRow watch = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
|
||||||
|
assertEquals("*:8 0x00600000:8", watch.getExpression());
|
||||||
|
assertTypeEquals(structDt, watch.getDataType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActionWatchViaListingStaticDataUnit() throws Throwable {
|
||||||
|
setupMappedDataSection();
|
||||||
|
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
|
||||||
|
|
||||||
|
Structure structDt = new StructureDataType("myStruct", 0);
|
||||||
|
structDt.add(DWordDataType.dataType, "field0", "");
|
||||||
|
structDt.add(DWordDataType.dataType, "field4", "");
|
||||||
|
|
||||||
|
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add data", true)) {
|
||||||
|
program.getListing().createData(tb.addr(stSpace, 0x00600000), structDt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Test with expanded structure?
|
||||||
|
|
||||||
|
performEnabledAction(codeViewerProvider, watchesProvider.actionAddFromLocation, true);
|
||||||
|
|
||||||
|
WatchRow watch = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
|
||||||
|
assertEquals("*:8 0x55750000:8", watch.getExpression());
|
||||||
|
assertTypeEquals(structDt, watch.getDataType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActionWatchViaRegisters() throws Throwable {
|
||||||
|
addPlugin(tool, DebuggerRegistersPlugin.class);
|
||||||
|
DebuggerRegistersProvider registersProvider =
|
||||||
|
waitForComponentProvider(DebuggerRegistersProvider.class);
|
||||||
|
traceManager.openTrace(tb.trace);
|
||||||
|
traceManager.activateThread(thread);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
RegisterRow rowR0 = registersProvider.getRegisterRow(r0);
|
||||||
|
rowR0.setDataType(PointerDataType.dataType);
|
||||||
|
registersProvider.setSelectedRow(rowR0);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
performEnabledAction(registersProvider, watchesProvider.actionAddFromRegister, true);
|
||||||
|
|
||||||
|
WatchRow watch = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
|
||||||
|
assertEquals("r0", watch.getExpression());
|
||||||
|
assertTypeEquals(PointerDataType.dataType, watch.getDataType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -384,7 +384,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
|
||||||
copyTrace();
|
copyTrace();
|
||||||
add2ndMapping();
|
add2ndMapping();
|
||||||
|
|
||||||
Map<TraceSnap, Collection<MappedAddressRange>> views =
|
Map<TraceSpan, Collection<MappedAddressRange>> views =
|
||||||
mappingService.getOpenMappedViews(program, new AddressSet());
|
mappingService.getOpenMappedViews(program, new AddressSet());
|
||||||
assertTrue(views.isEmpty());
|
assertTrue(views.isEmpty());
|
||||||
}
|
}
|
||||||
|
@ -407,12 +407,14 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
|
||||||
// After
|
// After
|
||||||
set.add(stSpace.getAddress(0xbadbadbadL), stSpace.getAddress(0xbadbadbadL + 0xff));
|
set.add(stSpace.getAddress(0xbadbadbadL), stSpace.getAddress(0xbadbadbadL + 0xff));
|
||||||
|
|
||||||
Map<TraceSnap, Collection<MappedAddressRange>> views =
|
Map<TraceSpan, Collection<MappedAddressRange>> views =
|
||||||
mappingService.getOpenMappedViews(program, set);
|
mappingService.getOpenMappedViews(program, set);
|
||||||
Msg.info(this, views);
|
Msg.info(this, views);
|
||||||
assertEquals(2, views.size());
|
assertEquals(2, views.size());
|
||||||
Collection<MappedAddressRange> mappedSet1 = views.get(new DefaultTraceSnap(tb.trace, 0));
|
Collection<MappedAddressRange> mappedSet1 =
|
||||||
Collection<MappedAddressRange> mappedSet2 = views.get(new DefaultTraceSnap(copy, 0));
|
views.get(new DefaultTraceSpan(tb.trace, Range.atLeast(0L)));
|
||||||
|
Collection<MappedAddressRange> mappedSet2 =
|
||||||
|
views.get(new DefaultTraceSpan(copy, Range.atLeast(0L)));
|
||||||
|
|
||||||
assertEquals(Set.of(
|
assertEquals(Set.of(
|
||||||
new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
|
new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
import ghidra.pcode.exec.*;
|
import ghidra.pcode.exec.*;
|
||||||
import ghidra.pcode.utils.Utils;
|
import ghidra.pcode.utils.Utils;
|
||||||
|
import ghidra.program.model.address.AddressRange;
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.Language;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
|
@ -155,4 +156,15 @@ public enum TraceSleighUtils {
|
||||||
SleighProgramCompiler.compileExpression((SleighLanguage) language, expr),
|
SleighProgramCompiler.compileExpression((SleighLanguage) language, expr),
|
||||||
trace, snap, thread, frame);
|
trace, snap, thread, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String generateExpressionForRange(Language language, AddressRange range) {
|
||||||
|
AddressSpace space = range.getAddressSpace();
|
||||||
|
long length = range.getLength();
|
||||||
|
long offset = range.getMinAddress().getOffset();
|
||||||
|
int ptrSize = space.getPointerSize();
|
||||||
|
if (language != null && language.getDefaultSpace() == space) {
|
||||||
|
return String.format("*:%d 0x%08x:%d", length, offset, ptrSize);
|
||||||
|
}
|
||||||
|
return String.format("*[%s]:%d 0x%08x:%d", space.getName(), length, offset, ptrSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,18 +17,26 @@ package ghidra.trace.model;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
|
||||||
|
import ghidra.trace.database.DBTraceUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: This is used to mark <trace,snap>; regardless of whether that snapshot is actually in the
|
* NOTE: This is used to mark <trace,snap>; regardless of whether that snapshot is actually in the
|
||||||
* database.... Cannot just use TraceSnapshot here.
|
* database.... Cannot just use TraceSnapshot here.
|
||||||
*/
|
*/
|
||||||
public class DefaultTraceSnap implements TraceSnap {
|
public class DefaultTraceSpan implements TraceSpan {
|
||||||
|
|
||||||
private final Trace trace;
|
private final Trace trace;
|
||||||
private final long snap;
|
private final Range<Long> span;
|
||||||
|
|
||||||
public DefaultTraceSnap(Trace trace, long snap) {
|
private final int hash;
|
||||||
|
|
||||||
|
public DefaultTraceSpan(Trace trace, Range<Long> span) {
|
||||||
this.trace = trace;
|
this.trace = trace;
|
||||||
this.snap = snap;
|
this.span = span;
|
||||||
|
|
||||||
|
this.hash = Objects.hash(trace, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -37,13 +45,13 @@ public class DefaultTraceSnap implements TraceSnap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSnap() {
|
public Range<Long> getSpan() {
|
||||||
return snap;
|
return span;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "TraceSnap<" + trace + ": " + snap + ">";
|
return "TraceSnap<" + trace + ": " + span + ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,14 +59,14 @@ public class DefaultTraceSnap implements TraceSnap {
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!(obj instanceof DefaultTraceSnap)) {
|
if (!(obj instanceof DefaultTraceSpan)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DefaultTraceSnap that = (DefaultTraceSnap) obj;
|
DefaultTraceSpan that = (DefaultTraceSpan) obj;
|
||||||
if (this.trace != that.trace) {
|
if (this.trace != that.trace) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.snap != that.snap) {
|
if (!Objects.equals(this.span, that.span)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -66,11 +74,11 @@ public class DefaultTraceSnap implements TraceSnap {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(trace, snap);
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(TraceSnap that) {
|
public int compareTo(TraceSpan that) {
|
||||||
if (this == that) {
|
if (this == that) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +87,13 @@ public class DefaultTraceSnap implements TraceSnap {
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
result = Long.compareUnsigned(this.snap, that.getSnap());
|
result = Long.compare(DBTraceUtils.lowerEndpoint(this.span),
|
||||||
|
DBTraceUtils.lowerEndpoint(that.getSpan()));
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result = Long.compare(DBTraceUtils.upperEndpoint(this.span),
|
||||||
|
DBTraceUtils.upperEndpoint(that.getSpan()));
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
|
@ -15,8 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.trace.model;
|
package ghidra.trace.model;
|
||||||
|
|
||||||
public interface TraceSnap extends Comparable<TraceSnap> {
|
import com.google.common.collect.Range;
|
||||||
|
|
||||||
|
public interface TraceSpan extends Comparable<TraceSpan> {
|
||||||
Trace getTrace();
|
Trace getTrace();
|
||||||
|
|
||||||
long getSnap();
|
Range<Long> getSpan();
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue