mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 13:44:50 +08:00
Merge remote-tracking branch 'origin/GP-5264_Dan_pcModFuncColumnsInTime--SQUASHED'
This commit is contained in:
+12
-4
@@ -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
|
||||||
|
|||||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 16 KiB |
+6
-2
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
|
|||||||
+28
-13
@@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+136
-3
@@ -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() {
|
||||||
|
|||||||
+11
-8
@@ -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));
|
||||||
|
|||||||
+128
-9
@@ -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 |
Reference in New Issue
Block a user