GP-5264: Add PC, Function, and Module columns to the Time panel

This commit is contained in:
Dan
2025-04-18 15:15:17 +00:00
parent b2829ad6ea
commit 5743b48101
12 changed files with 336 additions and 47 deletions

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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

View File

@@ -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()));
}

View File

@@ -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() {

View File

@@ -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));

View File

@@ -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());

View File

@@ -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

View File

@@ -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