mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-2970: Add 'Invalidate Emulator Cache' action.
This commit is contained in:
parent
bb79314d85
commit
46a620f687
13 changed files with 309 additions and 83 deletions
|
@ -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?
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
##VERSION: 2.0
|
##VERSION: 2.0
|
||||||
Module.manifest||GHIDRA||||END|
|
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 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() {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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,8 +110,10 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getRealTime() {
|
public long getRealTime() {
|
||||||
|
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||||
return realTime;
|
return realTime;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setRealTime(long millis) {
|
public void setRealTime(long millis) {
|
||||||
|
@ -121,8 +126,10 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
|
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDescription(String description) {
|
public void setDescription(String description) {
|
||||||
|
@ -135,8 +142,10 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TraceThread getEventThread() {
|
public TraceThread getEventThread() {
|
||||||
|
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||||
return eventThread;
|
return eventThread;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setEventThread(TraceThread thread) {
|
public void setEventThread(TraceThread thread) {
|
||||||
|
@ -156,13 +165,17 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TraceSchedule getSchedule() {
|
public TraceSchedule getSchedule() {
|
||||||
|
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||||
return schedule;
|
return schedule;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getScheduleString() {
|
public String getScheduleString() {
|
||||||
|
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||||
return scheduleStr;
|
return scheduleStr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSchedule(TraceSchedule schedule) {
|
public void setSchedule(TraceSchedule schedule) {
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
*
|
*
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue