GP-4236: Implement new columns: PC, Func, Mod, SP. Remove: Created, Destroyed

This commit is contained in:
Dan 2024-02-22 12:19:25 -05:00
parent f5008f9f99
commit 270fb01400
24 changed files with 320 additions and 200 deletions

View File

@ -15,10 +15,8 @@
*/
package ghidra.debug.api.action;
import java.util.concurrent.CompletableFuture;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.TraceAddressSnapRange;
@ -47,21 +45,21 @@ public interface LocationTracker {
* The address returned must be in the host platform's language, i.e., please use
* {@link TracePlatform#mapGuestToHost(Address)}.
*
* @param tool the tool containing the provider
* @param provider the service provider (usually the tool)
* @param coordinates the trace, thread, snap, etc., of the tool
* @return the address to navigate to
*/
Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates);
Address computeTraceAddress(ServiceProvider provider, DebuggerCoordinates coordinates);
/**
* Get the suggested input if the user activates "Go To" while this tracker is active
*
* @param tool the tool containing the provider
* @param provider the service provider (usually the tool)
* @param coordinates the user's current coordinates
* @param location the user's current location
* @return the suggested address or Sleigh expression
*/
GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
GoToInput getDefaultGoToInput(ServiceProvider provider, DebuggerCoordinates coordinates,
ProgramLocation location);
// TODO: Is there a way to generalize these so that other dependencies need not

View File

@ -119,7 +119,7 @@ public interface LocationTrackingSpec {
/**
* Compute a title prefix to indicate this tracking specification
*
* @param thread the provider's current thread
* @param coordinates the current coordinates
* @return a prefix, or {@code null} to use a default
*/
String computeTitle(DebuggerCoordinates coordinates);

View File

@ -20,7 +20,7 @@ import javax.swing.Icon;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.debug.api.action.*;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.TraceAddressSnapRange;
@ -63,12 +63,12 @@ public enum NoneLocationTrackingSpec implements LocationTrackingSpec, LocationTr
}
@Override
public Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates) {
public Address computeTraceAddress(ServiceProvider provider, DebuggerCoordinates coordinates) {
return null;
}
@Override
public GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
public GoToInput getDefaultGoToInput(ServiceProvider provider, DebuggerCoordinates coordinates,
ProgramLocation location) {
if (location == null) {
return GoToInput.fromString("00000000");

View File

@ -20,7 +20,7 @@ import javax.swing.Icon;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.debug.api.action.*;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Trace;
@ -66,9 +66,12 @@ public enum PCByStackLocationTrackingSpec implements LocationTrackingSpec, Locat
}
@Override
public Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates) {
public Address computeTraceAddress(ServiceProvider provider, DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
TraceThread thread = coordinates.getThread();
if (thread == null) {
return null;
}
long snap = coordinates.getSnap();
TraceStack stack = trace.getStackManager().getLatestStack(thread, snap);
if (stack == null) {
@ -83,11 +86,11 @@ public enum PCByStackLocationTrackingSpec implements LocationTrackingSpec, Locat
}
@Override
public GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
public GoToInput getDefaultGoToInput(ServiceProvider provider, DebuggerCoordinates coordinates,
ProgramLocation location) {
Address address = computeTraceAddress(tool, coordinates);
Address address = computeTraceAddress(provider, coordinates);
if (address == null) {
return NoneLocationTrackingSpec.INSTANCE.getDefaultGoToInput(tool, coordinates,
return NoneLocationTrackingSpec.INSTANCE.getDefaultGoToInput(provider, coordinates,
location);
}
return GoToInput.fromAddress(address);

View File

@ -20,7 +20,7 @@ import javax.swing.Icon;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.debug.api.action.*;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.TraceAddressSnapRange;
@ -68,20 +68,20 @@ public enum PCLocationTrackingSpec implements LocationTrackingSpec, LocationTrac
}
@Override
public Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates) {
public Address computeTraceAddress(ServiceProvider provider, DebuggerCoordinates coordinates) {
if (coordinates.getTime().isSnapOnly()) {
Address pc = BY_STACK.computeTraceAddress(tool, coordinates);
Address pc = BY_STACK.computeTraceAddress(provider, coordinates);
if (pc != null) {
return pc;
}
}
return BY_REG.computeTraceAddress(tool, coordinates);
return BY_REG.computeTraceAddress(provider, coordinates);
}
@Override
public GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
public GoToInput getDefaultGoToInput(ServiceProvider provider, DebuggerCoordinates coordinates,
ProgramLocation location) {
return BY_REG.getDefaultGoToInput(tool, coordinates, location);
return BY_REG.getDefaultGoToInput(provider, coordinates, location);
}
// Note it does no good to override affectByRegChange. It must do what we'd avoid anyway.

View File

@ -17,7 +17,7 @@ package ghidra.app.plugin.core.debug.gui.action;
import ghidra.debug.api.action.*;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
@ -51,10 +51,13 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec, Loca
}
@Override
default Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates) {
default Address computeTraceAddress(ServiceProvider provider, DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
TracePlatform platform = coordinates.getPlatform();
TraceThread thread = coordinates.getThread();
if (thread == null) {
return null;
}
long viewSnap = coordinates.getViewSnap();
long snap = coordinates.getSnap();
int frame = coordinates.getFrame();
@ -88,7 +91,7 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec, Loca
}
@Override
default GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
default GoToInput getDefaultGoToInput(ServiceProvider provider, DebuggerCoordinates coordinates,
ProgramLocation location) {
Register register = computeRegister(coordinates);
return GoToInput.offsetOnly(register.getName());

View File

@ -24,7 +24,7 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.debug.api.action.*;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.debug.api.watch.WatchRow;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.DebuggerPcodeUtils.WatchValue;
import ghidra.pcode.exec.SleighUtils.AddressOf;
@ -117,11 +117,12 @@ public class WatchLocationTrackingSpec implements LocationTrackingSpec {
private PcodeExpression compiled;
@Override
public Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates) {
public Address computeTraceAddress(ServiceProvider provider,
DebuggerCoordinates coordinates) {
if (!Objects.equals(current, coordinates) || exec == null) {
current = coordinates;
exec = current.getPlatform() == null ? null
: DebuggerPcodeUtils.buildWatchExecutor(tool, coordinates);
: DebuggerPcodeUtils.buildWatchExecutor(provider, coordinates);
}
else {
exec.getState().clear();
@ -129,20 +130,20 @@ public class WatchLocationTrackingSpec implements LocationTrackingSpec {
if (current.getTrace() == null) {
return null;
}
compiled = DebuggerPcodeUtils.compileExpression(tool, current, expression);
compiled = DebuggerPcodeUtils.compileExpression(provider, current, expression);
WatchValue value = compiled.evaluate(exec);
return value == null ? null : value.address();
}
@Override
public GoToInput getDefaultGoToInput(PluginTool tool, DebuggerCoordinates coordinates,
ProgramLocation location) {
public GoToInput getDefaultGoToInput(ServiceProvider provider,
DebuggerCoordinates coordinates, ProgramLocation location) {
TracePlatform platform = current.getPlatform();
String defaultSpace =
platform == null ? "ram" : platform.getLanguage().getDefaultSpace().getName();
AddressOf addrOf = SleighUtils.recoverAddressOf(defaultSpace, expression);
if (addrOf == null) {
return NoneLocationTrackingSpec.INSTANCE.getDefaultGoToInput(tool, coordinates,
return NoneLocationTrackingSpec.INSTANCE.getDefaultGoToInput(provider, coordinates,
location);
}
return new GoToInput(addrOf.space(),

View File

@ -54,6 +54,11 @@ public abstract class TraceValueObjectPropertyColumn<T>
super.getTableCellRendererComponent(data);
@SuppressWarnings("unchecked")
ValueProperty<T> p = (ValueProperty<T>) data.getValue();
if (p == null) {
setText("");
setToolTipText("");
return this;
}
setText(p.getHtmlDisplay());
setToolTipText(p.getToolTip());
setForeground(getForegroundFor(data.getTable(), p.isModified(), data.isSelected()));

View File

@ -40,20 +40,8 @@ public class ModuleRow {
}
}
public static String computeShortName(String path) {
int sep = path.lastIndexOf('\\');
if (sep > 0 && sep < path.length()) {
path = path.substring(sep + 1);
}
sep = path.lastIndexOf('/');
if (sep > 0 && sep < path.length()) {
path = path.substring(sep + 1);
}
return path;
}
public String getShortName() {
return computeShortName(module.getName());
return DebuggerStaticMappingUtils.computeModuleShortName(module.getName());
}
public String getName() {

View File

@ -27,6 +27,7 @@ import ghidra.app.plugin.core.debug.gui.model.AbstractQueryTablePanel.CellActiva
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueRow;
import ghidra.app.plugin.core.debug.gui.model.columns.TraceValueKeyColumn;
import ghidra.app.plugin.core.debug.gui.model.columns.TraceValueObjectAttributeColumn;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.dbg.target.TargetStack;
import ghidra.dbg.target.TargetStackFrame;
@ -40,7 +41,6 @@ import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.trace.model.Trace;
import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.stack.TraceObjectStackFrame;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObjectValue;
@ -84,7 +84,8 @@ public class DebuggerStackPanel extends AbstractObjectsTableBasedPanel<TraceObje
ServiceProvider serviceProvider) throws IllegalArgumentException {
TraceObjectValue value =
rowObject.getAttributeEntry(TargetStackFrame.PC_ATTRIBUTE_NAME);
return value == null ? null : provider.getFunction(value.castValue());
return DebuggerStaticMappingUtils.getFunction(value.castValue(), provider.current,
serviceProvider);
}
}
@ -99,7 +100,7 @@ public class DebuggerStackPanel extends AbstractObjectsTableBasedPanel<TraceObje
ServiceProvider serviceProvider) throws IllegalArgumentException {
TraceObjectValue value =
rowObject.getAttributeEntry(TargetStackFrame.PC_ATTRIBUTE_NAME);
return value == null ? null : provider.getModule(value.castValue());
return DebuggerStaticMappingUtils.getModuleName(value.castValue(), provider.current);
}
}

View File

@ -30,17 +30,12 @@ import docking.action.DockingAction;
import docking.action.builder.ActionBuilder;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.modules.ModuleRow;
import ghidra.app.plugin.core.debug.stack.UnwindStackCommand;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.*;
import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.Trace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.HelpLocation;
@ -192,39 +187,4 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
legacyPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
}
}
public Function getFunction(Address pc) {
if (pc == null) {
return null;
}
if (mappingService == null) {
return null;
}
TraceThread curThread = current.getThread();
if (curThread == null) {
return null;
}
TraceLocation dloc = new DefaultTraceLocation(curThread.getTrace(),
curThread, Lifespan.at(current.getSnap()), pc);
ProgramLocation sloc = mappingService.getOpenMappedLocation(dloc);
if (sloc == null) {
return null;
}
return sloc.getProgram().getFunctionManager().getFunctionContaining(sloc.getAddress());
}
public String getModule(Address pc) {
if (pc == null) {
return null;
}
Trace trace = current.getTrace();
if (trace == null) {
return null;
}
for (TraceModule module : trace.getModuleManager().getModulesAt(current.getSnap(), pc)) {
// Just take the first
return ModuleRow.computeShortName(module.getName());
}
return null;
}
}

View File

@ -16,6 +16,7 @@
package ghidra.app.plugin.core.debug.gui.stack;
import db.Transaction;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.trace.model.stack.TraceStackFrame;
@ -84,11 +85,12 @@ public class StackFrameRow {
}
public Function getFunction() {
return panel.provider.getFunction(getProgramCounter());
return DebuggerStaticMappingUtils.getFunction(getProgramCounter(), panel.current,
panel.provider.getTool());
}
public String getModule() {
return panel.provider.getModule(getProgramCounter());
return DebuggerStaticMappingUtils.getModuleName(getProgramCounter(), panel.current);
}
protected void update() {

View File

@ -37,6 +37,7 @@ import ghidra.framework.model.DomainObjectChangeRecord;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.AutoService;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.Address;
import ghidra.trace.model.*;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.thread.TraceThreadManager;
@ -55,32 +56,40 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
protected enum ThreadTableColumns
implements EnumeratedTableColumn<ThreadTableColumns, ThreadRow> {
NAME("Name", String.class, ThreadRow::getName, ThreadRow::setName, true),
CREATED("Created", Long.class, ThreadRow::getCreationSnap, true),
DESTROYED("Destroyed", String.class, ThreadRow::getDestructionSnap, true),
STATE("State", ThreadState.class, ThreadRow::getState, true),
COMMENT("Comment", String.class, ThreadRow::getComment, ThreadRow::setComment, true),
PLOT("Plot", Lifespan.class, ThreadRow::getLifespan, false);
NAME("Name", String.class, ThreadRow::getName, ThreadRow::setName, true, true),
PC("PC", Address.class, ThreadRow::getProgramCounter, true, true),
FUNCTION("Function", ghidra.program.model.listing.Function.class, ThreadRow::getFunction, //
true, true),
MODULE("Module", String.class, ThreadRow::getModule, true, false),
SP("SP", Address.class, ThreadRow::getStackPointer, true, false),
CREATED("Created", Long.class, ThreadRow::getCreationSnap, true, false),
DESTROYED("Destroyed", String.class, ThreadRow::getDestructionSnap, true, false),
STATE("State", ThreadState.class, ThreadRow::getState, true, true),
COMMENT("Comment", String.class, ThreadRow::getComment, ThreadRow::setComment, true, false),
PLOT("Plot", Lifespan.class, ThreadRow::getLifespan, false, true);
private final String header;
private final Function<ThreadRow, ?> getter;
private final BiConsumer<ThreadRow, Object> setter;
private final boolean sortable;
private final boolean visible;
private final Class<?> cls;
<T> ThreadTableColumns(String header, Class<T> cls, Function<ThreadRow, T> getter,
boolean sortable) {
this(header, cls, getter, null, sortable);
boolean sortable, boolean visible) {
this(header, cls, getter, null, sortable, visible);
}
@SuppressWarnings("unchecked")
<T> ThreadTableColumns(String header, Class<T> cls, Function<ThreadRow, T> getter,
BiConsumer<ThreadRow, T> setter, boolean sortable) {
BiConsumer<ThreadRow, T> setter, boolean sortable, boolean visible) {
this.header = header;
this.cls = cls;
this.getter = getter;
this.setter = (BiConsumer<ThreadRow, Object>) setter;
this.sortable = sortable;
this.visible = visible;
}
@Override
@ -108,6 +117,11 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
return sortable;
}
@Override
public boolean isVisible() {
return visible;
}
@Override
public void setValueOf(ThreadRow row, Object value) {
setter.accept(row, value);
@ -253,22 +267,16 @@ public class DebuggerLegacyThreadsPanel extends JPanel {
TableColumnModel columnModel = threadTable.getColumnModel();
TableColumn colName = columnModel.getColumn(ThreadTableColumns.NAME.ordinal());
colName.setPreferredWidth(100);
colName.setCellRenderer(boldCurrentRenderer);
TableColumn colCreated = columnModel.getColumn(ThreadTableColumns.CREATED.ordinal());
colCreated.setPreferredWidth(10);
colCreated.setCellRenderer(boldCurrentRenderer);
TableColumn colDestroyed = columnModel.getColumn(ThreadTableColumns.DESTROYED.ordinal());
colDestroyed.setPreferredWidth(10);
colDestroyed.setCellRenderer(boldCurrentRenderer);
TableColumn colState = columnModel.getColumn(ThreadTableColumns.STATE.ordinal());
colState.setPreferredWidth(20);
colState.setCellRenderer(boldCurrentRenderer);
TableColumn colComment = columnModel.getColumn(ThreadTableColumns.COMMENT.ordinal());
colComment.setPreferredWidth(100);
colComment.setCellRenderer(boldCurrentRenderer);
TableColumn colPlot = columnModel.getColumn(ThreadTableColumns.PLOT.ordinal());
colPlot.setPreferredWidth(200);
colPlot.setCellRenderer(spanRenderer);
colPlot.setHeaderRenderer(headerRenderer);

View File

@ -20,12 +20,16 @@ import java.util.List;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import docking.widgets.table.AbstractDynamicTableColumn;
import docking.widgets.table.RangeCursorTableHeaderRenderer.SeekListener;
import docking.widgets.table.TableColumnDescriptor;
import docking.widgets.table.threaded.ThreadedTableModelListener;
import ghidra.app.plugin.core.debug.gui.action.PCLocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.action.SPLocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.model.*;
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.*;
import ghidra.app.plugin.core.debug.gui.model.columns.*;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.dbg.target.*;
import ghidra.dbg.target.schema.TargetObjectSchema;
@ -34,10 +38,10 @@ import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.trace.model.Lifespan;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.trace.model.Trace;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObjectValue;
import ghidra.trace.model.thread.TraceObjectThread;
public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceObjectThread> {
@ -67,53 +71,92 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
}
}
private abstract static class AbstractThreadLifeBoundColumn
extends TraceValueObjectPropertyColumn<Long> {
public AbstractThreadLifeBoundColumn() {
super(Long.class);
private Address computeProgramCounter(DebuggerCoordinates coords) {
// TODO: Cheating a bit. Also, can user configure whether by stack or regs?
return PCLocationTrackingSpec.INSTANCE.computeTraceAddress(provider.getTool(),
coords);
}
private class ThreadPcColumn extends TraceValueObjectPropertyColumn<Address> {
public ThreadPcColumn() {
super(Address.class);
}
abstract Long fromLifespan(Lifespan lifespan);
@Override
public ValueProperty<Long> getProperty(ValueRow row) {
return new ValueDerivedProperty<>(row, Long.class) {
public ValueProperty<Address> getProperty(ValueRow row) {
TraceObject obj = row.getValue().getChild();
DebuggerCoordinates coords = provider.current.object(obj);
return new ValueAddressProperty(row) {
@Override
public Long getValue() {
// De-duplication may not select parent value at current snap
TraceObjectValue curVal =
row.getValue().getChild().getCanonicalParent(row.currentSnap());
if (curVal == null) {
// Thread is not actually alive a current snap
return null;
}
return fromLifespan(curVal.getLifespan());
public Address getValue() {
return computeProgramCounter(coords);
}
};
}
}
private static class ThreadCreatedColumn extends AbstractThreadLifeBoundColumn {
@Override
public String getColumnName() {
return "Created";
}
@Override
Long fromLifespan(Lifespan lifespan) {
return lifespan.minIsFinite() ? lifespan.lmin() : null;
return "PC";
}
}
private static class ThreadDestroyedColumn extends AbstractThreadLifeBoundColumn {
private class ThreadFunctionColumn
extends AbstractDynamicTableColumn<ValueRow, Function, Trace> {
@Override
public String getColumnName() {
return "Destroyed";
return "Function";
}
@Override
Long fromLifespan(Lifespan lifespan) {
return lifespan.maxIsFinite() ? lifespan.lmax() : null;
public Function getValue(ValueRow rowObject, Settings settings, Trace data,
ServiceProvider serviceProvider) throws IllegalArgumentException {
DebuggerCoordinates coords = provider.current.object(rowObject.currentObject());
Address pc = computeProgramCounter(coords);
if (pc == null) {
return null;
}
return DebuggerStaticMappingUtils.getFunction(pc, coords, serviceProvider);
}
}
private class ThreadModuleColumn extends AbstractDynamicTableColumn<ValueRow, String, Trace> {
@Override
public String getColumnName() {
return "Module";
}
@Override
public String getValue(ValueRow rowObject, Settings settings, Trace data,
ServiceProvider serviceProvider) throws IllegalArgumentException {
DebuggerCoordinates coords = provider.current.object(rowObject.currentObject());
Address pc = computeProgramCounter(coords);
if (pc == null) {
return null;
}
return DebuggerStaticMappingUtils.getModuleName(pc, coords);
}
}
private class ThreadSpColumn extends TraceValueObjectPropertyColumn<Address> {
public ThreadSpColumn() {
super(Address.class);
}
@Override
public ValueProperty<Address> getProperty(ValueRow row) {
DebuggerCoordinates coords = provider.current.object(row.currentObject());
return new ValueAddressProperty(row) {
@Override
public Address getValue() {
return SPLocationTrackingSpec.INSTANCE.computeTraceAddress(provider.getTool(),
coords);
}
};
}
@Override
public String getColumnName() {
return "SP";
}
}
@ -144,7 +187,7 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
private static class ThreadPlotColumn extends TraceValueLifePlotColumn {
}
private static class ThreadTableModel extends ObjectTableModel {
private class ThreadTableModel extends ObjectTableModel {
protected ThreadTableModel(Plugin plugin) {
super(plugin);
}
@ -154,10 +197,12 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
TableColumnDescriptor<ValueRow> descriptor = new TableColumnDescriptor<>();
descriptor.addHiddenColumn(new ThreadPathColumn());
descriptor.addVisibleColumn(new ThreadNameColumn(), 1, true);
descriptor.addVisibleColumn(new ThreadCreatedColumn());
descriptor.addVisibleColumn(new ThreadDestroyedColumn());
descriptor.addVisibleColumn(new ThreadPcColumn());
descriptor.addVisibleColumn(new ThreadFunctionColumn());
descriptor.addHiddenColumn(new ThreadModuleColumn());
descriptor.addHiddenColumn(new ThreadSpColumn());
descriptor.addVisibleColumn(new ThreadStateColumn());
descriptor.addVisibleColumn(new ThreadCommentColumn());
descriptor.addHiddenColumn(new ThreadCommentColumn());
descriptor.addVisibleColumn(new ThreadPlotColumn());
return descriptor;
}

View File

@ -16,8 +16,14 @@
package ghidra.app.plugin.core.debug.gui.thread;
import db.Transaction;
import ghidra.app.plugin.core.debug.gui.action.PCLocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.action.SPLocationTrackingSpec;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace;
import ghidra.trace.model.thread.TraceThread;
@ -50,6 +56,34 @@ public class ThreadRow {
return thread.getName();
}
private Address computeProgramCounter(DebuggerCoordinates coords) {
// TODO: Cheating a bit. Also, can user configure whether by stack or regs?
return PCLocationTrackingSpec.INSTANCE.computeTraceAddress(provider.getTool(),
coords);
}
public Address getProgramCounter() {
DebuggerCoordinates coords = provider.current.thread(thread);
return computeProgramCounter(coords);
}
public Function getFunction() {
DebuggerCoordinates coords = provider.current.thread(thread);
Address pc = computeProgramCounter(coords);
return DebuggerStaticMappingUtils.getFunction(pc, coords, provider.getTool());
}
public String getModule() {
DebuggerCoordinates coords = provider.current.thread(thread);
Address pc = computeProgramCounter(coords);
return DebuggerStaticMappingUtils.getModuleName(pc, coords);
}
public Address getStackPointer() {
DebuggerCoordinates coords = provider.current.thread(thread);
return SPLocationTrackingSpec.INSTANCE.computeTraceAddress(provider.getTool(), coords);
}
public long getCreationSnap() {
return thread.getCreationSnap();
}

View File

@ -17,7 +17,7 @@ package ghidra.app.plugin.core.debug.service.emulation.data;
import ghidra.debug.api.emulation.*;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.pcode.exec.trace.data.AbstractPcodeTraceAccess;
import ghidra.trace.model.guest.TracePlatform;
@ -31,21 +31,22 @@ public abstract class AbstractPcodeDebuggerAccess<S extends PcodeDebuggerMemoryA
extends AbstractPcodeTraceAccess<S, L>
implements PcodeDebuggerAccess {
protected final PluginTool tool;
protected final ServiceProvider provider;
protected final Target target;
/**
* Construct a shim
*
* @param tool the tool controlling the session
* @param recorder the target's recorder
* @param provider the service provider (usually the tool)
* @param target the target
* @param platform the associated platform, having the same trace as the recorder
* @param snap the associated snap
*/
public AbstractPcodeDebuggerAccess(PluginTool tool, Target target, TracePlatform platform,
public AbstractPcodeDebuggerAccess(ServiceProvider provider, Target target,
TracePlatform platform,
long snap) {
super(platform, snap);
this.tool = tool;
this.provider = provider;
this.target = target;
}
}

View File

@ -16,7 +16,7 @@
package ghidra.app.plugin.core.debug.service.emulation.data;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.thread.TraceThread;
@ -30,25 +30,25 @@ public class DefaultPcodeDebuggerAccess extends
/**
* Construct a shim
*
* @param tool the tool controlling the session
* @param provider the service provider (usually the tool)
* @param target the target
* @param platform the associated platform, having the same trace as the recorder
* @param snap the associated snap
*/
public DefaultPcodeDebuggerAccess(PluginTool tool, Target target, TracePlatform platform,
long snap) {
super(tool, target, platform, snap);
public DefaultPcodeDebuggerAccess(ServiceProvider provider, Target target,
TracePlatform platform, long snap) {
super(provider, target, platform, snap);
}
@Override
protected DefaultPcodeDebuggerMemoryAccess newDataForSharedState() {
return new DefaultPcodeDebuggerMemoryAccess(tool, target, platform, snap, viewport);
return new DefaultPcodeDebuggerMemoryAccess(provider, target, platform, snap, viewport);
}
@Override
protected DefaultPcodeDebuggerRegistersAccess newDataForLocalState(TraceThread thread,
int frame) {
return new DefaultPcodeDebuggerRegistersAccess(tool, target, platform, snap, thread, frame,
viewport);
return new DefaultPcodeDebuggerRegistersAccess(provider, target, platform, snap, thread,
frame, viewport);
}
}

View File

@ -23,7 +23,7 @@ import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.debug.api.emulation.PcodeDebuggerMemoryAccess;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.generic.util.datastruct.SemisparseByteArray;
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceMemoryAccess;
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
@ -42,22 +42,22 @@ import ghidra.util.task.TaskMonitor;
public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAccess
implements PcodeDebuggerMemoryAccess, InternalPcodeDebuggerDataAccess {
protected final PluginTool tool;
protected final ServiceProvider provider;
protected final Target target;
/**
* Construct a shim
*
* @param tool the tool controlling the session
* @param provider the service provider (usually the tool)
* @param target the target
* @param platform the associated platform, having the same trace as the recorder
* @param snap the associated snap
* @param viewport the viewport, set to the same snapshot
*/
protected DefaultPcodeDebuggerMemoryAccess(PluginTool tool, Target target,
protected DefaultPcodeDebuggerMemoryAccess(ServiceProvider provider, Target target,
TracePlatform platform, long snap, TraceTimeViewport viewport) {
super(platform, snap, viewport);
this.tool = Objects.requireNonNull(tool);
this.provider = Objects.requireNonNull(provider);
this.target = target;
}
@ -67,8 +67,8 @@ public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAcc
}
@Override
public PluginTool getTool() {
return tool;
public ServiceProvider getServiceProvider() {
return provider;
}
@Override
@ -97,7 +97,7 @@ public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAcc
public boolean readFromStaticImages(SemisparseByteArray bytes, AddressSetView guestView) {
// TODO: Expand to block? DON'T OVERWRITE KNOWN!
DebuggerStaticMappingService mappingService =
tool.getService(DebuggerStaticMappingService.class);
provider.getService(DebuggerStaticMappingService.class);
if (mappingService == null) {
return false;
}

View File

@ -53,7 +53,7 @@ public class DefaultPcodeDebuggerPropertyAccess<T>
@Override
protected T whenNull(Address hostAddress) {
DebuggerStaticMappingService mappingService =
data.getTool().getService(DebuggerStaticMappingService.class);
data.getServiceProvider().getService(DebuggerStaticMappingService.class);
if (mappingService == null) {
return super.whenNull(hostAddress);
}

View File

@ -19,7 +19,7 @@ import java.util.concurrent.CompletableFuture;
import ghidra.debug.api.emulation.PcodeDebuggerRegistersAccess;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceRegistersAccess;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
@ -33,13 +33,13 @@ import ghidra.trace.model.thread.TraceThread;
public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegistersAccess
implements PcodeDebuggerRegistersAccess, InternalPcodeDebuggerDataAccess {
protected final PluginTool tool;
protected final ServiceProvider provider;
protected final Target target;
/**
* Construct a shim
*
* @param tool the tool controlling the session
* @param provider the service provider (usually the tool)
* @param target the target
* @param platform the associated platform, having the same trace as the recorder
* @param snap the associated snap
@ -47,11 +47,11 @@ public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegist
* @param frame the associated frame, or 0 if not applicable
* @param viewport the viewport, set to the same snapshot
*/
protected DefaultPcodeDebuggerRegistersAccess(PluginTool tool, Target target,
protected DefaultPcodeDebuggerRegistersAccess(ServiceProvider provider, Target target,
TracePlatform platform, long snap, TraceThread thread, int frame,
TraceTimeViewport viewport) {
super(platform, snap, thread, frame, viewport);
this.tool = tool;
this.provider = provider;
this.target = target;
}
@ -61,8 +61,8 @@ public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegist
}
@Override
public PluginTool getTool() {
return tool;
public ServiceProvider getServiceProvider() {
return provider;
}
@Override

View File

@ -16,14 +16,14 @@
package ghidra.app.plugin.core.debug.service.emulation.data;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.lifecycle.Internal;
import ghidra.pcode.exec.trace.data.InternalPcodeTraceDataAccess;
import ghidra.trace.model.TraceTimeViewport;
@Internal
public interface InternalPcodeDebuggerDataAccess extends InternalPcodeTraceDataAccess {
PluginTool getTool();
ServiceProvider getServiceProvider();
Target getTarget();

View File

@ -20,18 +20,21 @@ import java.util.*;
import java.util.stream.Collectors;
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.modules.MapEntry;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.ProjectData;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Library;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.ExternalManager;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.*;
import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.ComparatorMath;
import ghidra.util.Msg;
@ -301,4 +304,55 @@ public enum DebuggerStaticMappingUtils {
}
return names.stream().collect(Collectors.joining(","));
}
public static Function getFunction(Address pc, DebuggerCoordinates coordinates,
ServiceProvider serviceProvider) {
if (pc == null) {
return null;
}
DebuggerStaticMappingService mappingService =
serviceProvider.getService(DebuggerStaticMappingService.class);
if (mappingService == null) {
return null;
}
TraceThread curThread = coordinates.getThread();
if (curThread == null) {
return null;
}
TraceLocation dloc = new DefaultTraceLocation(curThread.getTrace(),
curThread, Lifespan.at(coordinates.getSnap()), pc);
ProgramLocation sloc = mappingService.getOpenMappedLocation(dloc);
if (sloc == null) {
return null;
}
return sloc.getProgram().getFunctionManager().getFunctionContaining(sloc.getAddress());
}
public static String computeModuleShortName(String path) {
int sep = path.lastIndexOf('\\');
if (sep > 0 && sep < path.length()) {
path = path.substring(sep + 1);
}
sep = path.lastIndexOf('/');
if (sep > 0 && sep < path.length()) {
path = path.substring(sep + 1);
}
return path;
}
public static String getModuleName(Address pc, DebuggerCoordinates coordinates) {
if (pc == null) {
return null;
}
Trace trace = coordinates.getTrace();
if (trace == null) {
return null;
}
for (TraceModule module : trace.getModuleManager()
.getModulesAt(coordinates.getSnap(), pc)) {
// Just take the first
return computeModuleShortName(module.getName());
}
return null;
}
}

View File

@ -26,7 +26,7 @@ import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.pcode.emu.ThreadPcodeExecutorState;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
@ -73,12 +73,12 @@ public enum DebuggerPcodeUtils {
/**
* Construct a parser bound to the given coordinates
*
* @param tool the tool for the mapping service
* @param provider the service provider (usually the tool)
* @param coordinates the current coordinates for context
*/
public LabelBoundPcodeParser(PluginTool tool, DebuggerCoordinates coordinates) {
public LabelBoundPcodeParser(ServiceProvider provider, DebuggerCoordinates coordinates) {
super((SleighLanguage) coordinates.getPlatform().getLanguage());
this.mappings = tool.getService(DebuggerStaticMappingService.class);
this.mappings = provider.getService(DebuggerStaticMappingService.class);
this.coordinates = coordinates;
}
@ -194,15 +194,17 @@ public enum DebuggerPcodeUtils {
* substituted for their offsets. If a label moves, the program should be recompiled in order to
* update those substitutions.
*
* @param tool the tool for context
* @param provider the service provider (usually the tool)
* @param coordinates the coordinates for the trace (and programs) from which labels can be
* resolved
* @see SleighProgramCompiler#compileProgram(PcodeParser, SleighLanguage, String, String,
* PcodeUseropLibrary)
*/
public static PcodeProgram compileProgram(PluginTool tool, DebuggerCoordinates coordinates,
public static PcodeProgram compileProgram(ServiceProvider provider,
DebuggerCoordinates coordinates,
String sourceName, String source, PcodeUseropLibrary<?> library) {
return SleighProgramCompiler.compileProgram(new LabelBoundPcodeParser(tool, coordinates),
return SleighProgramCompiler.compileProgram(
new LabelBoundPcodeParser(provider, coordinates),
(SleighLanguage) coordinates.getPlatform().getLanguage(), sourceName, source, library);
}
@ -211,13 +213,14 @@ public enum DebuggerPcodeUtils {
*
* <p>
* This has the same limitations as
* {@link #compileProgram(PluginTool, DebuggerCoordinates, String, String, PcodeUseropLibrary)}
* {@link #compileProgram(ServiceProvider, DebuggerCoordinates, String, String, PcodeUseropLibrary)}
*
* @see SleighProgramCompiler#compileExpression(PcodeParser, SleighLanguage, String)
*/
public static PcodeExpression compileExpression(PluginTool tool,
public static PcodeExpression compileExpression(ServiceProvider provider,
DebuggerCoordinates coordinates, String source) {
return SleighProgramCompiler.compileExpression(new LabelBoundPcodeParser(tool, coordinates),
return SleighProgramCompiler.compileExpression(
new LabelBoundPcodeParser(provider, coordinates),
(SleighLanguage) coordinates.getPlatform().getLanguage(), source);
}
@ -228,11 +231,11 @@ public enum DebuggerPcodeUtils {
* If a thread is included, the executor state will have access to both the memory and registers
* in the context of that thread. Otherwise, only memory access is permitted.
*
* @param tool the plugin tool
* @param provider the service provider (usually the tool)
* @param coordinates the coordinates
* @return the state
*/
public static PcodeExecutorState<byte[]> executorStateForCoordinates(PluginTool tool,
public static PcodeExecutorState<byte[]> executorStateForCoordinates(ServiceProvider provider,
DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
if (trace == null) {
@ -244,7 +247,7 @@ public enum DebuggerPcodeUtils {
throw new IllegalArgumentException(
"Given trace or platform does not use a Sleigh language");
}
DefaultPcodeDebuggerAccess access = new DefaultPcodeDebuggerAccess(tool,
DefaultPcodeDebuggerAccess access = new DefaultPcodeDebuggerAccess(provider,
coordinates.getTarget(), platform, coordinates.getViewSnap());
PcodeExecutorState<byte[]> shared =
new RWTargetMemoryPcodeExecutorState(access.getDataForSharedState(), Mode.RW);
@ -270,13 +273,13 @@ public enum DebuggerPcodeUtils {
* If a thread is included, the executor will have access to both the memory and registers in
* the context of that thread. Otherwise, only memory access is permitted.
*
* @param tool the plugin tool. TODO: This shouldn't be required
* @param provider the service provider (usually the tool)
* @param coordinates the coordinates
* @return the executor
*/
public static PcodeExecutor<byte[]> executorForCoordinates(PluginTool tool,
public static PcodeExecutor<byte[]> executorForCoordinates(ServiceProvider provider,
DebuggerCoordinates coordinates) {
PcodeExecutorState<byte[]> state = executorStateForCoordinates(tool, coordinates);
PcodeExecutorState<byte[]> state = executorStateForCoordinates(provider, coordinates);
SleighLanguage slang = (SleighLanguage) state.getLanguage();
return new PcodeExecutor<>(slang, BytesPcodeArithmetic.forLanguage(slang), state,
@ -695,12 +698,12 @@ public enum DebuggerPcodeUtils {
}
}
public static WatchValuePcodeExecutorState buildWatchState(PluginTool tool,
public static WatchValuePcodeExecutorState buildWatchState(ServiceProvider provider,
DebuggerCoordinates coordinates) {
PcodeTraceDataAccess data = new DefaultPcodeTraceAccess(coordinates.getPlatform(),
coordinates.getViewSnap(), coordinates.getSnap())
.getDataForThreadState(coordinates.getThread(), coordinates.getFrame());
PcodeExecutorState<byte[]> bytesState = executorStateForCoordinates(tool, coordinates);
PcodeExecutorState<byte[]> bytesState = executorStateForCoordinates(provider, coordinates);
return new WatchValuePcodeExecutorState(new WatchValuePcodeExecutorStatePiece(
bytesState,
new TraceMemoryStatePcodeExecutorStatePiece(data),
@ -717,18 +720,18 @@ public enum DebuggerPcodeUtils {
* machine state, if applicable. Use the executor in a background thread to avoid locking the
* GUI.
*
* @param tool this plugin tool
* @param provider the service provider (usually the tool)
* @param coordinates the coordinates providing context for the evaluation
* @return an executor for evaluating the watch
*/
public static PcodeExecutor<WatchValue> buildWatchExecutor(PluginTool tool,
public static PcodeExecutor<WatchValue> buildWatchExecutor(ServiceProvider provider,
DebuggerCoordinates coordinates) {
TracePlatform platform = coordinates.getPlatform();
Language language = platform.getLanguage();
if (!(language instanceof SleighLanguage slang)) {
throw new IllegalArgumentException("Watch expressions require a Sleigh language");
}
WatchValuePcodeExecutorState state = buildWatchState(tool, coordinates);
WatchValuePcodeExecutorState state = buildWatchState(provider, coordinates);
return new PcodeExecutor<>(slang, state.getArithmetic(), state, Reason.INSPECT);
}
}

View File

@ -100,6 +100,15 @@ public class DefaultEnumeratedColumnTableModel<C extends Enum<C> & EnumeratedTab
return true;
}
/**
* Check if this column should be visible by default
*
* @return true if visible
*/
default public boolean isVisible() {
return true;
}
/**
* Get the default sort direction for this column
*
@ -217,10 +226,15 @@ public class DefaultEnumeratedColumnTableModel<C extends Enum<C> & EnumeratedTab
if (cols != null) { // Smells
List<C> defaultOrder = defaultSortOrder();
for (C col : cols) {
descriptor.addVisibleColumn(
new EnumeratedDynamicTableColumn<R>(col),
defaultOrder.indexOf(col), // -1 means not found, not sorted
col.defaultSortDirection().isAscending());
EnumeratedDynamicTableColumn<R> ecol = new EnumeratedDynamicTableColumn<R>(col);
if (col.isVisible()) {
descriptor.addVisibleColumn(ecol,
defaultOrder.indexOf(col), // -1 means not found, not sorted
col.defaultSortDirection().isAscending());
}
else {
descriptor.addHiddenColumn(ecol);
}
}
}
return descriptor;