GP-2970: Add 'Invalidate Emulator Cache' action.

This commit is contained in:
Dan 2023-02-08 16:40:13 -05:00
parent bb79314d85
commit 46a620f687
13 changed files with 309 additions and 83 deletions

View file

@ -475,65 +475,6 @@ public interface DebuggerResources {
} }
} }
interface EmulateProgramAction {
String NAME = "Emulate Program in new Trace";
String DESCRIPTION = "Emulate the current program in a new trace starting at the cursor";
Icon ICON = ICON_EMULATE;
String GROUP = GROUP_GENERAL;
String HELP_ANCHOR = "emulate_program";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarIcon(ICON)
.toolBarGroup(GROUP)
.menuPath(DebuggerPluginPackage.NAME, NAME)
.menuIcon(ICON)
.menuGroup(GROUP)
.popupMenuPath(NAME)
.popupMenuIcon(ICON)
.popupMenuGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface EmulateAddThreadAction {
String NAME = "Add Emulated Thread to Trace";
String DESCRIPTION = "Add an emulated thread to the current trace starting here";
Icon ICON = ICON_THREAD;
String GROUP = GROUP_GENERAL;
String HELP_ANCHOR = "add_emulated_thread";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuPath(DebuggerPluginPackage.NAME, NAME)
.menuIcon(ICON)
.menuGroup(GROUP)
.popupMenuPath(NAME)
.popupMenuIcon(ICON)
.popupMenuGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface ConfigureEmulatorAction {
String NAME = "Configure Emulator";
String DESCRIPTION = "Choose and configure the current emulator";
String GROUP = GROUP_MAINTENANCE;
String HELP_ANCHOR = "configure_emulator";
static ToggleActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ToggleActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
abstract class AbstractQuickLaunchAction extends DockingAction { abstract class AbstractQuickLaunchAction extends DockingAction {
public static final String NAME = "Quick Launch"; public static final String NAME = "Quick Launch";
public static final Icon ICON = ICON_DEBUGGER; // TODO: A different icon? public static final Icon ICON = ICON_DEBUGGER; // TODO: A different icon?

View file

@ -21,13 +21,17 @@ import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.Icon;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import docking.ActionContext;
import docking.action.DockingAction; import docking.action.DockingAction;
import docking.action.ToggleDockingAction; import docking.action.ToggleDockingAction;
import docking.action.builder.ActionBuilder;
import docking.action.builder.ToggleActionBuilder;
import ghidra.app.context.ProgramLocationActionContext; import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.events.ProgramActivatedPluginEvent; import ghidra.app.events.ProgramActivatedPluginEvent;
import ghidra.app.events.ProgramClosedPluginEvent; import ghidra.app.events.ProgramClosedPluginEvent;
@ -35,13 +39,14 @@ import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerCoordinates; import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent; import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.async.AsyncLazyMap; import ghidra.async.AsyncLazyMap;
import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed; import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.pcode.emu.PcodeMachine.*; import ghidra.pcode.emu.PcodeMachine.AccessKind;
import ghidra.pcode.emu.PcodeMachine.SwiMode;
import ghidra.pcode.exec.InjectionErrorPcodeExecutionException; import ghidra.pcode.exec.InjectionErrorPcodeExecutionException;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@ -54,6 +59,7 @@ import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.*; import ghidra.trace.model.time.schedule.*;
import ghidra.trace.model.time.schedule.Scheduler.RunResult; import ghidra.trace.model.time.schedule.Scheduler.RunResult;
import ghidra.util.HelpLocation;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
@ -83,6 +89,82 @@ import ghidra.util.task.TaskMonitor;
public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEmulationService { public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEmulationService {
protected static final int MAX_CACHE_SIZE = 5; protected static final int MAX_CACHE_SIZE = 5;
interface EmulateProgramAction {
String NAME = "Emulate Program in new Trace";
String DESCRIPTION = "Emulate the current program in a new trace starting at the cursor";
Icon ICON = DebuggerResources.ICON_EMULATE;
String GROUP = DebuggerResources.GROUP_GENERAL;
String HELP_ANCHOR = "emulate_program";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarIcon(ICON)
.toolBarGroup(GROUP)
.menuPath(DebuggerPluginPackage.NAME, NAME)
.menuIcon(ICON)
.menuGroup(GROUP)
.popupMenuPath(NAME)
.popupMenuIcon(ICON)
.popupMenuGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface EmulateAddThreadAction {
String NAME = "Add Emulated Thread to Trace";
String DESCRIPTION = "Add an emulated thread to the current trace starting here";
Icon ICON = DebuggerResources.ICON_THREAD;
String GROUP = DebuggerResources.GROUP_GENERAL;
String HELP_ANCHOR = "add_emulated_thread";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuPath(DebuggerPluginPackage.NAME, NAME)
.menuIcon(ICON)
.menuGroup(GROUP)
.popupMenuPath(NAME)
.popupMenuIcon(ICON)
.popupMenuGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface ConfigureEmulatorAction {
String NAME = "Configure Emulator";
String DESCRIPTION = "Choose and configure the current emulator";
String GROUP = DebuggerResources.GROUP_GENERAL;
String HELP_ANCHOR = "configure_emulator";
static ToggleActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ToggleActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface InvalidateEmulatorCacheAction {
String NAME = "Invalidate Emulator Cache";
String DESCRIPTION =
"Prevent the emulation service from using cached snapshots from the current trace";
String GROUP = DebuggerResources.GROUP_MAINTENANCE;
String HELP_ANCHOR = "invalidate_cache";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuPath(DebuggerPluginPackage.NAME, ConfigureEmulatorAction.NAME, NAME)
.menuGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
protected static class CacheKey implements Comparable<CacheKey> { protected static class CacheKey implements Comparable<CacheKey> {
// TODO: Should key on platform, not trace // TODO: Should key on platform, not trace
protected final Trace trace; protected final Trace trace;
@ -273,6 +355,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
DockingAction actionEmulateProgram; DockingAction actionEmulateProgram;
DockingAction actionEmulateAddThread; DockingAction actionEmulateAddThread;
DockingAction actionInvalidateCache;
Map<Class<? extends DebuggerPcodeEmulatorFactory>, ToggleDockingAction> // Map<Class<? extends DebuggerPcodeEmulatorFactory>, ToggleDockingAction> //
actionsChooseEmulatorFactory = new HashMap<>(); actionsChooseEmulatorFactory = new HashMap<>();
@ -302,6 +385,10 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
.popupWhen(this::emulateAddThreadEnabled) .popupWhen(this::emulateAddThreadEnabled)
.onAction(this::emulateAddThreadActivated) .onAction(this::emulateAddThreadActivated)
.buildAndInstall(tool); .buildAndInstall(tool);
actionInvalidateCache = InvalidateEmulatorCacheAction.builder(this)
.enabledWhen(this::invalidateCacheEnabled)
.onAction(this::invalidateCacheActivated)
.buildAndInstall(tool);
ClassSearcher.addChangeListener(classChangeListener); ClassSearcher.addChangeListener(classChangeListener);
updateConfigureEmulatorStates(); updateConfigureEmulatorStates();
} }
@ -312,7 +399,8 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
private ToggleDockingAction createActionChooseEmulator(DebuggerPcodeEmulatorFactory factory) { private ToggleDockingAction createActionChooseEmulator(DebuggerPcodeEmulatorFactory factory) {
ToggleDockingAction action = ConfigureEmulatorAction.builder(this) ToggleDockingAction action = ConfigureEmulatorAction.builder(this)
.menuPath(DebuggerPluginPackage.NAME, "Configure Emulator", factory.getTitle()) .menuPath(DebuggerPluginPackage.NAME, ConfigureEmulatorAction.NAME,
factory.getTitle())
.onAction(ctx -> configureEmulatorActivated(factory)) .onAction(ctx -> configureEmulatorActivated(factory))
.buildAndInstall(tool); .buildAndInstall(tool);
String[] path = action.getMenuBarData().getMenuPath(); String[] path = action.getMenuBarData().getMenuPath();
@ -460,6 +548,23 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
} }
} }
private boolean invalidateCacheEnabled(ActionContext ignored) {
return traceManager.getCurrentTrace() != null;
}
private void invalidateCacheActivated(ActionContext ignored) {
DebuggerCoordinates current = traceManager.getCurrent();
Trace trace = current.getTrace();
long version = trace.getEmulatorCacheVersion();
try (UndoableTransaction tid =
UndoableTransaction.start(trace, "Invalidate Emulator Cache")) {
trace.setEmulatorCacheVersion(version + 1);
}
// NB. Success should already display on screen, since it's current.
// Failure should be reported by tool's task manager.
traceManager.materialize(current);
}
private void configureEmulatorActivated(DebuggerPcodeEmulatorFactory factory) { private void configureEmulatorActivated(DebuggerPcodeEmulatorFactory factory) {
// TODO: Pull up config page. Tool Options? Program/Trace Options? // TODO: Pull up config page. Tool Options? Program/Trace Options?
setEmulatorFactory(factory); setEmulatorFactory(factory);
@ -493,7 +598,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
protected Map.Entry<CacheKey, CachedEmulator> findNearestPrefix(CacheKey key) { protected Map.Entry<CacheKey, CachedEmulator> findNearestPrefix(CacheKey key) {
synchronized (cache) { synchronized (cache) {
Map.Entry<CacheKey, CachedEmulator> candidate = cache.floorEntry(key); Map.Entry<CacheKey, CachedEmulator> candidate = cache.floorEntry(key);
if (candidate == null) { if (candidate == null || !candidate.getValue().isValid()) {
return null; return null;
} }
if (!candidate.getKey().compareKey(key).related) { if (!candidate.getKey().compareKey(key).related) {
@ -725,7 +830,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
public DebuggerPcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time) { public DebuggerPcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time) {
CachedEmulator ce = CachedEmulator ce =
cache.get(new CacheKey(trace.getPlatformManager().getHostPlatform(), time)); cache.get(new CacheKey(trace.getPlatformManager().getHostPlatform(), time));
return ce == null ? null : ce.emulator(); return ce == null || !ce.isValid() ? null : ce.emulator();
} }
@Override @Override

View file

@ -772,12 +772,13 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
if (coordinates.getTime().isSnapOnly()) { if (coordinates.getTime().isSnapOnly()) {
return coordinates.getSnap(); return coordinates.getSnap();
} }
Collection<? extends TraceSnapshot> suitable = coordinates.getTrace() Trace trace = coordinates.getTrace();
.getTimeManager() long version = trace.getEmulatorCacheVersion();
.getSnapshotsWithSchedule(coordinates.getTime()); for (TraceSnapshot snapshot : trace.getTimeManager()
if (!suitable.isEmpty()) { .getSnapshotsWithSchedule(coordinates.getTime())) {
TraceSnapshot found = suitable.iterator().next(); if (snapshot.getVersion() >= version) {
return found.getKey(); return snapshot.getKey();
}
} }
return null; return null;
} }

View file

@ -58,7 +58,11 @@ public interface DebuggerEmulationService {
/** /**
* An emulator managed by this service * An emulator managed by this service
*/ */
record CachedEmulator(Trace trace, DebuggerPcodeMachine<?> emulator) { record CachedEmulator(Trace trace, DebuggerPcodeMachine<?> emulator, long version) {
public CachedEmulator(Trace trace, DebuggerPcodeMachine<?> emulator) {
this(trace, emulator, trace.getEmulatorCacheVersion());
}
/** /**
* Get the trace to which the emulator is bound * Get the trace to which the emulator is bound
* *
@ -83,6 +87,15 @@ public interface DebuggerEmulationService {
public DebuggerPcodeMachine<?> emulator() { public DebuggerPcodeMachine<?> emulator() {
return emulator; return emulator;
} }
/**
* Check if this cached emulator is still valid
*
* @return true if valid
*/
public boolean isValid() {
return version >= trace.getEmulatorCacheVersion();
}
} }
/** /**

View file

@ -30,11 +30,13 @@ import generic.Unique;
import generic.test.category.NightlyCategory; import generic.test.category.NightlyCategory;
import ghidra.app.plugin.assembler.*; import ghidra.app.plugin.assembler.*;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper; import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper;
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOpinion; import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOpinion;
import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlugin; import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlugin;
import ghidra.app.services.DebuggerEmulationService.EmulationResult; import ghidra.app.services.DebuggerEmulationService.EmulationResult;
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
import ghidra.app.services.DebuggerStaticMappingService; import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.pcode.exec.InterruptPcodeExecutionException; import ghidra.pcode.exec.InterruptPcodeExecutionException;
import ghidra.pcode.utils.Utils; import ghidra.pcode.utils.Utils;
@ -592,4 +594,84 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
assertEquals(new BigInteger("0", 16), assertEquals(new BigInteger("0", 16),
regs.getViewValue(scratch, regR2).getUnsignedValue()); regs.getViewValue(scratch, regR2).getUnsignedValue());
} }
@Test
public void testCacheInvalidation() throws Throwable {
createProgram();
intoProject(program);
Assembler asm = Assemblers.getAssembler(program);
Memory memory = program.getMemory();
Address addrText = addr(program, 0x00400000);
Register regR0 = program.getRegister("r0");
Register regR2 = program.getRegister("r2");
Address addrI2;
try (UndoableTransaction tid = UndoableTransaction.start(program, "Initialize")) {
MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000,
(byte) 0, TaskMonitor.DUMMY, false);
blockText.setExecute(true);
InstructionIterator ii = asm.assemble(addrText,
"mov r1, r0",
"mov r2, r1");
ii.next();
addrI2 = ii.next().getMinAddress();
program.getProgramContext()
.setValue(regR0, addrText, addrText, new BigInteger("1234", 16));
}
programManager.openProgram(program);
waitForSwing();
codeBrowser.goTo(new ProgramLocation(program, addrText));
waitForSwing();
performEnabledAction(codeBrowser.getProvider(), emulationPlugin.actionEmulateProgram, true);
DebuggerCoordinates current = traceManager.getCurrent();
Trace trace = current.getTrace();
assertNotNull(trace);
TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
// Step as written to fill the cache
waitOn(traceManager.activateAndNotify(current.time(TraceSchedule.parse("0:t0-1")),
ActivationCause.USER, false));
waitForSwing();
waitOn(traceManager.activateAndNotify(current.time(TraceSchedule.parse("0:t0-2")),
ActivationCause.USER, false));
waitForSwing();
long scratch = traceManager.getCurrentView().getSnap();
// Sanity check
assertEquals(new BigInteger("1234", 16),
regs.getViewValue(scratch, regR2).getUnsignedValue());
// Inject some logic that would require a cache refresh to materialize
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add breakpoint")) {
TraceBreakpoint tb = trace.getBreakpointManager()
.addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), addrI2, Set.of(thread),
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
tb.setEmuSleigh("""
r1 = 0x5678;
emu_exec_decoded();
""");
}
// Check the cache is still valid
waitOn(traceManager.activateAndNotify(current.time(TraceSchedule.parse("0:t0-1")),
ActivationCause.USER, false));
waitForSwing();
waitOn(traceManager.activateAndNotify(current.time(TraceSchedule.parse("0:t0-2")),
ActivationCause.USER, false));
waitForSwing();
assertEquals(scratch, traceManager.getCurrentView().getSnap());
assertEquals(new BigInteger("1234", 16),
regs.getViewValue(scratch, regR2).getUnsignedValue());
// Invalidate the cache. View should update immediately
performEnabledAction(codeBrowser.getProvider(), emulationPlugin.actionInvalidateCache,
true);
waitForTasks();
assertEquals(new BigInteger("5678", 16),
regs.getViewValue(scratch, regR2).getUnsignedValue());
}
} }

View file

@ -1,2 +1,3 @@
##VERSION: 2.0 ##VERSION: 2.0
Module.manifest||GHIDRA||||END| Module.manifest||GHIDRA||||END|
data/tracemodeling.theme.properties||GHIDRA||||END|

View file

@ -0,0 +1,7 @@
[Defaults]
icon.content.handler.trace = video-x-generic16.png
[Dark Defaults]

View file

@ -80,6 +80,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
protected static final String BASE_COMPILER = "Base Compiler"; protected static final String BASE_COMPILER = "Base Compiler";
protected static final String PLATFORM = "Platform"; protected static final String PLATFORM = "Platform";
protected static final String EXECUTABLE_PATH = "Executable Location"; protected static final String EXECUTABLE_PATH = "Executable Location";
protected static final String EMU_CACHE_VERSION = "Emulator Cache Version";
protected static final int DB_TIME_INTERVAL = 500; protected static final int DB_TIME_INTERVAL = 500;
protected static final int DB_BUFFER_SIZE = 1000; protected static final int DB_BUFFER_SIZE = 1000;
@ -754,6 +755,16 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
return getOptions(TRACE_INFO).getDate(DATE_CREATED, new Date(0)); return getOptions(TRACE_INFO).getDate(DATE_CREATED, new Date(0));
} }
@Override
public void setEmulatorCacheVersion(long version) {
getOptions(TRACE_INFO).setLong(EMU_CACHE_VERSION, version);
}
@Override
public long getEmulatorCacheVersion() {
return getOptions(TRACE_INFO).getLong(EMU_CACHE_VERSION, 0);
}
@Override @Override
public Collection<TraceProgramView> getAllProgramViews() { public Collection<TraceProgramView> getAllProgramViews() {
/** /**

View file

@ -18,7 +18,6 @@ package ghidra.trace.database;
import java.io.IOException; import java.io.IOException;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.ImageIcon;
import db.DBHandle; import db.DBHandle;
import db.buffers.BufferFile; import db.buffers.BufferFile;
@ -39,7 +38,7 @@ import ghidra.util.task.TaskMonitor;
public class DBTraceContentHandler extends DBWithUserDataContentHandler<DBTrace> { public class DBTraceContentHandler extends DBWithUserDataContentHandler<DBTrace> {
public static final String TRACE_CONTENT_TYPE = "Trace"; public static final String TRACE_CONTENT_TYPE = "Trace";
public static ImageIcon TRACE_ICON = Trace.TRACE_ICON; public static final Icon TRACE_ICON = Trace.TRACE_ICON;
static final Class<DBTrace> TRACE_DOMAIN_OBJECT_CLASS = DBTrace.class; static final Class<DBTrace> TRACE_DOMAIN_OBJECT_CLASS = DBTrace.class;
static final String TRACE_CONTENT_DEFAULT_TOOL = "Debugger"; static final String TRACE_CONTENT_DEFAULT_TOOL = "Debugger";

View file

@ -19,11 +19,9 @@ import java.io.IOException;
import db.DBRecord; import db.DBRecord;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.Trace.TraceSnapshotChangeType;
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.trace.util.TraceChangeRecord;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.database.*; import ghidra.util.database.*;
@ -35,6 +33,7 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
protected static final String REAL_TIME_COLUMN_NAME = "RealTime"; protected static final String REAL_TIME_COLUMN_NAME = "RealTime";
protected static final String SCHEDULE_COLUMN_NAME = "Schedule"; protected static final String SCHEDULE_COLUMN_NAME = "Schedule";
protected static final String VERSION_COLUMN_NAME = "Version";
protected static final String DESCRIPTION_COLUMN_NAME = "Description"; protected static final String DESCRIPTION_COLUMN_NAME = "Description";
protected static final String THREAD_COLUMN_NAME = "Thread"; protected static final String THREAD_COLUMN_NAME = "Thread";
@ -42,6 +41,8 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
static DBObjectColumn REAL_TIME_COLUMN; static DBObjectColumn REAL_TIME_COLUMN;
@DBAnnotatedColumn(SCHEDULE_COLUMN_NAME) @DBAnnotatedColumn(SCHEDULE_COLUMN_NAME)
static DBObjectColumn SCHEDULE_COLUMN; static DBObjectColumn SCHEDULE_COLUMN;
@DBAnnotatedColumn(VERSION_COLUMN_NAME)
static DBObjectColumn VERSION_COLUMN;
@DBAnnotatedColumn(DESCRIPTION_COLUMN_NAME) @DBAnnotatedColumn(DESCRIPTION_COLUMN_NAME)
static DBObjectColumn DESCRIPTION_COLUMN; static DBObjectColumn DESCRIPTION_COLUMN;
@DBAnnotatedColumn(THREAD_COLUMN_NAME) @DBAnnotatedColumn(THREAD_COLUMN_NAME)
@ -51,6 +52,8 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
long realTime; // milliseconds long realTime; // milliseconds
@DBAnnotatedField(column = SCHEDULE_COLUMN_NAME, indexed = true) @DBAnnotatedField(column = SCHEDULE_COLUMN_NAME, indexed = true)
String scheduleStr = ""; String scheduleStr = "";
@DBAnnotatedField(column = VERSION_COLUMN_NAME)
long version;
@DBAnnotatedField(column = DESCRIPTION_COLUMN_NAME) @DBAnnotatedField(column = DESCRIPTION_COLUMN_NAME)
String description; String description;
@DBAnnotatedField(column = THREAD_COLUMN_NAME) @DBAnnotatedField(column = THREAD_COLUMN_NAME)
@ -107,7 +110,9 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
@Override @Override
public long getRealTime() { public long getRealTime() {
return realTime; try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return realTime;
}
} }
@Override @Override
@ -121,7 +126,9 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
@Override @Override
public String getDescription() { public String getDescription() {
return description; try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return description;
}
} }
@Override @Override
@ -135,7 +142,9 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
@Override @Override
public TraceThread getEventThread() { public TraceThread getEventThread() {
return eventThread; try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return eventThread;
}
} }
@Override @Override
@ -156,12 +165,16 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
@Override @Override
public TraceSchedule getSchedule() { public TraceSchedule getSchedule() {
return schedule; try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return schedule;
}
} }
@Override @Override
public String getScheduleString() { public String getScheduleString() {
return scheduleStr; try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return scheduleStr;
}
} }
@Override @Override
@ -174,6 +187,22 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
} }
} }
@Override
public long getVersion() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return version;
}
}
@Override
public void setVersion(long version) {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
this.version = version;
update(VERSION_COLUMN);
manager.notifySnapshotChanged(this);
}
}
@Override @Override
public void delete() { public void delete() {
manager.deleteSnapshot(this); manager.deleteSnapshot(this);

View file

@ -17,8 +17,9 @@ package ghidra.trace.model;
import java.util.*; import java.util.*;
import javax.swing.ImageIcon; import javax.swing.Icon;
import generic.theme.GIcon;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.CompilerSpec;
@ -48,10 +49,20 @@ import ghidra.trace.model.time.TraceTimeManager;
import ghidra.trace.util.DefaultTraceChangeType; import ghidra.trace.util.DefaultTraceChangeType;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.UniversalID; import ghidra.util.UniversalID;
import resources.ResourceManager;
/**
* An indexed record of observations over the course of a target's execution
*
* <p>
* Conceptually, this is the same as a {@link Program}, but multiplied by a concrete dimension of
* time and organized into {@link TraceSnapshot snapshots}. This also includes information about
* other objects not ordinarily of concern for static analysis, for example, {@link TraceThread
* threads}, {@link TraceModule modules}, and {@link TraceBreakpoint breakpoints}. To view a
* specific snapshot and/or manipulate the trace as if it were a program, use
* {@link #getProgramView()}.
*/
public interface Trace extends DataTypeManagerDomainObject { public interface Trace extends DataTypeManagerDomainObject {
ImageIcon TRACE_ICON = ResourceManager.loadImage("images/video-x-generic16.png"); Icon TRACE_ICON = new GIcon("icon.content.handler.trace");
/** /**
* TEMPORARY: An a/b switch while both table- (legacy) and object-mode traces are supported * TEMPORARY: An a/b switch while both table- (legacy) and object-mode traces are supported
@ -411,6 +422,10 @@ public interface Trace extends DataTypeManagerDomainObject {
CompilerSpec getBaseCompilerSpec(); CompilerSpec getBaseCompilerSpec();
void setEmulatorCacheVersion(long version);
long getEmulatorCacheVersion();
AddressFactory getBaseAddressFactory(); AddressFactory getBaseAddressFactory();
TraceAddressPropertyManager getAddressPropertyManager(); TraceAddressPropertyManager getAddressPropertyManager();

View file

@ -115,6 +115,22 @@ public interface TraceSnapshot {
*/ */
void setSchedule(TraceSchedule schedule); void setSchedule(TraceSchedule schedule);
/**
* Get the snapshot's version, esp., when it represents a cache entry
*
* @see Trace#getEmulatorCacheVersion()
* @return the version
*/
long getVersion();
/**
* Set the snapshot's version, esp., when it represents a cache entry
*
* @see Trace#getEmulatorCacheVersion()
* @param version the version
*/
void setVersion(long version);
/** /**
* Delete this snapshot * Delete this snapshot
* *

View file

@ -21,6 +21,12 @@ import java.util.*;
import ghidra.framework.model.DomainObjectChangeRecord; import ghidra.framework.model.DomainObjectChangeRecord;
/**
* A trace change type, assigned a number at runtime
*
* @param <T> the type of object changed
* @param <U> the type of the object's attribute that changed
*/
public class DefaultTraceChangeType<T, U> implements TraceChangeType<T, U> { public class DefaultTraceChangeType<T, U> implements TraceChangeType<T, U> {
private static int nextType = 0x3ACE; // Stay far away from manually-assigned types private static int nextType = 0x3ACE; // Stay far away from manually-assigned types
// But not too far, since it makes the bit set for events gigantic. // But not too far, since it makes the bit set for events gigantic.