mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-12-16 03:36:21 +08:00
GP-5264: Add PC, Function, and Module columns to the Time panel
This commit is contained in:
@@ -38,13 +38,21 @@
|
||||
<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>
|
||||
|
||||
<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
|
||||
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
|
||||
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
|
||||
|
||||
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) {
|
||||
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)
|
||||
.getAddress(value.getUnsignedValue().longValue(), true));
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public enum SPLocationTrackingSpec implements RegisterLocationTrackingSpec {
|
||||
|
||||
@Override
|
||||
public AddressSpace computeDefaultAddressSpace(DebuggerCoordinates coordinates) {
|
||||
return coordinates.getTrace().getBaseLanguage().getDefaultDataSpace();
|
||||
return coordinates.getPlatform().getCompilerSpec().getStackBaseSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,6 +31,7 @@ import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.model.DomainObjectEvent;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceDomainObjectListener;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
@@ -50,8 +51,11 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
||||
implements EnumeratedTableColumn<SnapshotTableColumns, SnapshotRow> {
|
||||
SNAP("Snap", Long.class, SnapshotRow::getSnap, false),
|
||||
TIME("Time", TraceSchedule.class, SnapshotRow::getTime, true),
|
||||
TIMESTAMP("Timestamp", Date.class, SnapshotRow::getTimeStamp, 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),
|
||||
DESCRIPTION("Description", String.class, SnapshotRow::getDescription, //
|
||||
SnapshotRow::setDescription, true);
|
||||
@@ -140,7 +144,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
||||
if (snapshot.getKey() < 0 && hideScratch) {
|
||||
return;
|
||||
}
|
||||
SnapshotRow row = new SnapshotRow(snapshot);
|
||||
SnapshotRow row = new SnapshotRow(snapshot, tool);
|
||||
snapshotTableModel.add(row);
|
||||
}
|
||||
|
||||
@@ -175,7 +179,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
||||
@Override
|
||||
protected String formatNumber(Number value, Settings settings) {
|
||||
return switch (value) {
|
||||
case null -> "<null>";
|
||||
case null -> "";
|
||||
// SNAP is the only column with Long type
|
||||
case Long snap -> getTimeRadix().format(snap);
|
||||
default -> super.formatNumber(value, settings);
|
||||
@@ -185,7 +189,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
||||
@Override
|
||||
protected String getText(Object value) {
|
||||
return switch (value) {
|
||||
case null -> "<null>";
|
||||
case null -> "";
|
||||
case Date date -> DateUtils.formatDateTimestamp(date);
|
||||
case TraceSchedule schedule -> schedule.toString(getTimeRadix());
|
||||
default -> value.toString();
|
||||
@@ -195,7 +199,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
||||
@Override
|
||||
public String getFilterString(Object t, Settings settings) {
|
||||
return switch (t) {
|
||||
case null -> "<null>";
|
||||
case null -> "";
|
||||
// SNAP is the only column with Long type
|
||||
case Long snap -> getTimeRadix().format(snap);
|
||||
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 GTable snapshotTable;
|
||||
protected final GhidraTableFilterPanel<SnapshotRow> snapshotFilterPanel;
|
||||
@@ -233,6 +238,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
||||
|
||||
public DebuggerSnapshotTablePanel(PluginTool tool) {
|
||||
super(new BorderLayout());
|
||||
this.tool = tool;
|
||||
snapshotTableModel = new SnapshotTableModel(tool);
|
||||
snapshotTable = new GTable(snapshotTableModel);
|
||||
snapshotTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
@@ -243,22 +249,31 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
||||
|
||||
TableColumnModel columnModel = snapshotTable.getColumnModel();
|
||||
TableColumn snapCol = columnModel.getColumn(SnapshotTableColumns.SNAP.ordinal());
|
||||
snapCol.setPreferredWidth(40);
|
||||
snapCol.setPreferredWidth(20);
|
||||
snapCol.setCellRenderer(styleCurrentRenderer);
|
||||
TableColumn timeCol = columnModel.getColumn(SnapshotTableColumns.TIME.ordinal());
|
||||
timeCol.setPreferredWidth(40);
|
||||
timeCol.setPreferredWidth(20);
|
||||
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());
|
||||
timeStampCol.setPreferredWidth(200);
|
||||
timeStampCol.setCellRenderer(styleCurrentRenderer);
|
||||
TableColumn etCol = columnModel.getColumn(SnapshotTableColumns.EVENT_THREAD.ordinal());
|
||||
etCol.setPreferredWidth(40);
|
||||
etCol.setCellRenderer(styleCurrentRenderer);
|
||||
TableColumn schdCol = columnModel.getColumn(SnapshotTableColumns.SCHEDULE.ordinal());
|
||||
schdCol.setPreferredWidth(60);
|
||||
schdCol.setCellRenderer(styleCurrentRenderer);
|
||||
TableColumn descCol = columnModel.getColumn(SnapshotTableColumns.DESCRIPTION.ordinal());
|
||||
descCol.setPreferredWidth(200);
|
||||
descCol.setPreferredWidth(20);
|
||||
descCol.setCellRenderer(styleCurrentRenderer);
|
||||
}
|
||||
|
||||
@@ -319,7 +334,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
||||
for (TraceSnapshot snapshot : hideScratch
|
||||
? manager.getSnapshots(0, true, Long.MAX_VALUE, true)
|
||||
: manager.getAllSnapshots()) {
|
||||
SnapshotRow row = new SnapshotRow(snapshot);
|
||||
SnapshotRow row = new SnapshotRow(snapshot, tool);
|
||||
toAdd.add(row);
|
||||
if (current != DebuggerCoordinates.NOWHERE &&
|
||||
snapshot.getKey() == current.getViewSnap()) {
|
||||
@@ -340,7 +355,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
||||
Collection<? extends TraceSnapshot> sratch =
|
||||
manager.getSnapshots(Long.MIN_VALUE, true, 0, false);
|
||||
snapshotTableModel.addAll(sratch.stream()
|
||||
.map(s -> new SnapshotRow(s))
|
||||
.map(s -> new SnapshotRow(s, tool))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,17 +18,32 @@ package ghidra.app.plugin.core.debug.gui.time;
|
||||
import java.util.Date;
|
||||
|
||||
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.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.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
|
||||
public class SnapshotRow {
|
||||
private final TraceSnapshot snapshot;
|
||||
private final ServiceProvider serviceProvider;
|
||||
|
||||
private final Trace trace;
|
||||
|
||||
public SnapshotRow(TraceSnapshot snapshot) {
|
||||
public SnapshotRow(TraceSnapshot snapshot, ServiceProvider serviceProvider) {
|
||||
this.snapshot = snapshot;
|
||||
this.serviceProvider = serviceProvider;
|
||||
this.trace = snapshot.getTrace();
|
||||
}
|
||||
|
||||
@@ -52,9 +67,127 @@ public class SnapshotRow {
|
||||
return new Date(snapshot.getRealTime());
|
||||
}
|
||||
|
||||
public String getEventThreadName() {
|
||||
private Address getProgramCounterByStack() {
|
||||
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() {
|
||||
|
||||
@@ -316,6 +316,11 @@ public enum DebuggerStaticMappingUtils {
|
||||
|
||||
public static Function getFunction(Address pc, DebuggerCoordinates coordinates,
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
@@ -324,8 +329,7 @@ public enum DebuggerStaticMappingUtils {
|
||||
if (mappingService == null) {
|
||||
return null;
|
||||
}
|
||||
TraceLocation dloc = new DefaultTraceLocation(coordinates.getTrace(),
|
||||
null, Lifespan.at(coordinates.getSnap()), pc);
|
||||
TraceLocation dloc = new DefaultTraceLocation(trace, null, Lifespan.at(snap), pc);
|
||||
ProgramLocation sloc = mappingService.getOpenMappedLocation(dloc);
|
||||
if (sloc == null) {
|
||||
return null;
|
||||
@@ -346,14 +350,13 @@ public enum DebuggerStaticMappingUtils {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Trace trace = coordinates.getTrace();
|
||||
if (trace == null) {
|
||||
return null;
|
||||
}
|
||||
long snap = coordinates.getSnap();
|
||||
for (TraceModule module : trace.getModuleManager().getModulesAt(snap, pc)) {
|
||||
// Just take the first
|
||||
return computeModuleShortName(module.getName(snap));
|
||||
|
||||
@@ -15,28 +15,84 @@
|
||||
*/
|
||||
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 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.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.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.time.TraceSnapshot;
|
||||
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;
|
||||
|
||||
public class DebuggerTimePluginScreenShots extends GhidraScreenShotGenerator {
|
||||
|
||||
private static final TaskMonitor MONITOR = new ConsoleTaskMonitor();
|
||||
|
||||
ProgramManager programManager;
|
||||
DebuggerTraceManagerService traceManager;
|
||||
DebuggerStaticMappingService mappingService;
|
||||
|
||||
DebuggerTimePlugin timePlugin;
|
||||
DebuggerTimeProvider timeProvider;
|
||||
|
||||
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
|
||||
public void setUpMine() throws Throwable {
|
||||
programManager = addPlugin(tool, ProgramManagerPlugin.class);
|
||||
traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
|
||||
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
|
||||
timePlugin = addPlugin(tool, DebuggerTimePlugin.class);
|
||||
timeProvider = waitForComponentProvider(DebuggerTimeProvider.class);
|
||||
|
||||
@@ -46,42 +102,105 @@ public class DebuggerTimePluginScreenShots extends GhidraScreenShotGenerator {
|
||||
@After
|
||||
public void tearDownMine() {
|
||||
tb.close();
|
||||
|
||||
if (progHw != null) {
|
||||
progHw.release(this);
|
||||
progHw = null;
|
||||
}
|
||||
if (progLibc != null) {
|
||||
progLibc.release(this);
|
||||
progLibc = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaptureDebuggerTimePlugin() throws Throwable {
|
||||
progHw = createDefaultProgram("helloworld", ToyProgramBuilder._X64, this);
|
||||
progLibc = createDefaultProgram("libc", ToyProgramBuilder._X64, this);
|
||||
|
||||
long fakeClock = (long) Integer.MAX_VALUE * 1000;
|
||||
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()) {
|
||||
snap = tb.trace.getTimeManager().createSnapshot("Trace started");
|
||||
snap.setRealTime(fakeClock);
|
||||
tb.trace.getObjectManager().createRootObject(ProgramEmulationUtils.EMU_SESSION_SCHEMA);
|
||||
|
||||
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.setRealTime(fakeClock);
|
||||
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.setRealTime(fakeClock);
|
||||
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.setRealTime(fakeClock);
|
||||
snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1"));
|
||||
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.setRealTime(fakeClock);
|
||||
snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1"));
|
||||
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.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
|
||||
(covered later in this course), this column may display the
|
||||
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
|
||||
caused the target to break. This only applies to snapshots that were
|
||||
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
|
||||
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
|
||||
|
||||
@@ -125,9 +125,11 @@ The columns are:
|
||||
* The **Time** column numbers each snapshot.
|
||||
Other windows that indicate life spans refer to these numbers.
|
||||
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.
|
||||
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.
|
||||
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 |
Reference in New Issue
Block a user