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 {
public static final String NAME = "Quick Launch";
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.stream.Collectors;
import javax.swing.Icon;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.commons.lang3.exception.ExceptionUtils;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.ToggleDockingAction;
import docking.action.builder.ActionBuilder;
import docking.action.builder.ToggleActionBuilder;
import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.events.ProgramActivatedPluginEvent;
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.DebuggerPluginPackage;
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.async.AsyncLazyMap;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
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.program.model.address.*;
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.schedule.*;
import ghidra.trace.model.time.schedule.Scheduler.RunResult;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.database.UndoableTransaction;
@ -83,6 +89,82 @@ import ghidra.util.task.TaskMonitor;
public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEmulationService {
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> {
// TODO: Should key on platform, not trace
protected final Trace trace;
@ -273,6 +355,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
DockingAction actionEmulateProgram;
DockingAction actionEmulateAddThread;
DockingAction actionInvalidateCache;
Map<Class<? extends DebuggerPcodeEmulatorFactory>, ToggleDockingAction> //
actionsChooseEmulatorFactory = new HashMap<>();
@ -302,6 +385,10 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
.popupWhen(this::emulateAddThreadEnabled)
.onAction(this::emulateAddThreadActivated)
.buildAndInstall(tool);
actionInvalidateCache = InvalidateEmulatorCacheAction.builder(this)
.enabledWhen(this::invalidateCacheEnabled)
.onAction(this::invalidateCacheActivated)
.buildAndInstall(tool);
ClassSearcher.addChangeListener(classChangeListener);
updateConfigureEmulatorStates();
}
@ -312,7 +399,8 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
private ToggleDockingAction createActionChooseEmulator(DebuggerPcodeEmulatorFactory factory) {
ToggleDockingAction action = ConfigureEmulatorAction.builder(this)
.menuPath(DebuggerPluginPackage.NAME, "Configure Emulator", factory.getTitle())
.menuPath(DebuggerPluginPackage.NAME, ConfigureEmulatorAction.NAME,
factory.getTitle())
.onAction(ctx -> configureEmulatorActivated(factory))
.buildAndInstall(tool);
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) {
// TODO: Pull up config page. Tool Options? Program/Trace Options?
setEmulatorFactory(factory);
@ -493,7 +598,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
protected Map.Entry<CacheKey, CachedEmulator> findNearestPrefix(CacheKey key) {
synchronized (cache) {
Map.Entry<CacheKey, CachedEmulator> candidate = cache.floorEntry(key);
if (candidate == null) {
if (candidate == null || !candidate.getValue().isValid()) {
return null;
}
if (!candidate.getKey().compareKey(key).related) {
@ -725,7 +830,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
public DebuggerPcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time) {
CachedEmulator ce =
cache.get(new CacheKey(trace.getPlatformManager().getHostPlatform(), time));
return ce == null ? null : ce.emulator();
return ce == null || !ce.isValid() ? null : ce.emulator();
}
@Override

View File

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

View File

@ -58,7 +58,11 @@ public interface DebuggerEmulationService {
/**
* 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
*
@ -83,6 +87,15 @@ public interface DebuggerEmulationService {
public DebuggerPcodeMachine<?> 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 ghidra.app.plugin.assembler.*;
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.mapping.DebuggerPlatformMapper;
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOpinion;
import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlugin;
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.pcode.exec.InterruptPcodeExecutionException;
import ghidra.pcode.utils.Utils;
@ -592,4 +594,84 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
assertEquals(new BigInteger("0", 16),
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
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 PLATFORM = "Platform";
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_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));
}
@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
public Collection<TraceProgramView> getAllProgramViews() {
/**

View File

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

View File

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

View File

@ -17,8 +17,9 @@ package ghidra.trace.model;
import java.util.*;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import generic.theme.GIcon;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.CompilerSpec;
@ -48,10 +49,20 @@ import ghidra.trace.model.time.TraceTimeManager;
import ghidra.trace.util.DefaultTraceChangeType;
import ghidra.util.LockHold;
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 {
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
@ -411,6 +422,10 @@ public interface Trace extends DataTypeManagerDomainObject {
CompilerSpec getBaseCompilerSpec();
void setEmulatorCacheVersion(long version);
long getEmulatorCacheVersion();
AddressFactory getBaseAddressFactory();
TraceAddressPropertyManager getAddressPropertyManager();

View File

@ -115,6 +115,22 @@ public interface TraceSnapshot {
*/
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
*

View File

@ -21,6 +21,12 @@ import java.util.*;
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> {
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.