Merge remote-tracking branch 'origin/GP-5264_Dan_pcModFuncColumnsInTime--SQUASHED'

This commit is contained in:
Ryan Kurtz
2025-04-18 11:17:35 -04:00
12 changed files with 336 additions and 47 deletions
@@ -38,13 +38,21 @@
<LI>Time - the "time" coordinate for the snapshot. This is the same as the Snap column for <LI>Time - the "time" coordinate for the snapshot. This is the same as the Snap column for
most snapshots. For <EM>scratch</EM> snapshots, this is the same as the Schedule column.</LI> most snapshots. For <EM>scratch</EM> snapshots, this is the same as the Schedule column.</LI>
<LI>Timestamp - the "wall-clock" time of the event. If the debugger doesn't give an event
time, or the snapshot does not correspond to an event, then it is the snapshot creation
time.</LI>
<LI>Event Thread - the thread that caused the event, if applicable. In the case of thread <LI>Event Thread - the thread that caused the event, if applicable. In the case of thread
creation, this should probably be the spawned thread, not the parent.</LI> creation, this should probably be the spawned thread, not the parent.</LI>
<LI>PC - the address of the instruction to execute next. Different debuggers may have
different subtleties in how the report PC.</LI>
<LI>Module - the name of the module containing the PC.</LI>
<LI>Function - the name of the function containing the PC, if Ghidra has the corresponding
module image imported, analyzed, and mapped.</LI>
<LI>Timestamp - the "wall-clock" time of the event. If the debugger doesn't give an event
time, or the snapshot does not correspond to an event, then it is the snapshot creation time.
<EM>(hidden by default)</EM></LI>
<LI>Schedule - if applicable, a source snap and the stepping schedule which produces this <LI>Schedule - if applicable, a source snap and the stepping schedule which produces this
snapshot. This always applies to <EM>scratch</EM> snapshots produced by emulation, but may snapshot. This always applies to <EM>scratch</EM> snapshots produced by emulation, but may
(rarely) apply to recorded events if the stepping schedule between them is somehow known. See (rarely) apply to recorded events if the stepping schedule between them is somehow known. See
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 16 KiB

@@ -84,8 +84,12 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec, Loca
if (value == null) { if (value == null) {
return null; return null;
} }
// TODO: Action to select the address space /**
// Could use code unit, but that can't specify space, yet, either.... * NOTE: I don't think the user needs a way to select the address space. For PC and SP, the
* tracker provides the best default, i.e., the default (code) space and the compiler's
* physical stack space. For watches, I believe the sleigh syntax allows the user to pick,
* but I can't recall testing that.
*/
return platform.mapGuestToHost(computeDefaultAddressSpace(coordinates) return platform.mapGuestToHost(computeDefaultAddressSpace(coordinates)
.getAddress(value.getUnsignedValue().longValue(), true)); .getAddress(value.getUnsignedValue().longValue(), true));
} }
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -59,7 +59,7 @@ public enum SPLocationTrackingSpec implements RegisterLocationTrackingSpec {
@Override @Override
public AddressSpace computeDefaultAddressSpace(DebuggerCoordinates coordinates) { public AddressSpace computeDefaultAddressSpace(DebuggerCoordinates coordinates) {
return coordinates.getTrace().getBaseLanguage().getDefaultDataSpace(); return coordinates.getPlatform().getCompilerSpec().getStackBaseSpace();
} }
@Override @Override
@@ -31,6 +31,7 @@ import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.docking.settings.Settings; import ghidra.docking.settings.Settings;
import ghidra.framework.model.DomainObjectEvent; import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceDomainObjectListener; import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.target.TraceObjectValue; import ghidra.trace.model.target.TraceObjectValue;
@@ -50,8 +51,11 @@ public class DebuggerSnapshotTablePanel extends JPanel {
implements EnumeratedTableColumn<SnapshotTableColumns, SnapshotRow> { implements EnumeratedTableColumn<SnapshotTableColumns, SnapshotRow> {
SNAP("Snap", Long.class, SnapshotRow::getSnap, false), SNAP("Snap", Long.class, SnapshotRow::getSnap, false),
TIME("Time", TraceSchedule.class, SnapshotRow::getTime, true), TIME("Time", TraceSchedule.class, SnapshotRow::getTime, true),
TIMESTAMP("Timestamp", Date.class, SnapshotRow::getTimeStamp, true),
EVENT_THREAD("Event Thread", String.class, SnapshotRow::getEventThreadName, true), EVENT_THREAD("Event Thread", String.class, SnapshotRow::getEventThreadName, true),
PC("PC", Address.class, SnapshotRow::getProgramCounter, true),
MODULE("Module", String.class, SnapshotRow::getModuleName, true),
FUNCTION("Function", ghidra.program.model.listing.Function.class, SnapshotRow::getFunction, true),
TIMESTAMP("Timestamp", Date.class, SnapshotRow::getTimeStamp, false),
SCHEDULE("Schedule", TraceSchedule.class, SnapshotRow::getSchedule, false), SCHEDULE("Schedule", TraceSchedule.class, SnapshotRow::getSchedule, false),
DESCRIPTION("Description", String.class, SnapshotRow::getDescription, // DESCRIPTION("Description", String.class, SnapshotRow::getDescription, //
SnapshotRow::setDescription, true); SnapshotRow::setDescription, true);
@@ -140,7 +144,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
if (snapshot.getKey() < 0 && hideScratch) { if (snapshot.getKey() < 0 && hideScratch) {
return; return;
} }
SnapshotRow row = new SnapshotRow(snapshot); SnapshotRow row = new SnapshotRow(snapshot, tool);
snapshotTableModel.add(row); snapshotTableModel.add(row);
} }
@@ -175,7 +179,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
@Override @Override
protected String formatNumber(Number value, Settings settings) { protected String formatNumber(Number value, Settings settings) {
return switch (value) { return switch (value) {
case null -> "<null>"; case null -> "";
// SNAP is the only column with Long type // SNAP is the only column with Long type
case Long snap -> getTimeRadix().format(snap); case Long snap -> getTimeRadix().format(snap);
default -> super.formatNumber(value, settings); default -> super.formatNumber(value, settings);
@@ -185,7 +189,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
@Override @Override
protected String getText(Object value) { protected String getText(Object value) {
return switch (value) { return switch (value) {
case null -> "<null>"; case null -> "";
case Date date -> DateUtils.formatDateTimestamp(date); case Date date -> DateUtils.formatDateTimestamp(date);
case TraceSchedule schedule -> schedule.toString(getTimeRadix()); case TraceSchedule schedule -> schedule.toString(getTimeRadix());
default -> value.toString(); default -> value.toString();
@@ -195,7 +199,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
@Override @Override
public String getFilterString(Object t, Settings settings) { public String getFilterString(Object t, Settings settings) {
return switch (t) { return switch (t) {
case null -> "<null>"; case null -> "";
// SNAP is the only column with Long type // SNAP is the only column with Long type
case Long snap -> getTimeRadix().format(snap); case Long snap -> getTimeRadix().format(snap);
case Number n -> formatNumber(n, settings); case Number n -> formatNumber(n, settings);
@@ -221,6 +225,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
} }
}; };
protected final PluginTool tool;
protected final SnapshotTableModel snapshotTableModel; protected final SnapshotTableModel snapshotTableModel;
protected final GTable snapshotTable; protected final GTable snapshotTable;
protected final GhidraTableFilterPanel<SnapshotRow> snapshotFilterPanel; protected final GhidraTableFilterPanel<SnapshotRow> snapshotFilterPanel;
@@ -233,6 +238,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
public DebuggerSnapshotTablePanel(PluginTool tool) { public DebuggerSnapshotTablePanel(PluginTool tool) {
super(new BorderLayout()); super(new BorderLayout());
this.tool = tool;
snapshotTableModel = new SnapshotTableModel(tool); snapshotTableModel = new SnapshotTableModel(tool);
snapshotTable = new GTable(snapshotTableModel); snapshotTable = new GTable(snapshotTableModel);
snapshotTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); snapshotTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@@ -243,22 +249,31 @@ public class DebuggerSnapshotTablePanel extends JPanel {
TableColumnModel columnModel = snapshotTable.getColumnModel(); TableColumnModel columnModel = snapshotTable.getColumnModel();
TableColumn snapCol = columnModel.getColumn(SnapshotTableColumns.SNAP.ordinal()); TableColumn snapCol = columnModel.getColumn(SnapshotTableColumns.SNAP.ordinal());
snapCol.setPreferredWidth(40); snapCol.setPreferredWidth(20);
snapCol.setCellRenderer(styleCurrentRenderer); snapCol.setCellRenderer(styleCurrentRenderer);
TableColumn timeCol = columnModel.getColumn(SnapshotTableColumns.TIME.ordinal()); TableColumn timeCol = columnModel.getColumn(SnapshotTableColumns.TIME.ordinal());
timeCol.setPreferredWidth(40); timeCol.setPreferredWidth(20);
timeCol.setCellRenderer(styleCurrentRenderer); timeCol.setCellRenderer(styleCurrentRenderer);
TableColumn etCol = columnModel.getColumn(SnapshotTableColumns.EVENT_THREAD.ordinal());
etCol.setPreferredWidth(20);
etCol.setCellRenderer(styleCurrentRenderer);
TableColumn pcCol = columnModel.getColumn(SnapshotTableColumns.PC.ordinal());
pcCol.setPreferredWidth(40);
pcCol.setCellRenderer(styleCurrentRenderer);
TableColumn moduleCol = columnModel.getColumn(SnapshotTableColumns.MODULE.ordinal());
moduleCol.setPreferredWidth(40);
moduleCol.setCellRenderer(styleCurrentRenderer);
TableColumn functionCol = columnModel.getColumn(SnapshotTableColumns.FUNCTION.ordinal());
functionCol.setPreferredWidth(40);
functionCol.setCellRenderer(styleCurrentRenderer);
TableColumn timeStampCol = columnModel.getColumn(SnapshotTableColumns.TIMESTAMP.ordinal()); TableColumn timeStampCol = columnModel.getColumn(SnapshotTableColumns.TIMESTAMP.ordinal());
timeStampCol.setPreferredWidth(200); timeStampCol.setPreferredWidth(200);
timeStampCol.setCellRenderer(styleCurrentRenderer); timeStampCol.setCellRenderer(styleCurrentRenderer);
TableColumn etCol = columnModel.getColumn(SnapshotTableColumns.EVENT_THREAD.ordinal());
etCol.setPreferredWidth(40);
etCol.setCellRenderer(styleCurrentRenderer);
TableColumn schdCol = columnModel.getColumn(SnapshotTableColumns.SCHEDULE.ordinal()); TableColumn schdCol = columnModel.getColumn(SnapshotTableColumns.SCHEDULE.ordinal());
schdCol.setPreferredWidth(60); schdCol.setPreferredWidth(60);
schdCol.setCellRenderer(styleCurrentRenderer); schdCol.setCellRenderer(styleCurrentRenderer);
TableColumn descCol = columnModel.getColumn(SnapshotTableColumns.DESCRIPTION.ordinal()); TableColumn descCol = columnModel.getColumn(SnapshotTableColumns.DESCRIPTION.ordinal());
descCol.setPreferredWidth(200); descCol.setPreferredWidth(20);
descCol.setCellRenderer(styleCurrentRenderer); descCol.setCellRenderer(styleCurrentRenderer);
} }
@@ -319,7 +334,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
for (TraceSnapshot snapshot : hideScratch for (TraceSnapshot snapshot : hideScratch
? manager.getSnapshots(0, true, Long.MAX_VALUE, true) ? manager.getSnapshots(0, true, Long.MAX_VALUE, true)
: manager.getAllSnapshots()) { : manager.getAllSnapshots()) {
SnapshotRow row = new SnapshotRow(snapshot); SnapshotRow row = new SnapshotRow(snapshot, tool);
toAdd.add(row); toAdd.add(row);
if (current != DebuggerCoordinates.NOWHERE && if (current != DebuggerCoordinates.NOWHERE &&
snapshot.getKey() == current.getViewSnap()) { snapshot.getKey() == current.getViewSnap()) {
@@ -340,7 +355,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
Collection<? extends TraceSnapshot> sratch = Collection<? extends TraceSnapshot> sratch =
manager.getSnapshots(Long.MIN_VALUE, true, 0, false); manager.getSnapshots(Long.MIN_VALUE, true, 0, false);
snapshotTableModel.addAll(sratch.stream() snapshotTableModel.addAll(sratch.stream()
.map(s -> new SnapshotRow(s)) .map(s -> new SnapshotRow(s, tool))
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
@@ -18,17 +18,32 @@ package ghidra.app.plugin.core.debug.gui.time;
import java.util.Date; import java.util.Date;
import db.Transaction; import db.Transaction;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Function;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.*;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.trace.model.time.schedule.TraceSchedule;
public class SnapshotRow { public class SnapshotRow {
private final TraceSnapshot snapshot; private final TraceSnapshot snapshot;
private final ServiceProvider serviceProvider;
private final Trace trace; private final Trace trace;
public SnapshotRow(TraceSnapshot snapshot) { public SnapshotRow(TraceSnapshot snapshot, ServiceProvider serviceProvider) {
this.snapshot = snapshot; this.snapshot = snapshot;
this.serviceProvider = serviceProvider;
this.trace = snapshot.getTrace(); this.trace = snapshot.getTrace();
} }
@@ -52,9 +67,127 @@ public class SnapshotRow {
return new Date(snapshot.getRealTime()); return new Date(snapshot.getRealTime());
} }
public String getEventThreadName() { private Address getProgramCounterByStack() {
TraceThread thread = snapshot.getEventThread(); TraceThread thread = snapshot.getEventThread();
return thread == null ? "" : thread.getName(snapshot.getKey()); if (thread == null) {
return null;
}
long snap = getTime().getSnap();
TraceStack stack;
try {
stack = trace.getStackManager().getLatestStack(thread, snap);
}
catch (IllegalStateException e) {
// Schema does not specify a stack
return null;
}
if (stack == null) {
return null;
}
TraceStackFrame frame = stack.getFrame(snap, 0, false);
if (frame == null) {
return null;
}
return frame.getProgramCounter(snap);
}
private Address getProgramCounterByRegister() {
TraceThread thread = getEventThread();
if (thread == null) {
return null;
}
long viewSnap = snapshot.getKey();
long snap = getTime().getSnap();
/**
* LATER: Some notion of an event platform? Or perhaps the thread has some attribute to
* indicate which platform is active?
*
* I could use the tool's "current" platform, but that may produce odd behavior when
* changing platforms. Each would have the most recent PC for the selected platform, which
* is totally irrelevant. For now, seek out the platform with the most recent update to its
* PC for the event thread. While this should be perfectly accurate, it's a bit expensive.
*/
record MostRecentValue(TracePlatform platform, long snap, RegisterValue value) {
static MostRecentValue choose(MostRecentValue a, MostRecentValue b) {
if (a == null) {
return b;
}
if (b == null) {
return a;
}
// Prefer negative ("view") snaps to positive. Of that, pick most recent.
if (Long.compareUnsigned(a.snap, b.snap) > 0) {
return a;
}
return b;
}
static MostRecentValue get(TracePlatform platform, TraceThread thread, long viewSnap,
long snap) {
Register reg = platform.getLanguage().getProgramCounter();
TraceMemoryManager mm = thread.getTrace().getMemoryManager();
TraceMemorySpace regs = reg.getAddressSpace().isRegisterSpace()
? mm.getMemoryRegisterSpace(thread, false)
: mm.getMemorySpace(reg.getAddressSpace(), false);
if (regs == null) {
return null;
}
if (regs.getState(platform, viewSnap, reg) == TraceMemoryState.KNOWN) {
RegisterValue value = regs.getValue(platform, viewSnap, reg);
return value == null ? null : new MostRecentValue(platform, viewSnap, value);
}
RegisterValue value = regs.getValue(platform, snap, reg);
return value == null ? null : new MostRecentValue(platform, snap, value);
}
Address mapToHost() {
AddressSpace codeSpace = platform.getAddressFactory().getDefaultAddressSpace();
return platform.mapGuestToHost(
codeSpace.getAddress(value.getUnsignedValue().longValue(), true));
}
}
MostRecentValue choice = MostRecentValue.get(trace.getPlatformManager().getHostPlatform(),
thread, viewSnap, snap);
for (TraceGuestPlatform guest : trace.getPlatformManager().getGuestPlatforms()) {
choice =
MostRecentValue.choose(choice, MostRecentValue.get(guest, thread, viewSnap, snap));
}
return choice == null ? null : choice.mapToHost();
}
public Address getProgramCounter() {
Address byStack = getProgramCounterByStack();
return byStack != null ? byStack : getProgramCounterByRegister();
}
public Function getFunction() {
Address pc = getProgramCounter();
if (pc == null) {
return null;
}
return DebuggerStaticMappingUtils.getFunction(pc, trace, getTime().getSnap(),
serviceProvider);
}
public String getModuleName() {
Address pc = getProgramCounter();
if (pc == null) {
return null;
}
return DebuggerStaticMappingUtils.getModuleName(pc, trace, getTime().getSnap());
}
private TraceThread getEventThread() {
TraceThread thread = snapshot.getEventThread();
if (thread != null) {
return thread;
}
return getTime().getEventThread(trace);
}
public String getEventThreadName() {
TraceThread thread = getEventThread();
return thread == null ? "" : thread.getName(getTime().getSnap());
} }
public TraceSchedule getSchedule() { public TraceSchedule getSchedule() {
@@ -316,6 +316,11 @@ public enum DebuggerStaticMappingUtils {
public static Function getFunction(Address pc, DebuggerCoordinates coordinates, public static Function getFunction(Address pc, DebuggerCoordinates coordinates,
ServiceProvider serviceProvider) { ServiceProvider serviceProvider) {
return getFunction(pc, coordinates.getTrace(), coordinates.getSnap(), serviceProvider);
}
public static Function getFunction(Address pc, Trace trace, long snap,
ServiceProvider serviceProvider) {
if (pc == null) { if (pc == null) {
return null; return null;
} }
@@ -324,8 +329,7 @@ public enum DebuggerStaticMappingUtils {
if (mappingService == null) { if (mappingService == null) {
return null; return null;
} }
TraceLocation dloc = new DefaultTraceLocation(coordinates.getTrace(), TraceLocation dloc = new DefaultTraceLocation(trace, null, Lifespan.at(snap), pc);
null, Lifespan.at(coordinates.getSnap()), pc);
ProgramLocation sloc = mappingService.getOpenMappedLocation(dloc); ProgramLocation sloc = mappingService.getOpenMappedLocation(dloc);
if (sloc == null) { if (sloc == null) {
return null; return null;
@@ -346,14 +350,13 @@ public enum DebuggerStaticMappingUtils {
} }
public static String getModuleName(Address pc, DebuggerCoordinates coordinates) { public static String getModuleName(Address pc, DebuggerCoordinates coordinates) {
if (pc == null) { return getModuleName(pc, coordinates.getTrace(), coordinates.getSnap());
}
public static String getModuleName(Address pc, Trace trace, long snap) {
if (pc == null || trace == null) {
return null; return null;
} }
Trace trace = coordinates.getTrace();
if (trace == null) {
return null;
}
long snap = coordinates.getSnap();
for (TraceModule module : trace.getModuleManager().getModulesAt(snap, pc)) { for (TraceModule module : trace.getModuleManager().getModulesAt(snap, pc)) {
// Just take the first // Just take the first
return computeModuleShortName(module.getName(snap)); return computeModuleShortName(module.getName(snap));
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,28 +15,84 @@
*/ */
package ghidra.app.plugin.core.debug.gui.time; package ghidra.app.plugin.core.debug.gui.time;
import java.io.IOException;
import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
import org.junit.*; import org.junit.*;
import db.Transaction; import db.Transaction;
import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin; import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.services.DebuggerTraceManagerService; import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
import ghidra.app.services.*;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.DomainObject;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.util.ProgramLocation;
import ghidra.test.ToyProgramBuilder; import ghidra.test.ToyProgramBuilder;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.model.DefaultTraceLocation;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.ConsoleTaskMonitor;
import ghidra.util.task.TaskMonitor;
import help.screenshot.GhidraScreenShotGenerator; import help.screenshot.GhidraScreenShotGenerator;
public class DebuggerTimePluginScreenShots extends GhidraScreenShotGenerator { public class DebuggerTimePluginScreenShots extends GhidraScreenShotGenerator {
private static final TaskMonitor MONITOR = new ConsoleTaskMonitor();
ProgramManager programManager;
DebuggerTraceManagerService traceManager; DebuggerTraceManagerService traceManager;
DebuggerStaticMappingService mappingService;
DebuggerTimePlugin timePlugin; DebuggerTimePlugin timePlugin;
DebuggerTimeProvider timeProvider; DebuggerTimeProvider timeProvider;
ToyDBTraceBuilder tb; ToyDBTraceBuilder tb;
Program progHw;
Program progLibc;
protected void intoProject(DomainObject obj) {
waitForDomainObject(obj);
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
waitForCondition(() -> {
try {
rootFolder.createFile(obj.getName(), obj, MONITOR);
return true;
}
catch (InvalidNameException | CancelledException e) {
throw new AssertionError(e);
}
catch (IOException e) {
// Usually "object is busy". Try again.
return false;
}
});
}
public static void waitForDomainObject(DomainObject object) {
object.flushEvents();
waitForSwing();
}
@Before @Before
public void setUpMine() throws Throwable { public void setUpMine() throws Throwable {
programManager = addPlugin(tool, ProgramManagerPlugin.class);
traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class); traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
timePlugin = addPlugin(tool, DebuggerTimePlugin.class); timePlugin = addPlugin(tool, DebuggerTimePlugin.class);
timeProvider = waitForComponentProvider(DebuggerTimeProvider.class); timeProvider = waitForComponentProvider(DebuggerTimeProvider.class);
@@ -46,42 +102,105 @@ public class DebuggerTimePluginScreenShots extends GhidraScreenShotGenerator {
@After @After
public void tearDownMine() { public void tearDownMine() {
tb.close(); tb.close();
if (progHw != null) {
progHw.release(this);
progHw = null;
}
if (progLibc != null) {
progLibc.release(this);
progLibc = null;
}
} }
@Test @Test
public void testCaptureDebuggerTimePlugin() throws Throwable { public void testCaptureDebuggerTimePlugin() throws Throwable {
progHw = createDefaultProgram("helloworld", ToyProgramBuilder._X64, this);
progLibc = createDefaultProgram("libc", ToyProgramBuilder._X64, this);
long fakeClock = (long) Integer.MAX_VALUE * 1000; long fakeClock = (long) Integer.MAX_VALUE * 1000;
TraceSnapshot snap; TraceSnapshot snap;
try (Transaction tx = progHw.openTransaction("Populate main")) {
progHw.getMemory()
.createInitializedBlock(".text", tb.addr(0x00400000), 0x2000, (byte) 0, MONITOR,
false);
progHw.getFunctionManager()
.createFunction("main", tb.addr(0x00401234),
tb.set(tb.range(0x00401234, 0x00401300)), SourceType.IMPORTED);
}
try (Transaction tx = progLibc.openTransaction("Populate puts")) {
progLibc.getMemory()
.createInitializedBlock(".text", tb.addr(0x00400000), 0x2000, (byte) 0, MONITOR,
false);
progLibc.getFunctionManager()
.createFunction("puts", tb.addr(0x00400110),
tb.set(tb.range(0x00400110, 0x00400120)), SourceType.IMPORTED);
}
intoProject(progHw);
intoProject(progLibc);
intoProject(tb.trace);
programManager.openProgram(progLibc);
programManager.openProgram(progHw);
traceManager.openTrace(tb.trace);
mappingService.changesSettled().get(1, TimeUnit.SECONDS);
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
snap = tb.trace.getTimeManager().createSnapshot("Trace started"); tb.trace.getObjectManager().createRootObject(ProgramEmulationUtils.EMU_SESSION_SCHEMA);
snap.setRealTime(fakeClock);
TraceThread thread = tb.getOrAddThread("[1]", snap.getKey()); tb.trace.getModuleManager()
.addLoadedModule("Modules[helloword]", "helloworld",
tb.range(0x00400000, 0x00402000), 0);
tb.trace.getModuleManager()
.addLoadedModule("Modules[libc]", "libc",
tb.range(0x7fff0000, 0x7fff2000), 0);
snap = tb.trace.getTimeManager().createSnapshot("Thread STOPPED"); mappingService.addMapping(
new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), tb.addr(0x00400000)),
new ProgramLocation(progHw, tb.addr(0x00400000)), 0x2000, false);
mappingService.addMapping(
new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), tb.addr(0x7fff0000)),
new ProgramLocation(progLibc, tb.addr(0x00400000)), 0x2000, false);
TraceThread thread = tb.getOrAddThread("Threads[1]", 0);
tb.trace.getObjectManager()
.createObject(KeyPath.parse("Threads[1].Registers"))
.insert(Lifespan.nowOn(0), ConflictResolution.DENY);
thread.setName(0, "1 main");
TraceMemorySpace regs =
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
Register pc = tb.host.getLanguage().getProgramCounter();
snap = tb.trace.getTimeManager().createSnapshot("STOP");
snap.setEventThread(thread); snap.setEventThread(thread);
snap.setRealTime(fakeClock); snap.setRealTime(fakeClock);
fakeClock += 1000; fakeClock += 1000;
regs.setValue(snap.getKey(), new RegisterValue(pc, BigInteger.valueOf(0x00401234)));
snap = tb.trace.getTimeManager().createSnapshot("Thread BREAKPOINT_HIT"); snap = tb.trace.getTimeManager().createSnapshot("BREAK");
snap.setEventThread(thread); snap.setEventThread(thread);
snap.setRealTime(fakeClock); snap.setRealTime(fakeClock);
fakeClock += 2300; fakeClock += 2300;
regs.setValue(snap.getKey(), new RegisterValue(pc, BigInteger.valueOf(0x7fff0110)));
snap = tb.trace.getTimeManager().createSnapshot("Thread STEP_COMPLETED"); snap = tb.trace.getTimeManager().createSnapshot("STEP");
snap.setEventThread(thread); snap.setEventThread(thread);
snap.setRealTime(fakeClock); snap.setRealTime(fakeClock);
snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1")); snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1"));
fakeClock += 444; fakeClock += 444;
regs.setValue(snap.getKey(), new RegisterValue(pc, BigInteger.valueOf(0x7fff0113)));
snap = tb.trace.getTimeManager().createSnapshot("Thread STEP_COMPLETED"); snap = tb.trace.getTimeManager().createSnapshot("STEP");
snap.setEventThread(thread); snap.setEventThread(thread);
snap.setRealTime(fakeClock); snap.setRealTime(fakeClock);
snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1")); snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1"));
fakeClock += 100; fakeClock += 100;
regs.setValue(snap.getKey(), new RegisterValue(pc, BigInteger.valueOf(0x7fff0115)));
} }
traceManager.openTrace(tb.trace); mappingService.changesSettled().get(1, TimeUnit.SECONDS);
traceManager.activateTrace(tb.trace); traceManager.activateTrace(tb.trace);
traceManager.activateSnap(snap.getKey()); traceManager.activateSnap(snap.getKey());
@@ -234,11 +234,16 @@ snapshot is at the bottom. The columns are:</p>
windows that indicate life spans refer to these numbers. If emulating windows that indicate life spans refer to these numbers. If emulating
(covered later in this course), this column may display the (covered later in this course), this column may display the
schedule.</li> schedule.</li>
<li>The <strong>Timestamp</strong> column gives the time when the
snapshot was created, i.e., the time when the event occurred.</li>
<li>The <strong>Event Thread</strong> column indicates which thread <li>The <strong>Event Thread</strong> column indicates which thread
caused the target to break. This only applies to snapshots that were caused the target to break. This only applies to snapshots that were
created because of an event, which is most.</li> created because of an event, which is most.</li>
<li>The <strong>PC</strong> column gives the address of the next
instruction.</li>
<li>The <strong>Function</strong> column gives the name of the function
containing the PC mapped to its static program database, if
available.</li>
<li>The <strong>Module</strong> column gives the name of the module
containing the PC.</li>
<li>The <strong>Description</strong> column describes the event that <li>The <strong>Description</strong> column describes the event that
generated the snapshot. This can be edited in the table, or by pressing generated the snapshot. This can be edited in the table, or by pressing
<strong><code>CTRL</code>-<code>SHIFT</code>-<code>N</code></strong> to <strong><code>CTRL</code>-<code>SHIFT</code>-<code>N</code></strong> to
@@ -125,9 +125,11 @@ The columns are:
* The **Time** column numbers each snapshot. * The **Time** column numbers each snapshot.
Other windows that indicate life spans refer to these numbers. Other windows that indicate life spans refer to these numbers.
If emulating (covered later in this course), this column may display the schedule. If emulating (covered later in this course), this column may display the schedule.
* The **Timestamp** column gives the time when the snapshot was created, i.e., the time when the event occurred.
* The **Event Thread** column indicates which thread caused the target to break. * The **Event Thread** column indicates which thread caused the target to break.
This only applies to snapshots that were created because of an event, which is most. This only applies to snapshots that were created because of an event, which is most.
* The **PC** column gives the address of the next instruction.
* The **Function** column gives the name of the function containing the PC mapped to its static program database, if available.
* The **Module** column gives the name of the module containing the PC.
* The **Description** column describes the event that generated the snapshot. * The **Description** column describes the event that generated the snapshot.
This can be edited in the table, or by pressing **`CTRL`-`SHIFT`-`N`** to mark interesting snapshots. This can be edited in the table, or by pressing **`CTRL`-`SHIFT`-`N`** to mark interesting snapshots.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB