mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-24 13:11:47 +00:00
GP-2970: Add 'Invalidate Emulator Cache' action.
This commit is contained in:
parent
bb79314d85
commit
46a620f687
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,3 @@
|
||||
##VERSION: 2.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/tracemodeling.theme.properties||GHIDRA||||END|
|
||||
|
@ -0,0 +1,7 @@
|
||||
|
||||
[Defaults]
|
||||
|
||||
icon.content.handler.trace = video-x-generic16.png
|
||||
|
||||
[Dark Defaults]
|
||||
|
@ -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() {
|
||||
/**
|
||||
|
@ -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";
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user