GP-2551: Fix RegistersProvider for new trace conventions

This commit is contained in:
Dan 2022-09-15 11:53:24 -04:00
parent e89b86a66f
commit bc2ba594b4
86 changed files with 1743 additions and 708 deletions

View file

@ -65,7 +65,7 @@ public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
CONTAINER_ATTRIBUTE_NAME, registers, // CONTAINER_ATTRIBUTE_NAME, registers, //
LENGTH_ATTRIBUTE_NAME, bitLength, // BIT_LENGTH_ATTRIBUTE_NAME, bitLength, //
DISPLAY_ATTRIBUTE_NAME, "[" + register.getName() + "]" // DISPLAY_ATTRIBUTE_NAME, "[" + register.getName() + "]" //
), "Initialized"); ), "Initialized");
} }

View file

@ -52,13 +52,14 @@ public class FridaModelTargetRegisterImpl
changeAttributes(List.of(), Map.of( // changeAttributes(List.of(), Map.of( //
CONTAINER_ATTRIBUTE_NAME, registers, // CONTAINER_ATTRIBUTE_NAME, registers, //
LENGTH_ATTRIBUTE_NAME, getBitLength(), // BIT_LENGTH_ATTRIBUTE_NAME, getBitLength(), //
DISPLAY_ATTRIBUTE_NAME, getDescription(0), // DISPLAY_ATTRIBUTE_NAME, getDescription(0), //
VALUE_ATTRIBUTE_NAME, value == null ? "0" : value, // VALUE_ATTRIBUTE_NAME, value == null ? "0" : value, //
MODIFIED_ATTRIBUTE_NAME, false // MODIFIED_ATTRIBUTE_NAME, false //
), "Initialized"); ), "Initialized");
} }
@Override
public String getDescription(int level) { public String getDescription(int level) {
return getName() + " : " + getValue(); return getName() + " : " + getValue();
} }
@ -85,6 +86,7 @@ public class FridaModelTargetRegisterImpl
return (FridaValue) getModelObject(); return (FridaValue) getModelObject();
} }
@Override
public byte[] getBytes() { public byte[] getBytes() {
String oldValue = value; String oldValue = value;
value = getValue(); value = getValue();
@ -113,6 +115,7 @@ public class FridaModelTargetRegisterImpl
return bytes; return bytes;
} }
@Override
public String getDisplay() { public String getDisplay() {
return getValue() == null ? getName() : getName() + " : " + getValue(); return getValue() == null ? getName() : getName() + " : " + getValue();
} }

View file

@ -60,7 +60,7 @@ public class GdbModelTargetRegister
changeAttributes(List.of(), Map.of( // changeAttributes(List.of(), Map.of( //
CONTAINER_ATTRIBUTE_NAME, registers, // CONTAINER_ATTRIBUTE_NAME, registers, //
LENGTH_ATTRIBUTE_NAME, bitLength, // BIT_LENGTH_ATTRIBUTE_NAME, bitLength, //
DISPLAY_ATTRIBUTE_NAME, getName() // DISPLAY_ATTRIBUTE_NAME, getName() //
), "Initialized"); ), "Initialized");
} }

View file

@ -64,7 +64,7 @@ public class GdbModelTargetStackFrameRegister
changeAttributes(List.of(), Map.of( // changeAttributes(List.of(), Map.of( //
CONTAINER_ATTRIBUTE_NAME, registers, // CONTAINER_ATTRIBUTE_NAME, registers, //
LENGTH_ATTRIBUTE_NAME, bitLength, // BIT_LENGTH_ATTRIBUTE_NAME, bitLength, //
DISPLAY_ATTRIBUTE_NAME, getName(), // DISPLAY_ATTRIBUTE_NAME, getName(), //
MODIFIED_ATTRIBUTE_NAME, false // MODIFIED_ATTRIBUTE_NAME, false //
), "Initialized"); ), "Initialized");

View file

@ -53,7 +53,7 @@ public class LldbModelTargetStackFrameRegisterImpl
changeAttributes(List.of(), Map.of( // changeAttributes(List.of(), Map.of( //
CONTAINER_ATTRIBUTE_NAME, bank.getContainer(), // CONTAINER_ATTRIBUTE_NAME, bank.getContainer(), //
LENGTH_ATTRIBUTE_NAME, getBitLength(), // BIT_LENGTH_ATTRIBUTE_NAME, getBitLength(), //
DISPLAY_ATTRIBUTE_NAME, getDescription(0), // DISPLAY_ATTRIBUTE_NAME, getDescription(0), //
VALUE_ATTRIBUTE_NAME, value == null ? "0" : value, // VALUE_ATTRIBUTE_NAME, value == null ? "0" : value, //
MODIFIED_ATTRIBUTE_NAME, false // MODIFIED_ATTRIBUTE_NAME, false //

View file

@ -50,7 +50,7 @@ public class JdiModelTargetRegister extends JdiModelTargetObjectImpl implements
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
DISPLAY_ATTRIBUTE_NAME, getDisplay(), // DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
CONTAINER_ATTRIBUTE_NAME, parent, // CONTAINER_ATTRIBUTE_NAME, parent, //
LENGTH_ATTRIBUTE_NAME, Long.SIZE // BIT_LENGTH_ATTRIBUTE_NAME, Long.SIZE //
), "Initialized"); ), "Initialized");
} }

View file

@ -98,6 +98,7 @@ public class DebuggerCoordinates {
private Long viewSnap; private Long viewSnap;
private DefaultTraceTimeViewport viewport; private DefaultTraceTimeViewport viewport;
private TraceObject registerContainer;
DebuggerCoordinates(Trace trace, TracePlatform platform, TraceRecorder recorder, DebuggerCoordinates(Trace trace, TracePlatform platform, TraceRecorder recorder,
TraceThread thread, TraceProgramView view, TraceSchedule time, Integer frame, TraceThread thread, TraceProgramView view, TraceSchedule time, Integer frame,
@ -483,7 +484,7 @@ public class DebuggerCoordinates {
} }
newTrace = trace; newTrace = trace;
} }
TracePlatform newPlatform = resolvePlatform(newTrace); TracePlatform newPlatform = platform != null ? platform : resolvePlatform(newTrace);
TraceThread newThread = resolveThread(newObject); TraceThread newThread = resolveThread(newObject);
Integer newFrame = resolveFrame(newObject); Integer newFrame = resolveFrame(newObject);
@ -553,6 +554,13 @@ public class DebuggerCoordinates {
return object; return object;
} }
public TraceObject getRegisterContainer() {
if (registerContainer != null) {
return registerContainer;
}
return registerContainer = object.queryRegisterContainer(getFrame());
}
public synchronized long getViewSnap() { public synchronized long getViewSnap() {
if (viewSnap != null) { if (viewSnap != null) {
return viewSnap; return viewSnap;

View file

@ -86,7 +86,7 @@ public abstract class DebuggerReadsMemoryTrait {
.map(TargetObject::invalidateCaches) .map(TargetObject::invalidateCaches)
.toArray(CompletableFuture[]::new); .toArray(CompletableFuture[]::new);
return CompletableFuture.allOf(requests).thenCompose(_r -> { return CompletableFuture.allOf(requests).thenCompose(_r -> {
return recorder.readMemoryBlocks(sel, monitor, false); return recorder.readMemoryBlocks(sel, monitor);
}); });
}); });
} }

View file

@ -68,6 +68,6 @@ public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
return AsyncUtils.NIL; return AsyncUtils.NIL;
} }
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY, false); return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY);
} }
} }

View file

@ -93,6 +93,6 @@ public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
return AsyncUtils.NIL; return AsyncUtils.NIL;
} }
return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY, false); return recorder.readMemoryBlocks(toRead, TaskMonitor.DUMMY);
} }
} }

View file

@ -812,8 +812,8 @@ public class DebuggerCopyIntoProgramDialog extends DialogComponentProvider {
throws Exception { throws Exception {
synchronized (this) { synchronized (this) {
monitor.checkCanceled(); monitor.checkCanceled();
CompletableFuture<NavigableMap<Address, byte[]>> recCapture = CompletableFuture<Void> recCapture =
recorder.readMemoryBlocks(new AddressSet(range), monitor, false); recorder.readMemoryBlocks(new AddressSet(range), monitor);
this.captureTask = recCapture.thenCompose(__ -> { this.captureTask = recCapture.thenCompose(__ -> {
return recorder.getTarget().getModel().flushEvents(); return recorder.getTarget().getModel().flushEvents();
}).thenCompose(__ -> { }).thenCompose(__ -> {

View file

@ -980,7 +980,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
doLoadPcodeFrameFromEmulator(emu); doLoadPcodeFrameFromEmulator(emu);
return; return;
} }
emulationService.backgroundEmulate(trace, time).thenAcceptAsync(__ -> { emulationService.backgroundEmulate(current.getPlatform(), time).thenAcceptAsync(__ -> {
clear(); clear();
if (current != this.current) { if (current != this.current) {
return; return;

View file

@ -70,10 +70,11 @@ import ghidra.program.model.listing.Data;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.Trace.*; import ghidra.trace.model.Trace.*;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.*; import ghidra.trace.model.listing.*;
import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.memory.*;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.*; import ghidra.trace.util.*;
import ghidra.util.*; import ghidra.util.*;
@ -223,8 +224,8 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
} }
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) { protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
if (!Objects.equals(a.getTrace(), b.getTrace())) { if (!Objects.equals(a.getPlatform(), b.getPlatform())) {
return false; return false; // subsumes trace
} }
if (!Objects.equals(a.getRecorder(), b.getRecorder())) { if (!Objects.equals(a.getRecorder(), b.getRecorder())) {
return false; // For live read/writes return false; // For live read/writes
@ -254,11 +255,23 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
listenFor(TraceThreadChangeType.LIFESPAN_CHANGED, this::threadDestroyed); listenFor(TraceThreadChangeType.LIFESPAN_CHANGED, this::threadDestroyed);
} }
private boolean isVisibleObjectsMode(AddressSpace space) {
TraceObject container = current.getRegisterContainer();
return container != null &&
container.getCanonicalPath().toString().equals(space.getName());
}
private boolean isVisible(TraceAddressSpace space) { private boolean isVisible(TraceAddressSpace space) {
TraceThread curThread = current.getThread(); TraceThread curThread = current.getThread();
if (curThread == null) { if (curThread == null) {
return false; return false;
} }
if (space.getAddressSpace().isOverlaySpace()) {
return isVisibleObjectsMode(space.getAddressSpace());
}
if (!space.getAddressSpace().isRegisterSpace()) {
return true; // Memory-mapped, visible no matter the active thread
}
if (space.getThread() != curThread) { if (space.getThread() != curThread) {
return false; return false;
} }
@ -272,6 +285,12 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (!isVisible(space)) { if (!isVisible(space)) {
return false; return false;
} }
if (space.getAddressSpace().isMemorySpace()) {
return current.getPlatform()
.getLanguage()
.getRegisterAddresses()
.intersects(range.getX1(), range.getX2());
}
TraceProgramView view = current.getView(); TraceProgramView view = current.getView();
if (view == null || !view.getViewport().containsAnyUpper(range.getLifespan())) { if (view == null || !view.getViewport().containsAnyUpper(range.getLifespan())) {
return false; return false;
@ -439,7 +458,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
private AsyncLazyValue<Void> readTheseCoords = private AsyncLazyValue<Void> readTheseCoords =
new AsyncLazyValue<>(this::readRegistersIfLiveAndAccessible); /* "read" past tense */ new AsyncLazyValue<>(this::readRegistersIfLiveAndAccessible); /* "read" past tense */
private Trace currentTrace; // Copy for transition private Trace currentTrace; // Copy for transition
private TraceRecorder currentRecorder; // Copy of transition private TraceRecorder currentRecorder; // Copy for transition
@AutoServiceConsumed @AutoServiceConsumed
private DebuggerModelService modelService; private DebuggerModelService modelService;
@ -498,7 +517,6 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
DebuggerRegisterActionContext myActionContext; DebuggerRegisterActionContext myActionContext;
AddressSetView viewKnown; AddressSetView viewKnown;
AddressSetView catalog;
protected DebuggerRegistersProvider(final DebuggerRegistersPlugin plugin, protected DebuggerRegistersProvider(final DebuggerRegistersPlugin plugin,
Map<LanguageCompilerSpecPair, LinkedHashSet<Register>> selectionByCSpec, Map<LanguageCompilerSpecPair, LinkedHashSet<Register>> selectionByCSpec,
@ -691,14 +709,14 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
} }
private void selectRegistersActivated() { private void selectRegistersActivated() {
TraceThread curThread = current.getThread(); TracePlatform curPlatform = current.getPlatform();
if (curThread == null) { if (current.getThread() == null) {
return; return;
} }
availableRegsDialog.setLanguage(curThread.getTrace().getBaseLanguage()); availableRegsDialog.setLanguage(curPlatform.getLanguage());
Set<Register> viewKnown = computeDefaultRegisterSelection(curThread); Set<Register> viewKnown = computeDefaultRegisterSelection(curPlatform);
availableRegsDialog.setKnown(viewKnown); availableRegsDialog.setKnown(viewKnown);
Set<Register> selection = getSelectionFor(curThread); Set<Register> selection = getSelectionFor(curPlatform);
// NOTE: Modifies selection in place // NOTE: Modifies selection in place
availableRegsDialog.setSelection(selection); availableRegsDialog.setSelection(selection);
tool.showDialog(availableRegsDialog); tool.showDialog(availableRegsDialog);
@ -777,20 +795,6 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
removeOldTraceListener(); removeOldTraceListener();
this.currentTrace = trace; this.currentTrace = trace;
addNewTraceListener(); addNewTraceListener();
catalogRegisterAddresses();
}
private void catalogRegisterAddresses() {
this.catalog = null;
if (currentTrace == null) {
return;
}
AddressSet catalog = new AddressSet();
for (Register reg : currentTrace.getBaseLanguage().getRegisters()) {
catalog.add(TraceRegisterUtils.rangeForRegister(reg));
}
this.catalog = catalog;
} }
private void removeOldRecorderListener() { private void removeOldRecorderListener() {
@ -830,6 +834,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
doSetRecorder(current.getRecorder()); doSetRecorder(current.getRecorder());
updateSubTitle(); updateSubTitle();
prepareRegisterSpace();
recomputeViewKnown(); recomputeViewKnown();
loadRegistersAndValues(); loadRegistersAndValues();
contextChanged(); contextChanged();
@ -861,7 +866,8 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (regs == null) { if (regs == null) {
return BigInteger.ZERO; return BigInteger.ZERO;
} }
return regs.getViewValue(current.getViewSnap(), register).getUnsignedValue(); return regs.getViewValue(current.getPlatform(), current.getViewSnap(), register)
.getUnsignedValue();
} }
void writeRegisterValue(Register register, BigInteger value) { void writeRegisterValue(Register register, BigInteger value) {
@ -902,8 +908,9 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
private RegisterValue combineWithTraceBaseRegisterValue(RegisterValue rv) { private RegisterValue combineWithTraceBaseRegisterValue(RegisterValue rv) {
TraceMemorySpace regs = getRegisterMemorySpace(false); TraceMemorySpace regs = getRegisterMemorySpace(false);
TracePlatform platform = current.getPlatform();
long snap = current.getViewSnap(); long snap = current.getViewSnap();
return TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, snap, regs, true); return TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, platform, snap, regs, true);
} }
/** /**
@ -916,9 +923,11 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
UndoableTransaction.start(current.getTrace(), "Edit Register Type")) { UndoableTransaction.start(current.getTrace(), "Edit Register Type")) {
TraceCodeSpace space = getRegisterMemorySpace(true).getCodeSpace(true); TraceCodeSpace space = getRegisterMemorySpace(true).getCodeSpace(true);
long snap = current.getViewSnap(); long snap = current.getViewSnap();
space.definedUnits().clear(Range.closed(snap, snap), register, TaskMonitor.DUMMY); TracePlatform platform = current.getPlatform();
space.definedUnits()
.clear(platform, Range.closed(snap, snap), register, TaskMonitor.DUMMY);
if (dataType != null) { if (dataType != null) {
space.definedData().create(Range.atLeast(snap), register, dataType); space.definedData().create(platform, Range.atLeast(snap), register, dataType);
} }
} }
catch (CodeUnitInsertionException | CancelledException e) { catch (CodeUnitInsertionException | CancelledException e) {
@ -931,8 +940,9 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (space == null) { if (space == null) {
return null; return null;
} }
TracePlatform platform = current.getPlatform();
long snap = current.getViewSnap(); long snap = current.getViewSnap();
return space.definedData().getForRegister(snap, register); return space.definedData().getForRegister(platform, snap, register);
} }
DataType getRegisterDataType(Register register) { DataType getRegisterDataType(Register register) {
@ -980,27 +990,64 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
return TraceRegisterUtils.getValueRepresentationHackPointer(data); return TraceRegisterUtils.getValueRepresentationHackPointer(data);
} }
/**
* Ensure the register space exists and has been populated from register object values.
*
* <p>
* TODO: I wish this were not necessary. Maybe I should create the space when register object
* values are populated.
*/
void prepareRegisterSpace() {
if (current.getThread() != null &&
current.getTrace().getObjectManager().getRootSchema() != null) {
try (UndoableTransaction tid =
UndoableTransaction.start(current.getTrace(), "Create/initialize register space")) {
getRegisterMemorySpace(true);
}
}
}
void recomputeViewKnown() { void recomputeViewKnown() {
if (catalog == null) { TracePlatform platform = current.getPlatform();
if (platform == null) {
viewKnown = null; viewKnown = null;
return; return;
} }
TraceMemorySpace regs = getRegisterMemorySpace(false);
TraceProgramView view = current.getView(); TraceProgramView view = current.getView();
if (regs == null || view == null) { if (view == null) {
viewKnown = null; viewKnown = null;
return; return;
} }
viewKnown = new AddressSet(view.getViewport() TraceMemoryManager mem = current.getTrace().getMemoryManager();
.unionedAddresses(snap -> regs.getAddressesWithState(snap, catalog, AddressSetView viewKnownMem = view.getViewport()
state -> state == TraceMemoryState.KNOWN))); .unionedAddresses(snap -> mem.getAddressesWithState(snap,
platform.mapGuestToHost(platform.getLanguage().getRegisterAddresses()),
state -> state == TraceMemoryState.KNOWN));
TraceMemorySpace regs = getRegisterMemorySpace(false);
if (regs == null) {
viewKnown = new AddressSet(viewKnownMem);
return;
}
AddressSetView hostRegs =
platform.mapGuestToHost(platform.getLanguage().getRegisterAddresses());
AddressSetView overlayRegs =
TraceRegisterUtils.getOverlaySet(regs.getAddressSpace(), hostRegs);
AddressSetView viewKnownRegs = view.getViewport()
.unionedAddresses(snap -> regs.getAddressesWithState(snap, overlayRegs,
state -> state == TraceMemoryState.KNOWN));
viewKnown = viewKnownRegs.union(viewKnownMem);
} }
boolean isRegisterKnown(Register register) { boolean isRegisterKnown(Register register) {
if (viewKnown == null) { if (viewKnown == null) {
return false; return false;
} }
AddressRange range = TraceRegisterUtils.rangeForRegister(register); TraceMemorySpace regs = getRegisterMemorySpace(false);
if (regs == null && register.getAddressSpace().isRegisterSpace()) {
return false;
}
AddressRange range =
current.getPlatform().getConventionalRegisterRange(regs.getAddressSpace(), register);
return viewKnown.contains(range.getMinAddress(), range.getMaxAddress()); return viewKnown.contains(range.getMinAddress(), range.getMaxAddress());
} }
@ -1008,7 +1055,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (previous.getThread() == null || current.getThread() == null) { if (previous.getThread() == null || current.getThread() == null) {
return false; return false;
} }
if (previous.getTrace().getBaseLanguage() != current.getTrace().getBaseLanguage()) { if (previous.getPlatform().getLanguage() != current.getPlatform().getLanguage()) {
return false; return false;
} }
if (!isRegisterKnown(register)) { if (!isRegisterKnown(register)) {
@ -1019,8 +1066,10 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (prevSpace == null) { if (prevSpace == null) {
return false; return false;
} }
RegisterValue curRegVal = curSpace.getViewValue(current.getViewSnap(), register); RegisterValue curRegVal =
RegisterValue prevRegVal = prevSpace.getViewValue(previous.getViewSnap(), register); curSpace.getViewValue(current.getPlatform(), current.getViewSnap(), register);
RegisterValue prevRegVal =
prevSpace.getViewValue(current.getPlatform(), previous.getViewSnap(), register);
return !Objects.equals(curRegVal, prevRegVal); return !Objects.equals(curRegVal, prevRegVal);
} }
@ -1031,8 +1080,10 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
/** /**
* Gather general registers, the program counter, and the stack pointer * Gather general registers, the program counter, and the stack pointer
* *
* <p>
* This excludes the context register * This excludes the context register
* *
* <p>
* TODO: Several pspec files need adjustment to clean up "common registers" * TODO: Several pspec files need adjustment to clean up "common registers"
* *
* @param cSpec the compiler spec * @param cSpec the compiler spec
@ -1061,49 +1112,17 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
return result; return result;
} }
public LinkedHashSet<Register> computeDefaultRegisterSelection(TraceThread thread) { public LinkedHashSet<Register> computeDefaultRegisterSelection(TracePlatform platform) {
return collectCommonRegisters(thread.getTrace().getBaseCompilerSpec()); return collectCommonRegisters(platform.getCompilerSpec());
} }
public LinkedHashSet<Register> computeDefaultRegisterFavorites(TraceThread thread) { public LinkedHashSet<Register> computeDefaultRegisterFavorites(TracePlatform platform) {
LinkedHashSet<Register> favorites = new LinkedHashSet<>(); LinkedHashSet<Register> favorites = new LinkedHashSet<>();
CompilerSpec cSpec = thread.getTrace().getBaseCompilerSpec(); favorites.add(platform.getLanguage().getProgramCounter());
favorites.add(cSpec.getLanguage().getProgramCounter()); favorites.add(platform.getCompilerSpec().getStackPointer());
favorites.add(cSpec.getStackPointer());
return favorites; return favorites;
} }
public LinkedHashSet<Register> computeDefaultRegistersOld(TraceThread thread) {
LinkedHashSet<Register> viewKnown = new LinkedHashSet<>();
/**
* NOTE: It is rare that this includes registers outside of those common to the view and
* target, but in case the user has manually populated such registers, this will ensure they
* are visible in the UI.
*
* Also, in case the current thread is not live, we want the DB values to appear.
*/
viewKnown.addAll(collectBaseRegistersWithKnownValues(thread));
Trace trace = thread.getTrace();
TraceRecorder recorder = modelService.getRecorder(trace);
if (recorder == null) {
viewKnown.addAll(collectCommonRegisters(trace.getBaseCompilerSpec()));
return viewKnown;
}
TargetThread targetThread = recorder.getTargetThread(thread);
if (targetThread == null || !recorder.isRegisterBankAccessible(thread, 0)) {
return viewKnown;
}
DebuggerRegisterMapper regMapper = recorder.getRegisterMapper(thread);
if (regMapper == null) {
return viewKnown;
}
for (Register onTarget : regMapper.getRegistersOnTarget()) {
viewKnown.add(onTarget);
viewKnown.addAll(onTarget.getChildRegisters());
}
return viewKnown;
}
protected static TraceMemorySpace getRegisterMemorySpace(DebuggerCoordinates coords, protected static TraceMemorySpace getRegisterMemorySpace(DebuggerCoordinates coords,
boolean createIfAbsent) { boolean createIfAbsent) {
TraceThread thread = coords.getThread(); TraceThread thread = coords.getThread();
@ -1163,33 +1182,29 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
return result; return result;
} }
protected LanguageCompilerSpecPair getLangCSpecPair(Trace trace) { protected static LanguageCompilerSpecPair getLangCSpecPair(TracePlatform platform) {
return new LanguageCompilerSpecPair(trace.getBaseLanguage().getLanguageID(), return new LanguageCompilerSpecPair(platform.getLanguage().getLanguageID(),
trace.getBaseCompilerSpec().getCompilerSpecID()); platform.getCompilerSpec().getCompilerSpecID());
} }
protected LanguageCompilerSpecPair getLangCSpecPair(TraceThread thread) { protected Set<Register> getSelectionFor(TracePlatform platform) {
return getLangCSpecPair(thread.getTrace());
}
protected Set<Register> getSelectionFor(TraceThread thread) {
synchronized (selectionByCSpec) { synchronized (selectionByCSpec) {
LanguageCompilerSpecPair lcsp = getLangCSpecPair(thread); LanguageCompilerSpecPair lcsp = getLangCSpecPair(platform);
return selectionByCSpec.computeIfAbsent(lcsp, return selectionByCSpec.computeIfAbsent(lcsp,
__ -> computeDefaultRegisterSelection(thread)); __ -> computeDefaultRegisterSelection(platform));
} }
} }
protected Set<Register> getFavoritesFor(TraceThread thread) { protected Set<Register> getFavoritesFor(TracePlatform platform) {
synchronized (favoritesByCSpec) { synchronized (favoritesByCSpec) {
LanguageCompilerSpecPair lcsp = getLangCSpecPair(thread); LanguageCompilerSpecPair lcsp = getLangCSpecPair(platform);
return favoritesByCSpec.computeIfAbsent(lcsp, return favoritesByCSpec.computeIfAbsent(lcsp,
__ -> computeDefaultRegisterFavorites(thread)); __ -> computeDefaultRegisterFavorites(platform));
} }
} }
protected void setFavorite(Register register, boolean favorite) { protected void setFavorite(Register register, boolean favorite) {
Set<Register> favorites = getFavoritesFor(current.getThread()); Set<Register> favorites = getFavoritesFor(current.getPlatform());
if (favorite) { if (favorite) {
favorites.add(register); favorites.add(register);
} }
@ -1199,13 +1214,13 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
} }
public boolean isFavorite(Register register) { public boolean isFavorite(Register register) {
Set<Register> favorites = getFavoritesFor(current.getThread()); Set<Register> favorites = getFavoritesFor(current.getPlatform());
return favorites.contains(register); return favorites.contains(register);
} }
public CompletableFuture<Void> setSelectedRegistersAndLoad( public CompletableFuture<Void> setSelectedRegistersAndLoad(
Collection<Register> selectedRegisters) { Collection<Register> selectedRegisters) {
Set<Register> selection = getSelectionFor(current.getThread()); Set<Register> selection = getSelectionFor(current.getPlatform());
selection.clear(); selection.clear();
selection.addAll(new TreeSet<>(selectedRegisters)); selection.addAll(new TreeSet<>(selectedRegisters));
return loadRegistersAndValues(); return loadRegistersAndValues();
@ -1226,7 +1241,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
} }
protected void displaySelectedRegisters(Set<Register> selected) { protected void displaySelectedRegisters(Set<Register> selected) {
List<Register> regs = currentTrace.getBaseLanguage().getRegisters(); List<Register> regs = current.getPlatform().getLanguage().getRegisters();
for (Iterator<Entry<Register, RegisterRow>> it = regMap.entrySet().iterator(); it for (Iterator<Entry<Register, RegisterRow>> it = regMap.entrySet().iterator(); it
.hasNext();) { .hasNext();) {
Map.Entry<Register, RegisterRow> ent = it.next(); Map.Entry<Register, RegisterRow> ent = it.next();
@ -1246,13 +1261,12 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
} }
protected CompletableFuture<Void> loadRegistersAndValues() { protected CompletableFuture<Void> loadRegistersAndValues() {
TraceThread curThread = current.getThread(); if (current.getThread() == null) {
if (curThread == null) {
regsTableModel.clear(); regsTableModel.clear();
regMap.clear(); regMap.clear();
return AsyncUtils.NIL; return AsyncUtils.NIL;
} }
Set<Register> selected = getSelectionFor(curThread); Set<Register> selected = getSelectionFor(current.getPlatform());
displaySelectedRegisters(selected); displaySelectedRegisters(selected);
return loadValues(); return loadValues();
} }
@ -1272,6 +1286,30 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
return regs.stream().filter(Register::isBaseRegister).collect(Collectors.toSet()); return regs.stream().filter(Register::isBaseRegister).collect(Collectors.toSet());
} }
protected CompletableFuture<?> readRegistersLegacy(TraceRecorder recorder,
TraceThread traceThread, Set<Register> toRead) {
DebuggerRegisterMapper regMapper = recorder.getRegisterMapper(traceThread);
if (regMapper == null) {
Msg.error(this, "Target is live, but we haven't got a register mapper, yet");
return AsyncUtils.NIL;
}
toRead.retainAll(regMapper.getRegistersOnTarget());
TargetRegisterBank bank = recorder.getTargetRegisterBank(traceThread, current.getFrame());
if (bank == null || !bank.isValid()) {
Msg.error(this, "Current frame's bank does not exist");
return AsyncUtils.NIL;
}
// TODO: Should probably always be the host platform. I suspect it's ignored anyway.
return recorder.captureThreadRegisters(current.getPlatform(), traceThread,
current.getFrame(), toRead);
}
protected CompletableFuture<?> readRegistersObjectMode(TraceRecorder recorder,
TraceThread traceThread, Set<Register> toRead) {
return recorder.captureThreadRegisters(current.getPlatform(), traceThread,
current.getFrame(), toRead);
}
protected CompletableFuture<Void> readRegistersIfLiveAndAccessible() { protected CompletableFuture<Void> readRegistersIfLiveAndAccessible() {
TraceRecorder recorder = current.getRecorder(); TraceRecorder recorder = current.getRecorder();
if (recorder == null) { if (recorder == null) {
@ -1289,20 +1327,16 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (targetThread == null) { if (targetThread == null) {
return AsyncUtils.NIL; return AsyncUtils.NIL;
} }
Set<Register> toRead = new HashSet<>(baseRegisters(getSelectionFor(traceThread)));
DebuggerRegisterMapper regMapper = recorder.getRegisterMapper(traceThread); Set<Register> toRead = new HashSet<>(baseRegisters(getSelectionFor(current.getPlatform())));
if (regMapper == null) {
Msg.error(this, "Target is live, but we haven't got a register mapper, yet"); CompletableFuture<?> future;
return AsyncUtils.NIL; if (current.getTrace().getObjectManager().getRootSchema() == null) {
future = readRegistersLegacy(recorder, traceThread, toRead);
} }
toRead.retainAll(regMapper.getRegistersOnTarget()); else {
TargetRegisterBank bank = recorder.getTargetRegisterBank(traceThread, current.getFrame()); future = readRegistersObjectMode(recorder, traceThread, toRead);
if (bank == null || !bank.isValid()) {
Msg.error(this, "Current frame's bank does not exist");
return AsyncUtils.NIL;
} }
CompletableFuture<?> future =
recorder.captureThreadRegisters(traceThread, current.getFrame(), toRead);
return future.exceptionally(ex -> { return future.exceptionally(ex -> {
ex = AsyncUtils.unwrapThrowable(ex); ex = AsyncUtils.unwrapThrowable(ex);
if (ex instanceof DebuggerModelAccessException) { if (ex instanceof DebuggerModelAccessException) {

View file

@ -95,8 +95,7 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
try { try {
// This is on its own task thread, so whatever. // This is on its own task thread, so whatever.
// Just don't hang it indefinitely. // Just don't hang it indefinitely.
recorder.readMemoryBlocks(set, TaskMonitor.DUMMY, false) recorder.readMemoryBlocks(set, TaskMonitor.DUMMY).get(1000, TimeUnit.MILLISECONDS);
.get(1000, TimeUnit.MILLISECONDS);
} }
catch (InterruptedException | ExecutionException | TimeoutException e) { catch (InterruptedException | ExecutionException | TimeoutException e) {
Msg.error("Could not read module header from target", e); Msg.error("Could not read module header from target", e);

View file

@ -30,11 +30,14 @@ import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus; import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.*;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.Trace.TraceProgramViewListener; import ghidra.trace.model.Trace.TraceProgramViewListener;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryOperations; import ghidra.trace.model.memory.TraceMemoryOperations;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.program.*; import ghidra.trace.model.program.*;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.schedule.PatchStep; import ghidra.trace.model.time.schedule.PatchStep;
@ -146,30 +149,41 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
return CompletableFuture return CompletableFuture
.failedFuture(new MemoryAccessException("View is not the present")); .failedFuture(new MemoryAccessException("View is not the present"));
} }
return recorder.writeVariable(coordinates.getThread(), coordinates.getFrame(), address, return recorder.writeVariable(coordinates.getPlatform(), coordinates.getThread(),
data); coordinates.getFrame(), address, data);
} }
protected CompletableFuture<Void> writeTraceVariable(DebuggerCoordinates coordinates, protected CompletableFuture<Void> writeTraceVariable(DebuggerCoordinates coordinates,
Address address, byte[] data) { Address guestAddress, byte[] data) {
Trace trace = coordinates.getTrace(); Trace trace = coordinates.getTrace();
TracePlatform platform = coordinates.getPlatform();
long snap = coordinates.getViewSnap(); long snap = coordinates.getViewSnap();
Address hostAddress = platform.mapGuestToHost(guestAddress);
if (hostAddress == null) {
throw new IllegalArgumentException(
"Guest address " + guestAddress + " is not mapped");
}
TraceMemoryOperations memOrRegs; TraceMemoryOperations memOrRegs;
Address overlayAddress;
try (UndoableTransaction txid = try (UndoableTransaction txid =
UndoableTransaction.start(trace, "Edit Variable")) { UndoableTransaction.start(trace, "Edit Variable")) {
if (address.isRegisterAddress()) { if (hostAddress.isRegisterAddress()) {
TraceThread thread = coordinates.getThread(); TraceThread thread = coordinates.getThread();
if (thread == null) { if (thread == null) {
throw new IllegalArgumentException("Register edits require a thread."); throw new IllegalArgumentException("Register edits require a thread.");
} }
memOrRegs = trace.getMemoryManager() TraceMemorySpace regs = trace.getMemoryManager()
.getMemoryRegisterSpace(thread, coordinates.getFrame(), .getMemoryRegisterSpace(thread, coordinates.getFrame(),
true); true);
memOrRegs = regs;
overlayAddress = regs.getAddressSpace().getOverlayAddress(hostAddress);
} }
else { else {
memOrRegs = trace.getMemoryManager(); memOrRegs = trace.getMemoryManager();
overlayAddress = hostAddress;
} }
if (memOrRegs.putBytes(snap, address, ByteBuffer.wrap(data)) != data.length) { if (memOrRegs.putBytes(snap, overlayAddress,
ByteBuffer.wrap(data)) != data.length) {
return CompletableFuture.failedFuture(new MemoryAccessException()); return CompletableFuture.failedFuture(new MemoryAccessException());
} }
} }
@ -186,9 +200,9 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
// TODO: Well, technically, only for register edits // TODO: Well, technically, only for register edits
throw new IllegalArgumentException("Emulator edits require a thread."); throw new IllegalArgumentException("Emulator edits require a thread.");
} }
Language language = coordinates.getPlatform().getLanguage();
TraceSchedule time = coordinates.getTime() TraceSchedule time = coordinates.getTime()
.patched(thread, PatchStep.generateSleigh( .patched(thread, language, PatchStep.generateSleigh(language, address, data));
coordinates.getTrace().getBaseLanguage(), address, data));
DebuggerCoordinates withTime = coordinates.time(time); DebuggerCoordinates withTime = coordinates.time(time);
Long found = traceManager.findSnapshot(withTime); Long found = traceManager.findSnapshot(withTime);
@ -198,7 +212,7 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
// TODO: Could still do it async on another thread, no? // TODO: Could still do it async on another thread, no?
// Not sure it buys anything, since program view will call .get on swing thread // Not sure it buys anything, since program view will call .get on swing thread
try { try {
emulationSerivce.emulate(coordinates.getTrace(), time, TaskMonitor.DUMMY); emulationSerivce.emulate(coordinates.getPlatform(), time, TaskMonitor.DUMMY);
} }
catch (CancelledException e) { catch (CancelledException e) {
throw new AssertionError(e); throw new AssertionError(e);

View file

@ -82,12 +82,15 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
protected static final int MAX_CACHE_SIZE = 5; protected static final int MAX_CACHE_SIZE = 5;
protected static class CacheKey implements Comparable<CacheKey> { protected static class CacheKey implements Comparable<CacheKey> {
// TODO: Should key on platform, not trace
protected final Trace trace; protected final Trace trace;
protected final TracePlatform platform;
protected final TraceSchedule time; protected final TraceSchedule time;
private final int hashCode; private final int hashCode;
public CacheKey(Trace trace, TraceSchedule time) { public CacheKey(TracePlatform platform, TraceSchedule time) {
this.trace = Objects.requireNonNull(trace); this.platform = Objects.requireNonNull(platform);
this.trace = platform.getTrace();
this.time = Objects.requireNonNull(time); this.time = Objects.requireNonNull(time);
this.hashCode = Objects.hash(trace, time); this.hashCode = Objects.hash(trace, time);
} }
@ -425,7 +428,8 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
} }
@Override @Override
public CompletableFuture<Long> backgroundEmulate(Trace trace, TraceSchedule time) { public CompletableFuture<Long> backgroundEmulate(TracePlatform platform, TraceSchedule time) {
Trace trace = platform.getTrace();
if (!traceManager.getOpenTraces().contains(trace)) { if (!traceManager.getOpenTraces().contains(trace)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Cannot emulate a trace unless it's opened in the tool."); "Cannot emulate a trace unless it's opened in the tool.");
@ -433,7 +437,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
if (time.isSnapOnly()) { if (time.isSnapOnly()) {
return CompletableFuture.completedFuture(time.getSnap()); return CompletableFuture.completedFuture(time.getSnap());
} }
return requests.get(new CacheKey(trace, time)); return requests.get(new CacheKey(platform, time));
} }
protected TraceSnapshot findScratch(Trace trace, TraceSchedule time) { protected TraceSnapshot findScratch(Trace trace, TraceSchedule time) {
@ -459,12 +463,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
protected long doEmulate(CacheKey key, TaskMonitor monitor) throws CancelledException { protected long doEmulate(CacheKey key, TaskMonitor monitor) throws CancelledException {
Trace trace = key.trace; Trace trace = key.trace;
/** TracePlatform platform = key.platform;
* TODO: object and/or platform should somehow be incorporated into the key, the schedule?
* something?
*/
DebuggerCoordinates current = traceManager.resolveTrace(trace);
TracePlatform platform = current.getPlatform();
TraceSchedule time = key.time; TraceSchedule time = key.time;
CachedEmulator ce; CachedEmulator ce;
@ -521,6 +520,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
return; return;
} }
// Cause object-register support to copy values into new register spaces // Cause object-register support to copy values into new register spaces
// TODO: I wish this were not necessary
monitor.setMessage("Creating register spaces"); monitor.setMessage("Creating register spaces");
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Prepare emulation")) { try (UndoableTransaction tid = UndoableTransaction.start(trace, "Prepare emulation")) {
for (TraceThread thread : time.getThreads(trace)) { for (TraceThread thread : time.getThreads(trace)) {
@ -530,8 +530,9 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
} }
@Override @Override
public long emulate(Trace trace, TraceSchedule time, TaskMonitor monitor) public long emulate(TracePlatform platform, TraceSchedule time, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
Trace trace = platform.getTrace();
if (!traceManager.getOpenTraces().contains(trace)) { if (!traceManager.getOpenTraces().contains(trace)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Cannot emulate a trace unless it's opened in the tool."); "Cannot emulate a trace unless it's opened in the tool.");
@ -539,12 +540,13 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
if (time.isSnapOnly()) { if (time.isSnapOnly()) {
return time.getSnap(); return time.getSnap();
} }
return doEmulate(new CacheKey(trace, time), monitor); return doEmulate(new CacheKey(platform, time), monitor);
} }
@Override @Override
public DebuggerPcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time) { public DebuggerPcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time) {
CachedEmulator ce = cache.get(new CacheKey(trace, time)); CachedEmulator ce =
cache.get(new CacheKey(trace.getPlatformManager().getHostPlatform(), time));
return ce == null ? null : ce.emulator; return ce == null ? null : ce.emulator;
} }

View file

@ -241,7 +241,7 @@ public enum ProgramEmulationUtils {
if (stack != null) { if (stack != null) {
CompilerSpec cSpec = trace.getBaseCompilerSpec(); CompilerSpec cSpec = trace.getBaseCompilerSpec();
Address sp = cSpec.stackGrowsNegative() Address sp = cSpec.stackGrowsNegative()
? stack.getMaxAddress() ? stack.getMaxAddress().addWrap(1)
: stack.getMinAddress(); : stack.getMinAddress();
Register regSP = cSpec.getStackPointer(); Register regSP = cSpec.getStackPointer();
if (regSP != null) { if (regSP != null) {

View file

@ -84,7 +84,7 @@ public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAcc
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);
} }
AddressSetView hostView = platform.mapGuestToHost(guestView); AddressSetView hostView = platform.mapGuestToHost(guestView);
return recorder.readMemoryBlocks(hostView, TaskMonitor.DUMMY, false) return recorder.readMemoryBlocks(hostView, TaskMonitor.DUMMY)
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents()) .thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
.thenCompose(__ -> recorder.flushTransactions()) .thenCompose(__ -> recorder.flushTransactions())
.thenAccept(__ -> platform.getTrace().flushEvents()) .thenAccept(__ -> platform.getTrace().flushEvents())

View file

@ -96,7 +96,7 @@ public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegist
toRead.add(register); toRead.add(register);
} }
} }
return recorder.captureThreadRegisters(thread, 0, toRead) return recorder.captureThreadRegisters(platform, thread, 0, toRead)
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents()) .thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
.thenCompose(__ -> recorder.flushTransactions()) .thenCompose(__ -> recorder.flushTransactions())
.thenAccept(__ -> platform.getTrace().flushEvents()) .thenAccept(__ -> platform.getTrace().flushEvents())
@ -108,7 +108,7 @@ public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegist
if (!isLive()) { if (!isLive()) {
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);
} }
return recorder.writeRegister(thread, frame, address.getPhysicalAddress(), data) return recorder.writeRegister(platform, thread, frame, address.getPhysicalAddress(), data)
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents()) .thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
.thenCompose(__ -> recorder.flushTransactions()) .thenCompose(__ -> recorder.flushTransactions())
.thenAccept(__ -> platform.getTrace().flushEvents()) .thenAccept(__ -> platform.getTrace().flushEvents())

View file

@ -46,9 +46,9 @@ public class DefaultMemoryRecorder implements ManagedMemoryRecorder {
this.memoryManager = trace.getMemoryManager(); this.memoryManager = trace.getMemoryManager();
} }
public CompletableFuture<NavigableMap<Address, byte[]>> captureProcessMemory(AddressSetView set, public CompletableFuture<Void> captureProcessMemory(AddressSetView set,
TaskMonitor monitor, boolean toMap) { TaskMonitor monitor) {
return RecorderUtils.INSTANCE.readMemoryBlocks(recorder, BLOCK_BITS, set, monitor, toMap); return RecorderUtils.INSTANCE.readMemoryBlocks(recorder, BLOCK_BITS, set, monitor);
} }
@Override @Override

View file

@ -42,6 +42,7 @@ import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.modules.TraceSection; import ghidra.trace.model.modules.TraceSection;
@ -268,12 +269,11 @@ public class DefaultTraceRecorder implements TraceRecorder {
/*---------------- CAPTURE METHODS -------------------*/ /*---------------- CAPTURE METHODS -------------------*/
@Override @Override
public CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks(AddressSetView set, public CompletableFuture<Void> readMemoryBlocks(AddressSetView set, TaskMonitor monitor) {
TaskMonitor monitor, boolean toMap) {
if (set.isEmpty()) { if (set.isEmpty()) {
return CompletableFuture.completedFuture(new TreeMap<>()); return AsyncUtils.NIL;
} }
return memoryRecorder.captureProcessMemory(set, monitor, toMap); return memoryRecorder.captureProcessMemory(set, monitor);
} }
@Override @Override
@ -315,11 +315,10 @@ public class DefaultTraceRecorder implements TraceRecorder {
} }
@Override @Override
public CompletableFuture<Map<Register, RegisterValue>> captureThreadRegisters( public CompletableFuture<Void> captureThreadRegisters(
TraceThread thread, int frameLevel, TracePlatform platform, TraceThread thread, int frameLevel, Set<Register> registers) {
Set<Register> registers) {
DefaultThreadRecorder rec = getThreadRecorder(thread); DefaultThreadRecorder rec = getThreadRecorder(thread);
return rec.captureThreadRegisters(thread, frameLevel, registers); return rec.captureThreadRegisters(thread, frameLevel, registers).thenApply(__ -> null);
} }
/*---------------- SNAPSHOT METHODS -------------------*/ /*---------------- SNAPSHOT METHODS -------------------*/
@ -537,8 +536,8 @@ public class DefaultTraceRecorder implements TraceRecorder {
} }
@Override @Override
public CompletableFuture<Void> writeThreadRegisters(TraceThread thread, int frameLevel, public CompletableFuture<Void> writeThreadRegisters(TracePlatform platform, TraceThread thread,
Map<Register, RegisterValue> values) { int frameLevel, Map<Register, RegisterValue> values) {
DefaultThreadRecorder rec = getThreadRecorder(thread); DefaultThreadRecorder rec = getThreadRecorder(thread);
return (rec == null) ? null : rec.writeThreadRegisters(frameLevel, values); return (rec == null) ? null : rec.writeThreadRegisters(frameLevel, values);
} }

View file

@ -521,10 +521,14 @@ public class TraceObjectManager {
} }
if (added.containsKey(TargetObject.VALUE_ATTRIBUTE_NAME)) { if (added.containsKey(TargetObject.VALUE_ATTRIBUTE_NAME)) {
TargetRegister register = (TargetRegister) parent; TargetRegister register = (TargetRegister) parent;
String valstr = (String) added.get(TargetObject.VALUE_ATTRIBUTE_NAME); Object val = added.get(TargetObject.VALUE_ATTRIBUTE_NAME);
byte[] value = new BigInteger(valstr, 16).toByteArray();
ManagedThreadRecorder rec = recorder.getThreadRecorderForSuccessor(register); ManagedThreadRecorder rec = recorder.getThreadRecorderForSuccessor(register);
rec.recordRegisterValue(register, value); if (val instanceof String valstr) {
rec.recordRegisterValue(register, new BigInteger(valstr, 16).toByteArray());
}
else if (val instanceof byte[] valarr) {
rec.recordRegisterValue(register, valarr);
}
} }
} }

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.core.debug.service.model.record; package ghidra.app.plugin.core.debug.service.model.record;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.math.BigInteger;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -27,6 +28,7 @@ import ghidra.app.plugin.core.debug.service.model.DebuggerModelServicePlugin;
import ghidra.app.plugin.core.debug.service.model.PermanentTransactionExecutor; import ghidra.app.plugin.core.debug.service.model.PermanentTransactionExecutor;
import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorder;
import ghidra.app.services.TraceRecorderListener; import ghidra.app.services.TraceRecorderListener;
import ghidra.async.AsyncFence;
import ghidra.async.AsyncUtils; import ghidra.async.AsyncUtils;
import ghidra.dbg.AnnotatedDebuggerAttributeListener; import ghidra.dbg.AnnotatedDebuggerAttributeListener;
import ghidra.dbg.error.DebuggerMemoryAccessException; import ghidra.dbg.error.DebuggerMemoryAccessException;
@ -34,13 +36,16 @@ import ghidra.dbg.error.DebuggerModelAccessException;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.dbg.target.TargetEventScope.TargetEventType; import ghidra.dbg.target.TargetEventScope.TargetEventType;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.dbg.util.PathMatcher;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.database.module.TraceObjectSection; import ghidra.trace.database.module.TraceObjectSection;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.breakpoint.*; import ghidra.trace.model.breakpoint.*;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.memory.TraceObjectMemoryRegion; import ghidra.trace.model.memory.TraceObjectMemoryRegion;
import ghidra.trace.model.modules.*; import ghidra.trace.model.modules.*;
@ -471,13 +476,13 @@ public class ObjectBasedTraceRecorder implements TraceRecorder {
@Override @Override
public boolean isRegisterBankAccessible(TargetRegisterBank bank) { public boolean isRegisterBankAccessible(TargetRegisterBank bank) {
// TODO: This seems a little aggressive, but the accessbility thing is already out of hand // TODO: This seems a little aggressive, but the accessibility thing is already out of hand
return true; return true;
} }
@Override @Override
public boolean isRegisterBankAccessible(TraceThread thread, int frameLevel) { public boolean isRegisterBankAccessible(TraceThread thread, int frameLevel) {
// TODO: This seems a little aggressive, but the accessbility thing is already out of hand // TODO: This seems a little aggressive, but the accessibility thing is already out of hand
return true; return true;
} }
@ -486,16 +491,88 @@ public class ObjectBasedTraceRecorder implements TraceRecorder {
return memoryRecorder.getAccessible(); return memoryRecorder.getAccessible();
} }
@Override protected TargetRegisterContainer getTargetRegisterContainer(TraceThread thread,
public CompletableFuture<Map<Register, RegisterValue>> captureThreadRegisters( int frameLevel) {
TraceThread thread, int frameLevel, Set<Register> registers) { if (!(thread instanceof TraceObjectThread tot)) {
return CompletableFuture.completedFuture(Map.of()); throw new AssertionError();
}
TraceObject objThread = tot.getObject();
TraceObject regContainer = objThread.queryRegisterContainer(frameLevel);
if (regContainer == null) {
Msg.error(this,
"No register container for " + thread + " and frame " + frameLevel + " in trace");
return null;
}
TargetObject result =
target.getModel().getModelObject(regContainer.getCanonicalPath().getKeyList());
if (result == null) {
Msg.error(this,
"No register container for " + thread + " and frame " + frameLevel + " on target");
return null;
}
return (TargetRegisterContainer) result;
} }
@Override @Override
public CompletableFuture<Void> writeThreadRegisters(TraceThread thread, int frameLevel, public CompletableFuture<Void> captureThreadRegisters(
Map<Register, RegisterValue> values) { TracePlatform platform, TraceThread thread, int frameLevel, Set<Register> registers) {
throw new UnsupportedOperationException(); TargetRegisterContainer regContainer = getTargetRegisterContainer(thread, frameLevel);
/**
* TODO: Seems I should be able to single out specific registers.... Is this convention
* universal, or do some models allow refreshing on a register-by-register basis? If so,
* what communicates that convention?
*/
if (regContainer == null) {
return AsyncUtils.NIL;
}
return regContainer.resync();
}
protected static byte[] encodeValue(int byteLength, BigInteger value) {
return Utils.bigIntegerToBytes(value, byteLength, true);
}
@Override
public CompletableFuture<Void> writeThreadRegisters(TracePlatform platform, TraceThread thread,
int frameLevel, Map<Register, RegisterValue> values) {
TargetRegisterContainer regContainer = getTargetRegisterContainer(thread, frameLevel);
if (regContainer == null) {
return AsyncUtils.NIL;
}
Map<TargetRegisterBank, Map<TargetRegister, byte[]>> writesByBank = new HashMap<>();
for (RegisterValue rv : values.values()) {
Register register = rv.getRegister();
PathMatcher matcher =
platform.getConventionalRegisterPath(regContainer.getSchema(), List.of(), register);
Collection<TargetObject> regs = matcher.getCachedSuccessors(regContainer).values();
if (regs.isEmpty()) {
Msg.warn(this, "No register object for " + register);
}
for (TargetObject objRegUntyped : regs) {
TargetRegister objReg = (TargetRegister) objRegUntyped;
List<String> pathBank = objReg.getModel()
.getRootSchema()
.searchForAncestor(TargetRegisterBank.class, objReg.getPath());
if (pathBank == null) {
Msg.warn(this, "No register bank for " + register);
continue;
}
TargetRegisterBank objBank =
(TargetRegisterBank) objReg.getModel().getModelObject(pathBank);
if (objBank == null) {
Msg.warn(this, "No register bank for " + register);
continue;
}
writesByBank.computeIfAbsent(objBank, __ -> new HashMap<>())
.put(objReg, encodeValue(objReg.getByteLength(), rv.getUnsignedValue()));
}
}
AsyncFence fence = new AsyncFence();
for (Map.Entry<TargetRegisterBank, Map<TargetRegister, byte[]>> ent : writesByBank
.entrySet()) {
fence.include(ent.getKey().writeRegisters(ent.getValue()));
}
return fence.ready();
} }
@Override @Override
@ -509,10 +586,8 @@ public class ObjectBasedTraceRecorder implements TraceRecorder {
} }
@Override @Override
public CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks( public CompletableFuture<Void> readMemoryBlocks(AddressSetView set, TaskMonitor monitor) {
AddressSetView set, TaskMonitor monitor, boolean returnResult) { return RecorderUtils.INSTANCE.readMemoryBlocks(this, BLOCK_BITS, set, monitor);
return RecorderUtils.INSTANCE.readMemoryBlocks(this, BLOCK_BITS, set, monitor,
returnResult);
} }
@Override @Override

View file

@ -15,8 +15,6 @@
*/ */
package ghidra.app.plugin.core.debug.service.model.record; package ghidra.app.plugin.core.debug.service.model.record;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorder;
@ -45,9 +43,8 @@ public enum RecorderUtils {
return result; return result;
} }
public CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks( public CompletableFuture<Void> readMemoryBlocks(
TraceRecorder recorder, int blockBits, AddressSetView set, TaskMonitor monitor, TraceRecorder recorder, int blockBits, AddressSetView set, TaskMonitor monitor) {
boolean returnResult) {
// NOTE: I don't intend to warn about the number of requests. // NOTE: I don't intend to warn about the number of requests.
// They're delivered in serial, and there's a cancel button that works // They're delivered in serial, and there's a cancel button that works
@ -61,7 +58,6 @@ public enum RecorderUtils {
monitor.initialize(total); monitor.initialize(total);
monitor.setMessage("Reading memory"); monitor.setMessage("Reading memory");
// TODO: Read blocks in parallel? Probably NO. Tends to overload the connector. // TODO: Read blocks in parallel? Probably NO. Tends to overload the connector.
NavigableMap<Address, byte[]> result = returnResult ? new TreeMap<>() : null;
return AsyncUtils.each(TypeSpec.VOID, expSet.iterator(), (r, loop) -> { return AsyncUtils.each(TypeSpec.VOID, expSet.iterator(), (r, loop) -> {
AddressRangeChunker blocks = new AddressRangeChunker(r, blockSize); AddressRangeChunker blocks = new AddressRangeChunker(r, blockSize);
AsyncUtils.each(TypeSpec.VOID, blocks.iterator(), (blk, inner) -> { AsyncUtils.each(TypeSpec.VOID, blocks.iterator(), (blk, inner) -> {
@ -69,15 +65,11 @@ public enum RecorderUtils {
monitor.incrementProgress(1); monitor.incrementProgress(1);
CompletableFuture<byte[]> future = CompletableFuture<byte[]> future =
recorder.readMemory(blk.getMinAddress(), (int) blk.getLength()); recorder.readMemory(blk.getMinAddress(), (int) blk.getLength());
future.thenAccept(data -> { future.exceptionally(e -> {
if (returnResult) {
result.put(blk.getMinAddress(), data);
}
}).exceptionally(e -> {
Msg.error(this, "Could not read " + blk + ": " + e); Msg.error(this, "Could not read " + blk + ": " + e);
return null; // Continue looping on errors return null; // Continue looping on errors
}).thenApply(__ -> !monitor.isCancelled()).handle(inner::repeatWhile); }).thenApply(__ -> !monitor.isCancelled()).handle(inner::repeatWhile);
}).thenApply(v -> !monitor.isCancelled()).handle(loop::repeatWhile); }).thenApply(v -> !monitor.isCancelled()).handle(loop::repeatWhile);
}).thenApply(__ -> result); });
} }
} }

View file

@ -651,7 +651,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
"Cannot navigate to coordinates with execution schedules, " + "Cannot navigate to coordinates with execution schedules, " +
"because the emulation service is not available."); "because the emulation service is not available.");
} }
return emulationService.backgroundEmulate(coordinates.getTrace(), coordinates.getTime()); return emulationService.backgroundEmulate(coordinates.getPlatform(), coordinates.getTime());
} }
protected CompletableFuture<Void> prepareViewAndFireEvent(DebuggerCoordinates coordinates) { protected CompletableFuture<Void> prepareViewAndFireEvent(DebuggerCoordinates coordinates) {

View file

@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture;
import ghidra.app.plugin.core.debug.service.emulation.*; import ghidra.app.plugin.core.debug.service.emulation.*;
import ghidra.framework.plugintool.ServiceInfo; import ghidra.framework.plugintool.ServiceInfo;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -83,13 +84,29 @@ public interface DebuggerEmulationService {
* costs. On the other hand, the service should be careful to invalidate cached results when the * costs. On the other hand, the service should be careful to invalidate cached results when the
* recorded machine state in a trace changes. * recorded machine state in a trace changes.
* *
* @param trace the trace containing the initial state * @param platform the trace platform containing the initial state
* @param time the time coordinates, including initial snap, steps, and p-code steps * @param time the time coordinates, including initial snap, steps, and p-code steps
* @param monitor a monitor for cancellation and progress reporting * @param monitor a monitor for cancellation and progress reporting
* @return the snap in the trace's scratch space where the realized state is stored * @return the snap in the trace's scratch space where the realized state is stored
* @throws CancelledException if the emulation is cancelled * @throws CancelledException if the emulation is cancelled
*/ */
long emulate(Trace trace, TraceSchedule time, TaskMonitor monitor) throws CancelledException; long emulate(TracePlatform platform, TraceSchedule time, TaskMonitor monitor)
throws CancelledException;
/**
* Emulate using the trace's "host" platform
*
* @see #emulate(TracePlatform, TraceSchedule, TaskMonitor)
* @param trace
* @param time
* @param monitor
* @return
* @throws CancelledException
*/
default long emulate(Trace trace, TraceSchedule time, TaskMonitor monitor)
throws CancelledException {
return emulate(trace.getPlatformManager().getHostPlatform(), time, monitor);
}
/** /**
* Invoke {@link #emulate(Trace, TraceSchedule, TaskMonitor)} in the background * Invoke {@link #emulate(Trace, TraceSchedule, TaskMonitor)} in the background
@ -97,15 +114,15 @@ public interface DebuggerEmulationService {
* <p> * <p>
* This is the preferred means of performing emulation. Because the underlying emulator may * This is the preferred means of performing emulation. Because the underlying emulator may
* request a <em>blocking</em> read from a target, it is important that * request a <em>blocking</em> read from a target, it is important that
* {@link #emulate(Trace, TraceSchedule, TaskMonitor)} is <em>never</em> called by the Swing * {@link #emulate(TracePlatform, TraceSchedule, TaskMonitor)} is <em>never</em> called by the
* thread. * Swing thread.
* *
* @param trace the trace containing the initial state * @param platform the trace platform containing the initial state
* @param time the time coordinates, including initial snap, steps, and p-code steps * @param time the time coordinates, including initial snap, steps, and p-code steps
* @return a future which completes with the result of * @return a future which completes with the result of
* {@link #emulate(Trace, TraceSchedule, TaskMonitor)} * {@link #emulate(TracePlatform, TraceSchedule, TaskMonitor)}
*/ */
CompletableFuture<Long> backgroundEmulate(Trace trace, TraceSchedule time); CompletableFuture<Long> backgroundEmulate(TracePlatform platform, TraceSchedule time);
/** /**
* The the cached emulator for the given trace and time * The the cached emulator for the given trace and time
@ -116,6 +133,9 @@ public interface DebuggerEmulationService {
* <p> * <p>
* <b>WARNING:</b> This emulator belongs to this service. Stepping it, or otherwise manipulating * <b>WARNING:</b> This emulator belongs to this service. Stepping it, or otherwise manipulating
* it without the service's knowledge can lead to unintended consequences. * it without the service's knowledge can lead to unintended consequences.
* <p>
* TODO: Should cache by (Platform, Time) instead, but need a way to distinguish platform in the
* trace's time table.
* *
* @param trace the trace containing the initial state * @param trace the trace containing the initial state
* @param time the time coordinates, including initial snap, steps, and p-code steps * @param time the time coordinates, including initial snap, steps, and p-code steps

View file

@ -91,9 +91,8 @@ public interface DebuggerStateEditingService {
default CompletableFuture<Void> setRegister(RegisterValue value) { default CompletableFuture<Void> setRegister(RegisterValue value) {
Register register = value.getRegister(); Register register = value.getRegister();
boolean isBigEndian = getCoordinates().getTrace().getBaseLanguage().isBigEndian();
byte[] bytes = Utils.bigIntegerToBytes(value.getUnsignedValue(), register.getNumBytes(), byte[] bytes = Utils.bigIntegerToBytes(value.getUnsignedValue(), register.getNumBytes(),
isBigEndian); register.isBigEndian());
return setVariable(register.getAddress(), bytes); return setVariable(register.getAddress(), bytes);
} }
} }
@ -115,6 +114,12 @@ public interface DebuggerStateEditingService {
StateEditor createStateEditor(DebuggerCoordinates coordinates); StateEditor createStateEditor(DebuggerCoordinates coordinates);
/**
* Create a state editor whose coordinates follow the trace manager for the given trace
*
* @param trace the trace to follow
* @return the editor
*/
StateEditor createStateEditor(Trace trace); StateEditor createStateEditor(Trace trace);
StateEditingMemoryHandler createStateEditor(TraceProgramView view); StateEditingMemoryHandler createStateEditor(TraceProgramView view);

View file

@ -31,6 +31,7 @@ import ghidra.program.model.lang.*;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.breakpoint.TraceBreakpoint; import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
@ -263,15 +264,16 @@ public interface TraceRecorder {
* Nevertheless, this method can force the retrieval of a given set of registers from the * Nevertheless, this method can force the retrieval of a given set of registers from the
* target. * target.
* *
* @param platform the platform whose language defines the registers
* @param thread the trace thread associated with the desired target thread * @param thread the trace thread associated with the desired target thread
* @param frameLevel the number of stack frames to "unwind", likely 0 * @param frameLevel the number of stack frames to "unwind", likely 0
* @param registers the <em>base</em> registers, as viewed by the trace * @param registers the <em>base</em> registers, as viewed by the trace
* @return a future which completes with the captured values * @return a future which completes when the commands succeed
* @throws IllegalArgumentException if no {@link TargetRegisterBank} is known for the given * @throws IllegalArgumentException if no {@link TargetRegisterBank} is known for the given
* thread * thread
*/ */
CompletableFuture<Map<Register, RegisterValue>> captureThreadRegisters(TraceThread thread, CompletableFuture<Void> captureThreadRegisters(TracePlatform platform,
int frameLevel, Set<Register> registers); TraceThread thread, int frameLevel, Set<Register> registers);
/** /**
* Write a target thread's registers. * Write a target thread's registers.
@ -280,6 +282,7 @@ public interface TraceRecorder {
* Note that the model and recorder should cause values successfully written on the target to be * Note that the model and recorder should cause values successfully written on the target to be
* updated in the trace. The caller should not update the trace out of band. * updated in the trace. The caller should not update the trace out of band.
* *
* @param platform the platform whose language defines the registers
* @param thread the trace thread associated with the desired target thread * @param thread the trace thread associated with the desired target thread
* @param frameLevel the number of stack frames to "unwind", likely 0 * @param frameLevel the number of stack frames to "unwind", likely 0
* @param values the values to write * @param values the values to write
@ -287,8 +290,8 @@ public interface TraceRecorder {
* @throws IllegalArgumentException if no {@link TargetRegisterBank} is known for the given * @throws IllegalArgumentException if no {@link TargetRegisterBank} is known for the given
* thread * thread
*/ */
CompletableFuture<Void> writeThreadRegisters(TraceThread thread, int frameLevel, CompletableFuture<Void> writeThreadRegisters(TracePlatform platform, TraceThread thread,
Map<Register, RegisterValue> values); int frameLevel, Map<Register, RegisterValue> values);
/** /**
* Read (and capture) a range of target memory * Read (and capture) a range of target memory
@ -320,20 +323,14 @@ public interface TraceRecorder {
* *
* <p> * <p>
* This task is relatively error tolerant. If a block or region cannot be captured -- a common * This task is relatively error tolerant. If a block or region cannot be captured -- a common
* occurrence -- the error is logged, but the task may still complete "successfully." For large * occurrence -- the error is logged, but the task may still complete "successfully."
* captures, it is recommended to set {@code returnResult} to false. The recorder will capture
* the bytes into the trace where they can be retrieved later. For small captures, and where
* bypassing the database may offer some advantage, set {@code returnResult} to true, and the
* captured bytes will be returned in an interval map. Connected intervals may or may not be
* joined.
* *
* @param set the addresses to capture, as viewed in the trace * @param set the addresses to capture, as viewed in the trace
* @param monitor a monitor for displaying task steps * @param monitor a monitor for displaying task steps
* @param returnResult true to complete with results, false to complete with null * @param returnResult true to complete with results, false to complete with null
* @return a future which completes when the task finishes * @return a future which completes when the task finishes
*/ */
CompletableFuture<NavigableMap<Address, byte[]>> readMemoryBlocks(AddressSetView set, CompletableFuture<Void> readMemoryBlocks(AddressSetView set, TaskMonitor monitor);
TaskMonitor monitor, boolean returnResult);
/** /**
* Write a variable (memory or register) of the given thread or the process * Write a variable (memory or register) of the given thread or the process
@ -350,13 +347,13 @@ public interface TraceRecorder {
* @param data the value to write * @param data the value to write
* @return a future which completes when the write is complete * @return a future which completes when the write is complete
*/ */
default CompletableFuture<Void> writeVariable(TraceThread thread, int frameLevel, default CompletableFuture<Void> writeVariable(TracePlatform platform, TraceThread thread,
Address address, byte[] data) { int frameLevel, Address address, byte[] data) {
if (address.isMemoryAddress()) { if (address.isMemoryAddress()) {
return writeMemory(address, data); return writeMemory(address, data);
} }
if (address.isRegisterAddress()) { if (address.isRegisterAddress()) {
return writeRegister(thread, frameLevel, address, data); return writeRegister(platform, thread, frameLevel, address, data);
} }
throw new IllegalArgumentException("Address is not in a recognized space: " + address); throw new IllegalArgumentException("Address is not in a recognized space: " + address);
} }
@ -370,21 +367,21 @@ public interface TraceRecorder {
* @param data the value to write * @param data the value to write
* @return a future which completes when the write is complete * @return a future which completes when the write is complete
*/ */
default CompletableFuture<Void> writeRegister(TraceThread thread, int frameLevel, default CompletableFuture<Void> writeRegister(TracePlatform platform, TraceThread thread,
Address address, byte[] data) { int frameLevel, Address address, byte[] data) {
Language lang = getTrace().getBaseLanguage(); Register register = platform.getLanguage().getRegister(address, data.length);
Register register = lang.getRegister(address, data.length);
if (register == null) { if (register == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Cannot identify the (single) register to write: " + address); "Cannot identify the (single) register to write: " + address);
} }
RegisterValue rv = new RegisterValue(register, RegisterValue rv = new RegisterValue(register,
Utils.bytesToBigInteger(data, data.length, lang.isBigEndian(), false)); Utils.bytesToBigInteger(data, data.length, register.isBigEndian(), false));
TraceMemorySpace regs = TraceMemorySpace regs =
getTrace().getMemoryManager().getMemoryRegisterSpace(thread, frameLevel, false); getTrace().getMemoryManager().getMemoryRegisterSpace(thread, frameLevel, false);
rv = TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, getSnap(), regs, true); rv = TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, platform, getSnap(), regs,
return writeThreadRegisters(thread, frameLevel, Map.of(rv.getRegister(), rv)); true);
return writeThreadRegisters(platform, thread, frameLevel, Map.of(rv.getRegister(), rv));
} }
/** /**

View file

@ -47,6 +47,7 @@ import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceLocation; import ghidra.trace.model.TraceLocation;
import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet; import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryOperations; import ghidra.trace.model.memory.TraceMemoryOperations;
import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
@ -158,7 +159,7 @@ public interface FlatDebuggerAPI {
* Get the current trace * Get the current trace
* *
* @see #getCurrentDebuggerCoordinates() * @see #getCurrentDebuggerCoordinates()
* @return the trace * @return the trace, or null
*/ */
default Trace getCurrentTrace() { default Trace getCurrentTrace() {
return getTraceManager().getCurrentTrace(); return getTraceManager().getCurrentTrace();
@ -192,6 +193,45 @@ public interface FlatDebuggerAPI {
return trace; return trace;
} }
/**
* Get the current trace platform
*
* @return the trace platform, or null
*/
default TracePlatform getCurrentPlatform() {
return getTraceManager().getCurrentPlatform();
}
/**
* Get the current trace platform, throwing an exception if there isn't one
*
* @return the trace platform
* @throws IllegalStateException if there is no current trace platform
*/
default TracePlatform requireCurrentPlatform() {
TracePlatform platform = getCurrentPlatform();
if (platform == null) {
// NB: Yes I've left off "platform"
// It's less confusing, and if there's a trace, there's always a platform
throw new IllegalStateException("There is no current trace");
}
return platform;
}
/**
* Require that the given platform is not null
*
* @param platform the platform
* @return the platform
* @throws IllegalStateException if the platform is null
*/
default TracePlatform requirePlatform(TracePlatform platform) {
if (platform == null) {
throw new IllegalStateException("There is no platform");
}
return platform;
}
/** /**
* Get the current thread * Get the current thread
* *
@ -594,6 +634,25 @@ public interface FlatDebuggerAPI {
return emulateLaunch(requireCurrentProgram(), address); return emulateLaunch(requireCurrentProgram(), address);
} }
/**
* Emulate the given trace platform as specified in the given schedule and display the result in
* the UI
*
* @param platform the trace platform
* @param time the schedule of steps
* @param monitor a monitor for the emulation
* @return true if successful
* @throws CancelledException if the user cancelled via the given monitor
*/
default boolean emulate(TracePlatform platform, TraceSchedule time, TaskMonitor monitor)
throws CancelledException {
// Use the script's thread to perform the actual emulation
getEmulationService().emulate(platform, time, monitor);
// This should just display the cached state
getTraceManager().activateTime(time);
return true;
}
/** /**
* Emulate the given trace as specified in the given schedule and display the result in the UI * Emulate the given trace as specified in the given schedule and display the result in the UI
* *
@ -605,11 +664,7 @@ public interface FlatDebuggerAPI {
*/ */
default boolean emulate(Trace trace, TraceSchedule time, TaskMonitor monitor) default boolean emulate(Trace trace, TraceSchedule time, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
// Use the script's thread to perform the actual emulation return emulate(trace.getPlatformManager().getHostPlatform(), time, monitor);
getEmulationService().emulate(trace, time, monitor);
// This should just display the cached state
getTraceManager().activateTime(time);
return true;
} }
/** /**
@ -622,7 +677,7 @@ public interface FlatDebuggerAPI {
* @throws IllegalStateException if there is no current trace * @throws IllegalStateException if there is no current trace
*/ */
default boolean emulate(TraceSchedule time, TaskMonitor monitor) throws CancelledException { default boolean emulate(TraceSchedule time, TaskMonitor monitor) throws CancelledException {
return emulate(requireCurrentTrace(), time, monitor); return emulate(requireCurrentPlatform(), time, monitor);
} }
/** /**
@ -636,13 +691,13 @@ public interface FlatDebuggerAPI {
*/ */
default boolean stepEmuInstruction(long count, TaskMonitor monitor) throws CancelledException { default boolean stepEmuInstruction(long count, TaskMonitor monitor) throws CancelledException {
DebuggerCoordinates current = getCurrentDebuggerCoordinates(); DebuggerCoordinates current = getCurrentDebuggerCoordinates();
Trace trace = requireCurrentTrace(); TracePlatform platform = requireCurrentPlatform();
TraceThread thread = current.getThread(); TraceThread thread = current.getThread();
TraceSchedule time = current.getTime(); TraceSchedule time = current.getTime();
TraceSchedule stepped = count <= 0 TraceSchedule stepped = count <= 0
? time.steppedBackward(trace, -count) ? time.steppedBackward(platform.getTrace(), -count)
: time.steppedForward(requireThread(thread), count); : time.steppedForward(requireThread(thread), count);
return emulate(trace, stepped, monitor); return emulate(platform, stepped, monitor);
} }
/** /**
@ -655,13 +710,13 @@ public interface FlatDebuggerAPI {
*/ */
default boolean stepEmuPcodeOp(int count, TaskMonitor monitor) throws CancelledException { default boolean stepEmuPcodeOp(int count, TaskMonitor monitor) throws CancelledException {
DebuggerCoordinates current = getCurrentDebuggerCoordinates(); DebuggerCoordinates current = getCurrentDebuggerCoordinates();
Trace trace = requireCurrentTrace(); TracePlatform platform = requireCurrentPlatform();
TraceThread thread = current.getThread(); TraceThread thread = current.getThread();
TraceSchedule time = current.getTime(); TraceSchedule time = current.getTime();
TraceSchedule stepped = count <= 0 TraceSchedule stepped = count <= 0
? time.steppedPcodeBackward(-count) ? time.steppedPcodeBackward(-count)
: time.steppedPcodeForward(requireThread(thread), count); : time.steppedPcodeForward(requireThread(thread), count);
return emulate(trace, stepped, monitor); return emulate(platform, stepped, monitor);
} }
/** /**
@ -678,13 +733,13 @@ public interface FlatDebuggerAPI {
*/ */
default boolean skipEmuInstruction(long count, TaskMonitor monitor) throws CancelledException { default boolean skipEmuInstruction(long count, TaskMonitor monitor) throws CancelledException {
DebuggerCoordinates current = getCurrentDebuggerCoordinates(); DebuggerCoordinates current = getCurrentDebuggerCoordinates();
Trace trace = requireCurrentTrace(); TracePlatform platform = requireCurrentPlatform();
TraceThread thread = current.getThread(); TraceThread thread = current.getThread();
TraceSchedule time = current.getTime(); TraceSchedule time = current.getTime();
TraceSchedule stepped = count <= 0 TraceSchedule stepped = count <= 0
? time.steppedBackward(trace, -count) ? time.steppedBackward(platform.getTrace(), -count)
: time.skippedForward(requireThread(thread), count); : time.skippedForward(requireThread(thread), count);
return emulate(trace, stepped, monitor); return emulate(platform, stepped, monitor);
} }
/** /**
@ -701,13 +756,13 @@ public interface FlatDebuggerAPI {
*/ */
default boolean skipEmuPcodeOp(int count, TaskMonitor monitor) throws CancelledException { default boolean skipEmuPcodeOp(int count, TaskMonitor monitor) throws CancelledException {
DebuggerCoordinates current = getCurrentDebuggerCoordinates(); DebuggerCoordinates current = getCurrentDebuggerCoordinates();
Trace trace = requireCurrentTrace(); TracePlatform platform = requireCurrentPlatform();
TraceThread thread = current.getThread(); TraceThread thread = current.getThread();
TraceSchedule time = current.getTime(); TraceSchedule time = current.getTime();
TraceSchedule stepped = count <= 0 TraceSchedule stepped = count <= 0
? time.steppedPcodeBackward(-count) ? time.steppedPcodeBackward(-count)
: time.skippedPcodeForward(requireThread(thread), count); : time.skippedPcodeForward(requireThread(thread), count);
return emulate(trace, stepped, monitor); return emulate(platform, stepped, monitor);
} }
/** /**
@ -720,11 +775,11 @@ public interface FlatDebuggerAPI {
*/ */
default boolean patchEmu(String sleigh, TaskMonitor monitor) throws CancelledException { default boolean patchEmu(String sleigh, TaskMonitor monitor) throws CancelledException {
DebuggerCoordinates current = getCurrentDebuggerCoordinates(); DebuggerCoordinates current = getCurrentDebuggerCoordinates();
Trace trace = requireCurrentTrace(); TracePlatform platform = requireCurrentPlatform();
TraceThread thread = current.getThread(); TraceThread thread = current.getThread();
TraceSchedule time = current.getTime(); TraceSchedule time = current.getTime();
TraceSchedule patched = time.patched(requireThread(thread), sleigh); TraceSchedule patched = time.patched(requireThread(thread), platform.getLanguage(), sleigh);
return emulate(trace, patched, monitor); return emulate(platform, patched, monitor);
} }
/** /**
@ -771,7 +826,7 @@ public interface FlatDebuggerAPI {
if (recorder.getSnap() != snap) { if (recorder.getSnap() != snap) {
return; return;
} }
waitOn(recorder.readMemoryBlocks(new AddressSet(safeRange(start, length)), monitor, false)); waitOn(recorder.readMemoryBlocks(new AddressSet(safeRange(start, length)), monitor));
waitOn(recorder.getTarget().getModel().flushEvents()); waitOn(recorder.getTarget().getModel().flushEvents());
waitOn(recorder.flushTransactions()); waitOn(recorder.flushTransactions());
trace.flushEvents(); trace.flushEvents();
@ -903,6 +958,7 @@ public interface FlatDebuggerAPI {
/** /**
* Copy registers from target to trace, if applicable and not already cached * Copy registers from target to trace, if applicable and not already cached
* *
* @param platform the platform whose language defines the registers
* @param thread the trace thread to update * @param thread the trace thread to update
* @param frame the frame level, 0 being the innermost * @param frame the frame level, 0 being the innermost
* @param snap the snap, to determine whether target values are applicable * @param snap the snap, to determine whether target values are applicable
@ -911,8 +967,8 @@ public interface FlatDebuggerAPI {
* @throws ExecutionException if an error occurs * @throws ExecutionException if an error occurs
* @throws TimeoutException if the operation times out * @throws TimeoutException if the operation times out
*/ */
default void refreshRegistersIfLive(TraceThread thread, int frame, long snap, default void refreshRegistersIfLive(TracePlatform platform, TraceThread thread, int frame,
Collection<Register> registers) long snap, Collection<Register> registers)
throws InterruptedException, ExecutionException, TimeoutException { throws InterruptedException, ExecutionException, TimeoutException {
Trace trace = thread.getTrace(); Trace trace = thread.getTrace();
TraceRecorder recorder = getModelService().getRecorder(trace); TraceRecorder recorder = getModelService().getRecorder(trace);
@ -924,7 +980,7 @@ public interface FlatDebuggerAPI {
} }
Set<Register> asSet = Set<Register> asSet =
registers instanceof Set<?> ? (Set<Register>) registers : Set.copyOf(registers); registers instanceof Set<?> ? (Set<Register>) registers : Set.copyOf(registers);
waitOn(recorder.captureThreadRegisters(thread, frame, asSet)); waitOn(recorder.captureThreadRegisters(platform, thread, frame, asSet));
waitOn(recorder.getTarget().getModel().flushEvents()); waitOn(recorder.getTarget().getModel().flushEvents());
waitOn(recorder.flushTransactions()); waitOn(recorder.flushTransactions());
trace.flushEvents(); trace.flushEvents();
@ -933,16 +989,17 @@ public interface FlatDebuggerAPI {
/** /**
* Read several registers from the given context, refreshing from target if needed * Read several registers from the given context, refreshing from target if needed
* *
* @param platform the platform whose language defines the registers
* @param thread the trace thread * @param thread the trace thread
* @param frame the source frame level, 0 being the innermost * @param frame the source frame level, 0 being the innermost
* @param snap the source snap * @param snap the source snap
* @param registers the source registers * @param registers the source registers
* @return the list of register values, or null on error * @return the list of register values, or null on error
*/ */
default List<RegisterValue> readRegisters(TraceThread thread, int frame, long snap, default List<RegisterValue> readRegisters(TracePlatform platform, TraceThread thread, int frame,
Collection<Register> registers) { long snap, Collection<Register> registers) {
try { try {
refreshRegistersIfLive(thread, frame, snap, registers); refreshRegistersIfLive(platform, thread, frame, snap, registers);
} }
catch (InterruptedException | ExecutionException | TimeoutException e) { catch (InterruptedException | ExecutionException | TimeoutException e) {
return null; return null;
@ -961,9 +1018,9 @@ public interface FlatDebuggerAPI {
* @see #readRegisters(TraceThread, int, long, Collection) * @see #readRegisters(TraceThread, int, long, Collection)
* @return the register's value, or null on error * @return the register's value, or null on error
*/ */
default RegisterValue readRegister(TraceThread thread, int frame, long snap, default RegisterValue readRegister(TracePlatform platform, TraceThread thread, int frame,
Register register) { long snap, Register register) {
List<RegisterValue> result = readRegisters(thread, frame, snap, Set.of(register)); List<RegisterValue> result = readRegisters(platform, thread, frame, snap, Set.of(register));
return result == null ? null : result.get(0); return result == null ? null : result.get(0);
} }
@ -974,8 +1031,8 @@ public interface FlatDebuggerAPI {
*/ */
default List<RegisterValue> readRegisters(Collection<Register> registers) { default List<RegisterValue> readRegisters(Collection<Register> registers) {
DebuggerCoordinates current = getCurrentDebuggerCoordinates(); DebuggerCoordinates current = getCurrentDebuggerCoordinates();
return readRegisters(requireThread(current.getThread()), current.getFrame(), return readRegisters(requireCurrentPlatform(), requireThread(current.getThread()),
current.getSnap(), registers); current.getFrame(), current.getSnap(), registers);
} }
/** /**
@ -1030,6 +1087,27 @@ public interface FlatDebuggerAPI {
return readRegisters(validateRegisterNames(requireCurrentTrace().getBaseLanguage(), names)); return readRegisters(validateRegisterNames(requireCurrentTrace().getBaseLanguage(), names));
} }
/**
* Read a register from the current context, refreshing from the target if needed
*
* @param platform the platform whose language defines the register
* @param register the register
* @return the value, or null on error
*/
default RegisterValue readRegister(TracePlatform platform, Register register) {
DebuggerCoordinates current = getCurrentDebuggerCoordinates();
if (platform.getTrace() != current.getTrace()) {
throw new IllegalArgumentException("Given platform is not from the current trace");
}
Language language = platform.getLanguage();
if (!register.equals(language.getRegister(register.getName()))) {
throw new IllegalArgumentException(
"Register " + register + " is not in language " + language);
}
return readRegister(platform, requireThread(current.getThread()), current.getFrame(),
current.getSnap(), register);
}
/** /**
* Read a register from the current context, refreshing from the target if needed * Read a register from the current context, refreshing from the target if needed
* *
@ -1037,9 +1115,7 @@ public interface FlatDebuggerAPI {
* @return the value, or null on error * @return the value, or null on error
*/ */
default RegisterValue readRegister(Register register) { default RegisterValue readRegister(Register register) {
DebuggerCoordinates current = getCurrentDebuggerCoordinates(); return readRegister(requireCurrentPlatform(), register);
return readRegister(requireThread(current.getThread()), current.getFrame(),
current.getSnap(), register);
} }
/** /**
@ -1049,7 +1125,9 @@ public interface FlatDebuggerAPI {
* @throws IllegalArgumentException if the name is invalid * @throws IllegalArgumentException if the name is invalid
*/ */
default RegisterValue readRegister(String name) { default RegisterValue readRegister(String name) {
return readRegister(validateRegisterName(requireCurrentTrace().getBaseLanguage(), name)); TracePlatform platform = requireCurrentPlatform();
Register register = validateRegisterName(platform.getLanguage(), name);
return readRegister(platform, register);
} }
/** /**
@ -1059,10 +1137,10 @@ public interface FlatDebuggerAPI {
* @return the program counter, or null if not known * @return the program counter, or null if not known
*/ */
default Address getProgramCounter(DebuggerCoordinates coordinates) { default Address getProgramCounter(DebuggerCoordinates coordinates) {
Language language = requireTrace(coordinates.getTrace()).getBaseLanguage(); TracePlatform platform = requirePlatform(coordinates.getPlatform());
RegisterValue value = Language language = platform.getLanguage();
readRegister(requireThread(coordinates.getThread()), coordinates.getFrame(), RegisterValue value = readRegister(platform, requireThread(coordinates.getThread()),
coordinates.getSnap(), language.getProgramCounter()); coordinates.getFrame(), coordinates.getSnap(), language.getProgramCounter());
if (!value.hasValue()) { if (!value.hasValue()) {
return null; return null;
} }
@ -1085,10 +1163,10 @@ public interface FlatDebuggerAPI {
* @return the stack pointer, or null if not known * @return the stack pointer, or null if not known
*/ */
default Address getStackPointer(DebuggerCoordinates coordinates) { default Address getStackPointer(DebuggerCoordinates coordinates) {
CompilerSpec cSpec = requireTrace(coordinates.getTrace()).getBaseCompilerSpec(); TracePlatform platform = requirePlatform(coordinates.getPlatform());
RegisterValue value = CompilerSpec cSpec = platform.getCompilerSpec();
readRegister(requireThread(coordinates.getThread()), coordinates.getFrame(), RegisterValue value = readRegister(platform, requireThread(coordinates.getThread()),
coordinates.getSnap(), cSpec.getStackPointer()); coordinates.getFrame(), coordinates.getSnap(), cSpec.getStackPointer());
if (!value.hasValue()) { if (!value.hasValue()) {
return null; return null;
} }

View file

@ -605,18 +605,27 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
modelService.addModel(mb.testModel); modelService.addModel(mb.testModel);
} }
protected TraceRecorder recordAndWaitSync() throws Throwable { protected void populateTestModel() throws Throwable {
createTestModel();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
mb.createTestThreadRegisterBanks();
// NOTE: Test mapper uses TOYBE64 // NOTE: Test mapper uses TOYBE64
mb.testProcess1.regs.addRegistersFromLanguage(getToyBE64Language(), mb.testProcess1.regs.addRegistersFromLanguage(getToyBE64Language(),
Register::isBaseRegister); Register::isBaseRegister);
mb.createTestThreadRegisterBanks();
mb.testProcess1.addRegion(".text", mb.rng(0x00400000, 0x00401000), "rx"); mb.testProcess1.addRegion(".text", mb.rng(0x00400000, 0x00401000), "rx");
mb.testProcess1.addRegion(".data", mb.rng(0x00600000, 0x00601000), "rw"); mb.testProcess1.addRegion(".data", mb.rng(0x00600000, 0x00601000), "rw");
}
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, protected TargetObject chooseTarget() {
createTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC); return mb.testProcess1;
}
protected TraceRecorder recordAndWaitSync() throws Throwable {
createTestModel();
populateTestModel();
TargetObject target = chooseTarget();
TraceRecorder recorder = modelService.recordTarget(target,
createTargetTraceMapper(target), ActionSource.AUTOMATIC);
waitRecorder(recorder); waitRecorder(recorder);
return recorder; return recorder;

View file

@ -1272,7 +1272,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
.createRegion(".text", 0, tb.range(0x00400000, 0x0040ffff), .createRegion(".text", 0, tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
thread1 = tb.getOrAddThread("Thread1", 0); thread1 = tb.getOrAddThread("Thread1", 0);
tb.exec(0, 0, thread1, "RIP = 0x00400000;"); tb.exec(0, thread1, 0, "RIP = 0x00400000;");
} }
TraceThread thread2; TraceThread thread2;
@ -1282,7 +1282,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
.createRegion(".text", 0, tb2.range(0x200, 0x3ff), TraceMemoryFlag.READ, .createRegion(".text", 0, tb2.range(0x200, 0x3ff), TraceMemoryFlag.READ,
TraceMemoryFlag.EXECUTE); TraceMemoryFlag.EXECUTE);
thread2 = tb2.getOrAddThread("Thread2", 0); thread2 = tb2.getOrAddThread("Thread2", 0);
tb2.exec(0, 0, thread2, "PC = 0x100;"); tb2.exec(0, thread2, 0, "PC = 0x100;");
} }
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);

View file

@ -0,0 +1,167 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.register;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.experimental.categories.Category;
import com.google.common.collect.Range;
import generic.test.category.NightlyCategory;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.mapping.DebuggerTargetTraceMapper;
import ghidra.app.plugin.core.debug.mapping.ObjectBasedDebuggerTargetTraceMapper;
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServicePlugin;
import ghidra.app.services.TraceRecorder;
import ghidra.dbg.target.TargetObject;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceCodeSpace;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.database.UndoableTransaction;
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
public class DebuggerRegistersProviderGuestTest extends DebuggerRegistersProviderTest {
protected TraceGuestPlatform toy;
@Override
protected void createTrace() throws IOException {
createTrace("DATA:BE:64:default");
}
public void createToyPlatform() throws Exception {
try (UndoableTransaction tid = tb.startTransaction()) {
toy = tb.trace.getPlatformManager()
.addGuestPlatform(getToyBE64Language().getDefaultCompilerSpec());
toy.addMappedRange(tb.addr(0), tb.addr(toy, 0), -1);
toy.addMappedRegisterRange();
}
}
@Before
@Override
public void setUpRegistersProviderTest() throws Exception {
registersPlugin = addPlugin(tool, DebuggerRegistersPlugin.class);
registersProvider = waitForComponentProvider(DebuggerRegistersProvider.class);
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
editingService = addPlugin(tool, DebuggerStateEditingServicePlugin.class);
createTrace();
createToyPlatform();
r0 = tb.reg(toy, "r0");
pc = toy.getLanguage().getProgramCounter();
sp = toy.getCompilerSpec().getStackPointer();
contextreg = toy.getLanguage().getContextBaseRegister();
pch = tb.reg(toy, "pch");
pcl = tb.reg(toy, "pcl");
r0h = tb.reg(toy, "r0h");
r0l = tb.reg(toy, "r0l");
r0Struct = new StructureDataType("r0_struct", 0);
r0Struct.add(SignedDWordDataType.dataType, "hi", "");
r0Struct.add(DWordDataType.dataType, "lo", "");
baseRegs = toy.getLanguage()
.getRegisters()
.stream()
.filter(Register::isBaseRegister)
.collect(Collectors.toSet());
}
@Override
protected TargetObject chooseTarget() {
return mb.testModel.session;
}
@Override
protected DebuggerTargetTraceMapper createTargetTraceMapper(TargetObject target)
throws Exception {
return new ObjectBasedDebuggerTargetTraceMapper(target,
new LanguageID("DATA:BE:64:default"), new CompilerSpecID("pointer64"), Set.of()) {
@Override
public TraceRecorder startRecording(DebuggerModelServicePlugin service,
Trace trace) {
useTrace(trace);
return super.startRecording(service, trace);
}
};
}
@Override
protected TraceRecorder recordAndWaitSync() throws Throwable {
TraceRecorder recorder = super.recordAndWaitSync();
createToyPlatform();
return recorder;
}
@Override
protected TracePlatform getPlatform() {
return toy;
}
@Override
protected void activateThread(TraceThread thread) {
traceManager.activate(traceManager.resolveThread(thread).platform(toy));
}
@Override
protected void addRegisterValues(TraceThread thread, UndoableTransaction tid) {
TraceMemorySpace regVals =
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
regVals.putBytes(toy, 0, pc, tb.buf(0, 0, 0, 0, 0, 0x40, 0, 0));
regVals.putBytes(toy, 0, sp, tb.buf(0x1f, 0, 0, 0, 0, 0, 0, 0));
regVals.putBytes(toy, 0, r0, tb.buf(1, 2, 3, 4, 5, 6, 7, 8));
}
@Override
protected void addRegisterTypes(TraceThread thread, UndoableTransaction tid)
throws CodeUnitInsertionException {
TraceCodeSpace regCode =
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
regCode.definedData().create(toy, Range.atLeast(0L), pc, PointerDataType.dataType);
// TODO: Pointer needs to be to ram, not register space
regCode.definedData().create(toy, Range.atLeast(0L), r0, r0Struct);
}
@Override
public void testDefaultSelection() throws Exception {
traceManager.openTrace(tb.trace);
TraceThread thread = addThread();
addRegisterValues(thread);
traceManager.activate(traceManager.resolveThread(thread).platform(toy));
waitForSwing();
assertEquals(DebuggerRegistersProvider.collectCommonRegisters(toy.getCompilerSpec()),
registersProvider.getSelectionFor(toy));
}
}

View file

@ -47,6 +47,7 @@ import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.listing.DBTraceCodeSpace; import ghidra.trace.database.listing.DBTraceCodeSpace;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.*; import ghidra.trace.model.listing.*;
import ghidra.trace.model.memory.TraceMemoryFlag; import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.memory.TraceMemorySpace;
@ -115,6 +116,14 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
} }
} }
protected TracePlatform getPlatform() {
return tb.host;
}
protected void activateThread(TraceThread thread) {
traceManager.activateThread(thread);
}
protected void addRegisterValues(TraceThread thread) { protected void addRegisterValues(TraceThread thread) {
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
addRegisterValues(thread, tid); addRegisterValues(thread, tid);
@ -252,7 +261,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
assertPCRowValueEmpty(); assertPCRowValueEmpty();
@ -269,12 +278,12 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
// TODO: Use another language to test effect of recorded non-common registers // TODO: Use another language to test effect of recorded non-common registers
TraceThread thread = addThread(); TraceThread thread = addThread();
addRegisterValues(thread); addRegisterValues(thread);
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
assertEquals( assertEquals(
DebuggerRegistersProvider.collectCommonRegisters(tb.trace.getBaseCompilerSpec()), DebuggerRegistersProvider.collectCommonRegisters(tb.trace.getBaseCompilerSpec()),
registersProvider.getSelectionFor(thread)); registersProvider.getSelectionFor(tb.host));
} }
@Test @Test
@ -283,7 +292,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
TraceThread thread = addThread(); TraceThread thread = addThread();
addRegisterValues(thread); addRegisterValues(thread);
traceManager.activateThread(thread); activateThread(thread);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
assertPCRowValuePopulated(); assertPCRowValuePopulated();
@ -299,7 +308,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
mb.testBank1.writeRegister("pc", new byte[] { 0x00, 0x40, 0x00, 0x00 }); mb.testBank1.writeRegister("pc", new byte[] { 0x00, 0x40, 0x00, 0x00 });
waitForSwing(); waitForSwing();
traceManager.activateThread(recorder.getTraceThread(mb.testThread1)); activateThread(recorder.getTraceThread(mb.testThread1));
waitForSwing(); waitForSwing();
assertPCRowValuePopulated(); assertPCRowValuePopulated();
@ -309,7 +318,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
public void testLiveActivateThenAddValuesPopulatesPanel() throws Throwable { public void testLiveActivateThenAddValuesPopulatesPanel() throws Throwable {
TraceRecorder recorder = recordAndWaitSync(); TraceRecorder recorder = recordAndWaitSync();
traceManager.openTrace(recorder.getTrace()); traceManager.openTrace(recorder.getTrace());
traceManager.activateThread(recorder.getTraceThread(mb.testThread1)); activateThread(recorder.getTraceThread(mb.testThread1));
waitForSwing(); waitForSwing();
mb.testBank1.writeRegister("pc", new byte[] { 0x00, 0x40, 0x00, 0x00 }); mb.testBank1.writeRegister("pc", new byte[] { 0x00, 0x40, 0x00, 0x00 });
@ -326,7 +335,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
addRegisterValues(thread); addRegisterValues(thread);
@ -343,7 +352,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
TraceThread thread = addThread(); TraceThread thread = addThread();
addRegisterValues(thread); addRegisterValues(thread);
addRegisterTypes(thread); addRegisterTypes(thread);
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
assertPCRowTypePopulated(); assertPCRowTypePopulated();
@ -355,7 +364,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
addRegisterValues(thread); addRegisterValues(thread);
addRegisterTypes(thread); addRegisterTypes(thread);
@ -373,7 +382,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
@ -395,7 +404,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
long viewSnap = traceManager.getCurrent().getViewSnap(); long viewSnap = traceManager.getCurrent().getViewSnap();
assertTrue(DBTraceUtils.isScratch(viewSnap)); assertTrue(DBTraceUtils.isScratch(viewSnap));
assertEquals(BigInteger.valueOf(0x1234), assertEquals(BigInteger.valueOf(0x1234),
regVals.getValue(viewSnap, r0).getUnsignedValue()); regVals.getValue(getPlatform(), viewSnap, r0).getUnsignedValue());
assertEquals(BigInteger.valueOf(0x1234), row.getValue()); assertEquals(BigInteger.valueOf(0x1234), row.getValue());
}); });
} }
@ -411,7 +420,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
@ -437,7 +446,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
long viewSnap = traceManager.getCurrent().getViewSnap(); long viewSnap = traceManager.getCurrent().getViewSnap();
assertTrue(DBTraceUtils.isScratch(viewSnap)); assertTrue(DBTraceUtils.isScratch(viewSnap));
assertEquals(BigInteger.valueOf(encodeDouble(1234)), assertEquals(BigInteger.valueOf(encodeDouble(1234)),
regVals.getValue(viewSnap, r0).getUnsignedValue()); regVals.getValue(getPlatform(), viewSnap, r0).getUnsignedValue());
assertEquals(BigInteger.valueOf(encodeDouble(1234)), row.getValue()); assertEquals(BigInteger.valueOf(encodeDouble(1234)), row.getValue());
}); });
} }
@ -449,7 +458,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
RegisterRow row = findRegisterRow(pc); RegisterRow row = findRegisterRow(pc);
@ -459,7 +468,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
DBTraceCodeSpace regCode = DBTraceCodeSpace regCode =
tb.trace.getCodeManager().getCodeRegisterSpace(thread, false); tb.trace.getCodeManager().getCodeRegisterSpace(thread, false);
assertNotNull(regCode); assertNotNull(regCode);
TraceData data = regCode.data().getForRegister(0L, pc); TraceData data = regCode.data().getForRegister(getPlatform(), 0L, pc);
assertTypeEquals(PointerDataType.dataType, data.getDataType()); assertTypeEquals(PointerDataType.dataType, data.getDataType());
} }
@ -469,9 +478,9 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
TraceThread thread = addThread(); TraceThread thread = addThread();
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
tb.exec(0, 0, thread, "pc = 100;"); tb.exec(getPlatform(), 0, thread, 0, "pc = 100;");
} }
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
RegisterRow row = findRegisterRow(pc); RegisterRow row = findRegisterRow(pc);
@ -481,7 +490,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
DBTraceCodeSpace regCode = DBTraceCodeSpace regCode =
tb.trace.getCodeManager().getCodeRegisterSpace(thread, false); tb.trace.getCodeManager().getCodeRegisterSpace(thread, false);
assertNotNull(regCode); assertNotNull(regCode);
TraceData data = regCode.data().getForRegister(0L, pc); TraceData data = regCode.data().getForRegister(getPlatform(), 0L, pc);
assertTypeEquals(LongLongDataType.dataType, data.getDataType()); assertTypeEquals(LongLongDataType.dataType, data.getDataType());
assertEquals("64h", row.getRepresentation()); assertEquals("64h", row.getRepresentation());
@ -505,7 +514,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
RegisterRow rowL = findRegisterRow(r0l); RegisterRow rowL = findRegisterRow(r0l);
@ -518,10 +527,11 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
tb.trace.getCodeManager().getCodeRegisterSpace(thread, false); tb.trace.getCodeManager().getCodeRegisterSpace(thread, false);
assertNotNull(regCode); assertNotNull(regCode);
// It's two units, not a struct with two components // It's two units, not a struct with two components
assertNull(regCode.data().getForRegister(0L, r0)); TracePlatform platform = getPlatform();
TraceData dataL = regCode.data().getForRegister(0L, r0l); assertNull(regCode.data().getForRegister(platform, 0L, r0));
TraceData dataL = regCode.data().getForRegister(platform, 0L, r0l);
assertTypeEquals(PointerDataType.dataType, dataL.getDataType()); assertTypeEquals(PointerDataType.dataType, dataL.getDataType());
TraceData dataH = regCode.data().getForRegister(0L, r0h); TraceData dataH = regCode.data().getForRegister(platform, 0L, r0h);
assertTypeEquals(PointerDataType.dataType, dataH.getDataType()); assertTypeEquals(PointerDataType.dataType, dataH.getDataType());
} }
@ -530,7 +540,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
// Group adds into a single transaction // Group adds into a single transaction
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
addRegisterValues(thread, tid); addRegisterValues(thread, tid);
@ -564,7 +574,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
addRegisterValues(thread, tid); addRegisterValues(thread, tid);
@ -595,7 +605,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
assertFalse(registersProvider.actionEnableEdits.isEnabled()); assertFalse(registersProvider.actionEnableEdits.isEnabled());
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
assertTrue(registersProvider.actionEnableEdits.isEnabled()); assertTrue(registersProvider.actionEnableEdits.isEnabled());
@ -613,7 +623,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
TraceThread thread = addThread(); TraceThread thread = addThread();
addRegisterValues(thread); addRegisterValues(thread);
traceManager.activateThread(thread); activateThread(thread);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
assertEquals(0, traceManager.getCurrentSnap()); assertEquals(0, traceManager.getCurrentSnap());
@ -625,8 +635,9 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true); tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
TraceCodeSpace regCode = TraceCodeSpace regCode =
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true); tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
regVals.putBytes(1, r0, tb.buf(1, 1, 2, 2, 3, 3, 4, 4)); TracePlatform platform = getPlatform();
regCode.definedData().create(Range.atLeast(1L), r0, r0Struct); regVals.putBytes(platform, 1, r0, tb.buf(1, 1, 2, 2, 3, 3, 4, 4));
regCode.definedData().create(platform, Range.atLeast(1L), r0, r0Struct);
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@ -641,7 +652,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
TraceThread thread = addThread(); TraceThread thread = addThread();
addRegisterValues(thread); addRegisterValues(thread);
addRegisterTypes(thread); addRegisterTypes(thread);
traceManager.activateThread(thread); activateThread(thread);
traceManager.activateSnap(1); traceManager.activateSnap(1);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@ -651,7 +662,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceCodeSpace regCode = TraceCodeSpace regCode =
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true); tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
TraceCodeUnit code = regCode.codeUnits().getContaining(1, r0); TraceCodeUnit code = regCode.codeUnits().getContaining(getPlatform(), 1, r0);
code.setEndSnap(0); code.setEndSnap(0);
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@ -673,7 +684,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
TraceThread thread = addThread(); TraceThread thread = addThread();
addRegisterValues(thread); addRegisterValues(thread);
addRegisterTypes(thread); addRegisterTypes(thread);
traceManager.activateThread(thread); activateThread(thread);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
assertR0RowTypePopulated(); assertR0RowTypePopulated();
@ -681,7 +692,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceCodeSpace regCode = TraceCodeSpace regCode =
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true); tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
TraceCodeUnit code = regCode.codeUnits().getContaining(1, r0); TraceCodeUnit code = regCode.codeUnits().getContaining(getPlatform(), 1, r0);
code.delete(); code.delete();
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@ -702,7 +713,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
assertFalse(registersProvider.actionCreateSnapshot.isEnabled()); assertFalse(registersProvider.actionCreateSnapshot.isEnabled());
traceManager.activateThread(thread1); activateThread(thread1);
waitForSwing(); waitForSwing();
assertTrue(registersProvider.actionCreateSnapshot.isEnabled()); assertTrue(registersProvider.actionCreateSnapshot.isEnabled());
@ -716,7 +727,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
assertEquals("[Registers]", cloned.getTitle()); assertEquals("[Registers]", cloned.getTitle());
assertEquals("Thread1", cloned.getSubTitle()); assertEquals("Thread1", cloned.getSubTitle());
traceManager.activateThread(thread2); activateThread(thread2);
waitForSwing(); waitForSwing();
assertEquals(thread2, registersProvider.current.getThread()); assertEquals(thread2, registersProvider.current.getThread());
@ -757,7 +768,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
// Ensure cause is goto PC, not register tracking // Ensure cause is goto PC, not register tracking
listingPlugin.setTrackingSpec( listingPlugin.setTrackingSpec(
LocationTrackingSpec.fromConfigName(NoneLocationTrackingSpec.CONFIG_NAME)); LocationTrackingSpec.fromConfigName(NoneLocationTrackingSpec.CONFIG_NAME));
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
assertPCRowTypePopulated(); assertPCRowTypePopulated();
@ -784,7 +795,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
assertFalse(registersProvider.actionSelectRegisters.isEnabled()); assertFalse(registersProvider.actionSelectRegisters.isEnabled());
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
assertTrue(registersProvider.regsTableModel.getRowIndex(findRegisterRow(pc)) >= 0); assertTrue(registersProvider.regsTableModel.getRowIndex(findRegisterRow(pc)) >= 0);
@ -823,21 +834,21 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
TraceThread thread1 = addThread(); TraceThread thread1 = addThread();
TraceThread thread2 = addThread("Thread2"); TraceThread thread2 = addThread("Thread2");
addRegisterValues(thread1); addRegisterValues(thread1);
traceManager.activateThread(thread2); activateThread(thread2);
waitForSwing(); waitForSwing();
assertEquals(thread2, registersProvider.current.getThread()); assertEquals(thread2, registersProvider.current.getThread());
assertPCRowValueEmpty(); assertPCRowValueEmpty();
// Should have no effect // Should have no effect
traceManager.activateThread(thread2); activateThread(thread2);
waitForSwing(); waitForSwing();
assertPCRowValueEmpty(); assertPCRowValueEmpty();
assertEquals(thread2, registersProvider.current.getThread()); assertEquals(thread2, registersProvider.current.getThread());
// Should have effect // Should have effect
traceManager.activateThread(thread1); activateThread(thread1);
waitForSwing(); waitForSwing();
assertEquals(thread1, registersProvider.current.getThread()); assertEquals(thread1, registersProvider.current.getThread());
@ -867,7 +878,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
assertPCRowValueEmpty(); assertPCRowValueEmpty();
// Should just work // Should just work
traceManager.activateThread(thread1); activateThread(thread1);
waitForSwing(); waitForSwing();
assertEquals(thread1, registersProvider.current.getThread()); assertEquals(thread1, registersProvider.current.getThread());
@ -880,7 +891,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
addRegisterValues(thread); addRegisterValues(thread);
@ -895,9 +906,9 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true); tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
TraceCodeSpace regCode = TraceCodeSpace regCode =
tb.trace.getCodeManager().getCodeRegisterSpace(thread, true); tb.trace.getCodeManager().getCodeRegisterSpace(thread, true);
regVals.putBytes(10, r0, tb.buf(0, 0, 0, 0, 0, 0, 0, 0)); regVals.putBytes(getPlatform(), 10, r0, tb.buf(0, 0, 0, 0, 0, 0, 0, 0));
// NB. the manager should have split the data unit at the value change // NB. the manager should have split the data unit at the value change
TraceCodeUnit cu = regCode.codeUnits().getContaining(10, r0); TraceCodeUnit cu = regCode.codeUnits().getContaining(getPlatform(), 10, r0);
assertNotNull(cu); assertNotNull(cu);
assertEquals(10, cu.getStartSnap()); assertEquals(10, cu.getStartSnap());
cu.delete(); cu.delete();
@ -922,7 +933,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
TraceThread thread = addThread(); TraceThread thread = addThread();
traceManager.activateThread(thread); activateThread(thread);
waitForSwing(); waitForSwing();
addRegisterValues(thread); addRegisterValues(thread);
@ -935,11 +946,12 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceMemorySpace regVals = TraceMemorySpace regVals =
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, 1, true); tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, 1, true);
regVals.putBytes(0, pc, tb.buf(0, 0, 0, 0, 0, 0x50, 0, 0)); regVals.putBytes(getPlatform(), 0, pc, tb.buf(0, 0, 0, 0, 0, 0x50, 0, 0));
TraceCodeSpace regCode = TraceCodeSpace regCode =
tb.trace.getCodeManager().getCodeRegisterSpace(thread, 1, true); tb.trace.getCodeManager().getCodeRegisterSpace(thread, 1, true);
regCode.definedData().create(Range.atLeast(0L), pc, QWordDataType.dataType); regCode.definedData()
.create(getPlatform(), Range.atLeast(0L), pc, QWordDataType.dataType);
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);

View file

@ -26,7 +26,7 @@ import org.junit.Test;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.services.ActionSource; import ghidra.app.services.ActionSource;
import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorder;
import ghidra.dbg.model.TestTargetRegister; import ghidra.dbg.model.*;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.dbg.util.CollectionUtils.Delta; import ghidra.dbg.util.CollectionUtils.Delta;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
@ -54,6 +54,18 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
} }
} }
protected static void assertSameRegister(TargetRegister expected, TargetObject actual) {
if (actual instanceof TestTargetRegister tr) {
assertEquals(expected, tr);
}
else if (actual instanceof TestTargetRegisterValue rv) {
assertEquals(expected, rv.desc);
}
else {
fail();
}
}
@Before @Before
public void setUpMapperTest() throws Throwable { public void setUpMapperTest() throws Throwable {
createTestModel(); createTestModel();
@ -98,8 +110,8 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
TestTargetRegister tEAX = TestTargetRegister tEAX =
Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("EAX")); Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("EAX"));
assertEquals(tRAX, waitForValue(() -> rm.getTargetRegister("rax"))); assertSameRegister(tRAX, waitForValue(() -> rm.getTargetRegister("rax")));
assertEquals(tEAX, waitForValue(() -> rm.getTargetRegister("eax"))); // Seems reasonable assertSameRegister(tEAX, waitForValue(() -> rm.getTargetRegister("eax"))); // Seems reasonable
} }
@Test @Test
@ -130,7 +142,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
Register lRAX = Objects.requireNonNull(getSLEIGH_X86_64_LANGUAGE().getRegister("RAX")); Register lRAX = Objects.requireNonNull(getSLEIGH_X86_64_LANGUAGE().getRegister("RAX"));
TargetRegister tReg = waitForValue(() -> rm.traceToTarget(lRAX)); TargetRegister tReg = waitForValue(() -> rm.traceToTarget(lRAX));
assertEquals(tRAX, tReg); assertSameRegister(tRAX, tReg);
} }
@Test @Test
@ -175,7 +187,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
TestTargetRegister tEAX = TestTargetRegister tEAX =
Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("EAX")); Objects.requireNonNull(mb.testProcess1.regs.getCachedElements().get("EAX"));
assertEquals(tEAX, waitForValue(() -> rm.getTargetRegister("eax"))); assertSameRegister(tEAX, waitForValue(() -> rm.getTargetRegister("eax")));
assertNull(rm.getTargetRegister("rax")); assertNull(rm.getTargetRegister("rax"));
} }
@ -211,7 +223,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
Register lRAX = Objects.requireNonNull(getSLEIGH_X86_64_LANGUAGE().getRegister("RAX")); Register lRAX = Objects.requireNonNull(getSLEIGH_X86_64_LANGUAGE().getRegister("RAX"));
TargetRegister tReg = waitForValue(() -> rm.traceToTarget(lRAX)); TargetRegister tReg = waitForValue(() -> rm.traceToTarget(lRAX));
assertEquals(tEAX, tReg); assertSameRegister(tEAX, tReg);
} }
@Test @Test
@ -256,7 +268,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
// NOTE: This is not allowed, but still generates a courtesy warning // NOTE: This is not allowed, but still generates a courtesy warning
assertNull(rm.targetToTrace("eax", genBytes4())); assertNull(rm.targetToTrace("eax", genBytes4()));
Delta<?, ?> delta = mb.testProcess1.regs.changeElements(List.of("RAX"), List.of(), "WoW64"); Delta<?, ?> delta = mb.testProcess1.regs.removeRegister(lRAX, "WoW64");
assertFalse(delta.removed.isEmpty()); assertFalse(delta.removed.isEmpty());
waitForPass(() -> assertNull(rm.getTargetRegister("rax"))); waitForPass(() -> assertNull(rm.getTargetRegister("rax")));
@ -287,7 +299,7 @@ public class LargestSubDebuggerRegisterMapperTest extends AbstractGhidraHeadedDe
assertEquals("RAX", ent.getKey()); assertEquals("RAX", ent.getKey());
assertArrayEquals(genBytes8(), ent.getValue()); assertArrayEquals(genBytes8(), ent.getValue());
Delta<?, ?> delta = mb.testProcess1.regs.changeElements(List.of("RAX"), List.of(), "WoW64"); Delta<?, ?> delta = mb.testProcess1.regs.removeRegister(lRAX, "WoW64");
assertFalse(delta.removed.isEmpty()); assertFalse(delta.removed.isEmpty());
waitForPass(() -> assertNull(rm.getTargetRegister("rax"))); waitForPass(() -> assertNull(rm.getTargetRegister("rax")));

View file

@ -0,0 +1,89 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.service.editing;
import java.io.IOException;
import java.util.Set;
import ghidra.app.plugin.core.debug.mapping.DebuggerTargetTraceMapper;
import ghidra.app.plugin.core.debug.mapping.ObjectBasedDebuggerTargetTraceMapper;
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServicePlugin;
import ghidra.app.services.TraceRecorder;
import ghidra.dbg.target.TargetObject;
import ghidra.program.model.lang.CompilerSpecID;
import ghidra.program.model.lang.LanguageID;
import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.util.database.UndoableTransaction;
public class DebuggerStateEditingServiceGuestTest extends DebuggerStateEditingServiceTest {
protected TraceGuestPlatform platform;
public void createToyPlatform() {
try (UndoableTransaction tid = tb.startTransaction()) {
platform = tb.trace.getPlatformManager()
.addGuestPlatform(getToyBE64Language().getDefaultCompilerSpec());
platform.addMappedRegisterRange();
platform.addMappedRange(tb.addr(0), tb.addr(platform, 0), -1);
}
catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
protected void createAndOpenTrace() throws IOException {
createAndOpenTrace("DATA:BE:64:default");
createToyPlatform();
}
@Override
protected void activateTrace() {
traceManager.activatePlatform(platform);
}
@Override
protected TracePlatform getPlatform() {
return platform;
}
@Override
protected TargetObject chooseTarget() {
return mb.testModel.session;
}
@Override
protected DebuggerTargetTraceMapper createTargetTraceMapper(TargetObject target)
throws Exception {
return new ObjectBasedDebuggerTargetTraceMapper(target,
new LanguageID("DATA:BE:64:default"), new CompilerSpecID("pointer64"), Set.of()) {
@Override
public TraceRecorder startRecording(DebuggerModelServicePlugin service,
Trace trace) {
useTrace(trace);
return super.startRecording(service, trace);
}
};
}
@Override
protected TraceRecorder recordAndWaitSync() throws Throwable {
TraceRecorder recorder = super.recordAndWaitSync();
createToyPlatform();
return recorder;
}
}

View file

@ -23,8 +23,7 @@ import java.nio.ByteBuffer;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import ghidra.app.plugin.assembler.Assembler; import ghidra.app.plugin.assembler.*;
import ghidra.app.plugin.assembler.Assemblers;
import ghidra.app.plugin.core.debug.DebuggerCoordinates; 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.services.DebuggerStateEditingService; import ghidra.app.services.DebuggerStateEditingService;
@ -32,24 +31,35 @@ import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
import ghidra.app.services.DebuggerStateEditingService.StateEditor; import ghidra.app.services.DebuggerStateEditingService.StateEditor;
import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorder;
import ghidra.dbg.target.TargetRegisterBank; import ghidra.dbg.target.TargetRegisterBank;
import ghidra.pcode.exec.DebuggerPcodeUtils;
import ghidra.pcode.exec.PcodeExecutor;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebuggerGUITest { public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebuggerGUITest {
private DebuggerStateEditingService editingService; protected DebuggerStateEditingService editingService;
private Register r0; protected Register r0;
private Register r0h; protected Register r0h;
private RegisterValue rv1234; protected RegisterValue rv1234;
private RegisterValue rv5678; protected RegisterValue rv5678;
private RegisterValue rvHigh1234; protected RegisterValue rvHigh1234;
protected StateEditor createStateEditor() {
return editingService.createStateEditor(tb.trace);
}
protected void activateTrace() {
traceManager.activateTrace(tb.trace);
}
protected TracePlatform getPlatform() {
return tb.trace.getPlatformManager().getHostPlatform();
}
@Before @Before
public void setUpEditorTest() throws Exception { public void setUpEditorTest() throws Exception {
@ -71,9 +81,11 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
* only work if they don't refer to any register. * only work if they don't refer to any register.
*/ */
createAndOpenTrace(); createAndOpenTrace();
activateTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4))); waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
} }
@ -82,7 +94,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
createAndOpenTrace(); createAndOpenTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
StateEditor editor = editingService.createStateEditor(tb.trace); activateTrace();
waitForSwing();
StateEditor editor = createStateEditor();
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
} }
@ -95,9 +110,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
// NB. TraceManager should automatically activate the first thread // NB. TraceManager should automatically activate the first thread
tb.getOrAddThread("Threads[0]", 0); tb.getOrAddThread("Threads[0]", 0);
} }
activateTrace();
waitForSwing(); waitForSwing();
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4))); waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
waitForSwing(); waitForSwing();
@ -120,9 +136,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
// NB. TraceManager should automatically activate the first thread // NB. TraceManager should automatically activate the first thread
thread = tb.getOrAddThread("Threads[0]", 0); thread = tb.getOrAddThread("Threads[0]", 0);
} }
activateTrace();
waitForSwing(); waitForSwing();
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
waitForSwing(); waitForSwing();
@ -131,7 +148,9 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
assertTrue(DBTraceUtils.isScratch(snap)); assertTrue(DBTraceUtils.isScratch(snap));
RegisterValue value = RegisterValue value =
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false).getValue(snap, r0); tb.trace.getMemoryManager()
.getMemoryRegisterSpace(thread, false)
.getValue(getPlatform(), snap, r0);
assertEquals(rv1234, value); assertEquals(rv1234, value);
} }
@ -143,14 +162,14 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
// NB. TraceManager should automatically activate the first thread // NB. TraceManager should automatically activate the first thread
TraceThread thread = tb.getOrAddThread("Threads[0]", 0); TraceThread thread = tb.getOrAddThread("Threads[0]", 0);
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates( Assembler asm = Assemblers.getAssembler(getPlatform().getLanguage());
env.getTool(), DebuggerCoordinates.NOWHERE.thread(thread)); AssemblyBuffer buf = new AssemblyBuffer(asm, tb.addr(getPlatform(), 0x00400000));
buf.assemble("imm r0,#123");
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0)); tb.trace.getMemoryManager()
asm.assemble(tb.addr(0x00400000), "imm r0,#123"); .putBytes(0, tb.addr(0x00400000), ByteBuffer.wrap(buf.getBytes()));
executor.executeSleigh("pc = 0x00400000;"); tb.exec(getPlatform(), 0, thread, 0, "pc = 0x00400000;");
} }
traceManager.activateTrace(tb.trace); activateTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
waitForSwing(); waitForSwing();
@ -158,7 +177,7 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
traceManager.activateTime(step1); traceManager.activateTime(step1);
waitForPass(() -> assertEquals(step1, traceManager.getCurrent().getTime())); waitForPass(() -> assertEquals(step1, traceManager.getCurrent().getTime()));
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setVariable(tb.addr(0x00600000), tb.arr(1, 2, 3, 4))); waitOn(editor.setVariable(tb.addr(0x00600000), tb.arr(1, 2, 3, 4)));
waitForSwing(); waitForSwing();
@ -181,14 +200,14 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
// NB. TraceManager should automatically activate the first thread // NB. TraceManager should automatically activate the first thread
thread = tb.getOrAddThread("Threads[0]", 0); thread = tb.getOrAddThread("Threads[0]", 0);
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates( Assembler asm = Assemblers.getAssembler(getPlatform().getLanguage());
env.getTool(), DebuggerCoordinates.NOWHERE.thread(thread)); AssemblyBuffer buf = new AssemblyBuffer(asm, tb.addr(getPlatform(), 0x00400000));
buf.assemble("imm r0,#123");
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0)); tb.trace.getMemoryManager()
asm.assemble(tb.addr(0x00400000), "imm r0,#123"); .putBytes(0, tb.addr(0x00400000), ByteBuffer.wrap(buf.getBytes()));
executor.executeSleigh("pc = 0x00400000;"); tb.exec(getPlatform(), 0, thread, 0, "pc = 0x00400000;");
} }
traceManager.activateTrace(tb.trace); activateTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
waitForSwing(); waitForSwing();
@ -196,7 +215,7 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
traceManager.activateTime(step1); traceManager.activateTime(step1);
waitForPass(() -> assertEquals(step1, traceManager.getCurrent().getTime())); waitForPass(() -> assertEquals(step1, traceManager.getCurrent().getTime()));
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
waitForSwing(); waitForSwing();
@ -205,8 +224,9 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
long snap = current.getViewSnap(); long snap = current.getViewSnap();
assertTrue(DBTraceUtils.isScratch(snap)); assertTrue(DBTraceUtils.isScratch(snap));
RegisterValue value = RegisterValue value = tb.trace.getMemoryManager()
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false).getValue(snap, r0); .getMemoryRegisterSpace(thread, false)
.getValue(getPlatform(), snap, r0);
assertEquals(rv1234, value); assertEquals(rv1234, value);
} }
@ -219,9 +239,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
// NB. TraceManager should automatically activate the first thread // NB. TraceManager should automatically activate the first thread
tb.getOrAddThread("Threads[0]", 0); tb.getOrAddThread("Threads[0]", 0);
} }
activateTrace();
waitForSwing(); waitForSwing();
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4))); waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
waitOn(editor.setVariable(tb.addr(0x00400002), tb.arr(5, 6, 7, 8))); waitOn(editor.setVariable(tb.addr(0x00400002), tb.arr(5, 6, 7, 8)));
waitForSwing(); waitForSwing();
@ -246,9 +267,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
// NB. TraceManager should automatically activate the first thread // NB. TraceManager should automatically activate the first thread
thread = tb.getOrAddThread("Threads[0]", 0); thread = tb.getOrAddThread("Threads[0]", 0);
} }
activateTrace();
waitForSwing(); waitForSwing();
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
waitOn(editor.setRegister(rv5678)); waitOn(editor.setRegister(rv5678));
waitForSwing(); waitForSwing();
@ -258,8 +280,9 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
assertTrue(DBTraceUtils.isScratch(snap)); assertTrue(DBTraceUtils.isScratch(snap));
assertEquals(1, current.getTime().patchCount()); // Check coalesced assertEquals(1, current.getTime().patchCount()); // Check coalesced
RegisterValue value = RegisterValue value = tb.trace.getMemoryManager()
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false).getValue(snap, r0); .getMemoryRegisterSpace(thread, false)
.getValue(getPlatform(), snap, r0);
assertEquals(rv5678, value); assertEquals(rv5678, value);
} }
@ -268,8 +291,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
// NB. Definitely no thread required // NB. Definitely no thread required
createAndOpenTrace(); createAndOpenTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE);
activateTrace();
waitForSwing();
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
// NB. Editor creates its own transaction // NB. Editor creates its own transaction
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4))); waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
waitForSwing(); waitForSwing();
@ -288,8 +313,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
// NB. Definitely no thread required // NB. Definitely no thread required
createAndOpenTrace(); createAndOpenTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE);
activateTrace();
waitForSwing();
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
// NB. Editor creates its own transaction // NB. Editor creates its own transaction
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
} }
@ -305,9 +332,10 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
// NB. TraceManager should automatically activate the first thread // NB. TraceManager should automatically activate the first thread
thread = tb.getOrAddThread("Threads[0]", 0); thread = tb.getOrAddThread("Threads[0]", 0);
} }
activateTrace();
waitForSwing(); waitForSwing();
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
// NB. Editor creates its own transaction // NB. Editor creates its own transaction
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
waitForSwing(); waitForSwing();
@ -316,20 +344,22 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
long snap = current.getViewSnap(); long snap = current.getViewSnap();
assertEquals(0, snap); assertEquals(0, snap);
RegisterValue value = RegisterValue value = tb.trace.getMemoryManager()
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false).getValue(snap, r0); .getMemoryRegisterSpace(thread, false)
.getValue(getPlatform(), snap, r0);
assertEquals(rv1234, value); assertEquals(rv1234, value);
} }
@Test @Test
public void testWriteTargetMemory() throws Throwable { public void testWriteTargetMemory() throws Throwable {
TraceRecorder recorder = recordAndWaitSync(); TraceRecorder recorder = recordAndWaitSync();
traceManager.openTrace(recorder.getTrace()); traceManager.openTrace(tb.trace);
activateTrace();
traceManager.activateThread(recorder.getTraceThread(mb.testThread1)); traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
waitForSwing(); waitForSwing();
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET); editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4))); waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
assertArrayEquals(mb.arr(1, 2, 3, 4), assertArrayEquals(mb.arr(1, 2, 3, 4),
@ -341,12 +371,14 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
TraceRecorder recorder = recordAndWaitSync(); TraceRecorder recorder = recordAndWaitSync();
TargetRegisterBank bank = TargetRegisterBank bank =
(TargetRegisterBank) mb.testThread1.getCachedAttribute("RegisterBank"); (TargetRegisterBank) mb.testThread1.getCachedAttribute("RegisterBank");
traceManager.openTrace(recorder.getTrace()); traceManager.openTrace(tb.trace);
activateTrace();
waitForSwing();
traceManager.activateThread(recorder.getTraceThread(mb.testThread1)); traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
waitForSwing(); waitForSwing();
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET); editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
assertArrayEquals(mb.arr(0, 0, 0, 0, 0, 0, 4, 0xd2), waitOn(bank.readRegister("r0"))); assertArrayEquals(mb.arr(0, 0, 0, 0, 0, 0, 4, 0xd2), waitOn(bank.readRegister("r0")));
@ -357,19 +389,20 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
TraceRecorder recorder = recordAndWaitSync(); TraceRecorder recorder = recordAndWaitSync();
TargetRegisterBank bank = TargetRegisterBank bank =
(TargetRegisterBank) mb.testThread1.getCachedAttribute("RegisterBank"); (TargetRegisterBank) mb.testThread1.getCachedAttribute("RegisterBank");
traceManager.openTrace(recorder.getTrace()); traceManager.openTrace(tb.trace);
activateTrace();
TraceThread thread = recorder.getTraceThread(mb.testThread1); TraceThread thread = recorder.getTraceThread(mb.testThread1);
traceManager.activateThread(thread); traceManager.activateThread(thread);
waitForSwing(); waitForSwing();
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET); editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
waitForPass(() -> { waitForPass(() -> {
TraceMemorySpace regs = TraceMemorySpace regs =
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false); tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
assertNotNull(regs); assertNotNull(regs);
RegisterValue value = regs.getValue(traceManager.getCurrentSnap(), r0); RegisterValue value = regs.getValue(getPlatform(), traceManager.getCurrentSnap(), r0);
assertEquals(rv1234, value); assertEquals(rv1234, value);
}); });
waitOn(editor.setRegister(rvHigh1234)); waitOn(editor.setRegister(rvHigh1234));
@ -380,64 +413,70 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
@Test(expected = MemoryAccessException.class) @Test(expected = MemoryAccessException.class)
public void testWriteTargetMemoryNotPresentErr() throws Throwable { public void testWriteTargetMemoryNotPresentErr() throws Throwable {
TraceRecorder recorder = recordAndWaitSync(); TraceRecorder recorder = recordAndWaitSync();
traceManager.openTrace(recorder.getTrace()); traceManager.openTrace(tb.trace);
activateTrace();
traceManager.activateThread(recorder.getTraceThread(mb.testThread1)); traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
waitForSwing(); waitForSwing();
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET); editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
traceManager.activateSnap(traceManager.getCurrentSnap() - 1); traceManager.activateSnap(traceManager.getCurrentSnap() - 1);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4))); waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
} }
@Test(expected = MemoryAccessException.class) @Test(expected = MemoryAccessException.class)
public void testWriteTargetRegisterNotPresentErr() throws Throwable { public void testWriteTargetRegisterNotPresentErr() throws Throwable {
TraceRecorder recorder = recordAndWaitSync(); TraceRecorder recorder = recordAndWaitSync();
traceManager.openTrace(recorder.getTrace()); traceManager.openTrace(tb.trace);
activateTrace();
traceManager.activateThread(recorder.getTraceThread(mb.testThread1)); traceManager.activateThread(recorder.getTraceThread(mb.testThread1));
waitForSwing(); waitForSwing();
editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET); editingService.setCurrentMode(recorder.getTrace(), StateEditingMode.WRITE_TARGET);
traceManager.activateSnap(traceManager.getCurrentSnap() - 1); traceManager.activateSnap(traceManager.getCurrentSnap() - 1);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
} }
@Test(expected = MemoryAccessException.class) @Test(expected = MemoryAccessException.class)
public void testWriteTargetMemoryNotAliveErr() throws Throwable { public void testWriteTargetMemoryNotAliveErr() throws Throwable {
createAndOpenTrace(); createAndOpenTrace();
activateTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TARGET); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TARGET);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4))); waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
} }
@Test(expected = MemoryAccessException.class) @Test(expected = MemoryAccessException.class)
public void testWriteTargetRegisterNotAliveErr() throws Throwable { public void testWriteTargetRegisterNotAliveErr() throws Throwable {
createAndOpenTrace(); createAndOpenTrace();
activateTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TARGET); editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TARGET);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
} }
@Test(expected = MemoryAccessException.class) @Test(expected = MemoryAccessException.class)
public void testWriteReadOnlyMemoryErr() throws Throwable { public void testWriteReadOnlyMemoryErr() throws Throwable {
createAndOpenTrace(); createAndOpenTrace();
activateTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.READ_ONLY); editingService.setCurrentMode(tb.trace, StateEditingMode.READ_ONLY);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4))); waitOn(editor.setVariable(tb.addr(0x00400000), tb.arr(1, 2, 3, 4)));
} }
@Test(expected = MemoryAccessException.class) @Test(expected = MemoryAccessException.class)
public void testWriteReadOnlyRegisterErr() throws Throwable { public void testWriteReadOnlyRegisterErr() throws Throwable {
createAndOpenTrace(); createAndOpenTrace();
activateTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.READ_ONLY); editingService.setCurrentMode(tb.trace, StateEditingMode.READ_ONLY);
StateEditor editor = editingService.createStateEditor(tb.trace); StateEditor editor = createStateEditor();
waitOn(editor.setRegister(rv1234)); waitOn(editor.setRegister(rv1234));
} }
} }

View file

@ -328,7 +328,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
} }
long scratch = long scratch =
emulationPlugin.emulate(tb.trace, TraceSchedule.parse("0:t0-1"), TaskMonitor.DUMMY); emulationPlugin.emulate(platform, TraceSchedule.parse("0:t0-1"), TaskMonitor.DUMMY);
TraceMemorySpace regs = mem.getMemoryRegisterSpace(thread, false); TraceMemorySpace regs = mem.getMemoryRegisterSpace(thread, false);
assertEquals("deadbeefcafebabe", assertEquals("deadbeefcafebabe",
regs.getViewValue(platform, scratch, tb.reg(platform, "RAX")) regs.getViewValue(platform, scratch, tb.reg(platform, "RAX"))

View file

@ -15,10 +15,10 @@
*/ */
package ghidra.app.plugin.core.debug.service.model.record; package ghidra.app.plugin.core.debug.service.model.record;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.oneOf;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
@ -39,6 +39,7 @@ import ghidra.dbg.target.TargetEventScope.TargetEventType;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRangeImpl; import ghidra.program.model.address.AddressRangeImpl;
import ghidra.program.model.lang.Language;
import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.breakpoint.*; import ghidra.trace.model.breakpoint.*;
@ -334,9 +335,9 @@ public class ObjectBasedTraceRecorderTest extends AbstractGhidraHeadedDebuggerGU
is(oneOf(null, TraceMemoryState.UNKNOWN))); is(oneOf(null, TraceMemoryState.UNKNOWN)));
byte[] data = new byte[10]; byte[] data = new byte[10];
assertNull(waitOn(recorder.readMemoryBlocks( waitOn(recorder.readMemoryBlocks(
tb.set(tb.range(0x00400123, 0x00400123), tb.range(0x00600ffe, 0x00601000)), tb.set(tb.range(0x00400123, 0x00400123), tb.range(0x00600ffe, 0x00601000)),
TaskMonitor.DUMMY, false))); TaskMonitor.DUMMY));
flushAndWait(); flushAndWait();
assertEquals(Set.of( assertEquals(Set.of(
stateEntry(0x00400000, 0x00400fff, TraceMemoryState.KNOWN), stateEntry(0x00400000, 0x00400fff, TraceMemoryState.KNOWN),
@ -493,15 +494,22 @@ public class ObjectBasedTraceRecorderTest extends AbstractGhidraHeadedDebuggerGU
public void testRecordRegisters() throws Throwable { public void testRecordRegisters() throws Throwable {
startRecording(); startRecording();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
// TODO: Adjust schema to reflect merging of container and bank
// TODO: Other bank placements. Will need different schemas, though :/ // TODO: Other bank placements. Will need different schemas, though :/
mb.createTestThreadRegisterBanks(); mb.createTestThreadRegisterBanks();
TestTargetRegisterValue targetPC = new TestTargetRegisterValue(mb.testBank1, "pc", true, Language toy = getToyBE64Language();
BigInteger.valueOf(0x00400123), 8); mb.testProcess1.regs.addRegister(toy.getRegister("pc"));
mb.testProcess1.regs.addRegister(toy.getRegister("r0"));
TestTargetRegisterValue targetPC =
(TestTargetRegisterValue) mb.testBank1.getCachedAttribute("pc");
targetPC.changeAttributes(List.of(),
Map.of(TargetRegister.VALUE_ATTRIBUTE_NAME, tb.arr(0, 0, 0, 0, 0, 0x40, 0x01, 0x23)),
"Write PC=0x00400123");
TestTargetRegisterValue targetR0 = TestTargetRegisterValue targetR0 =
new TestTargetRegisterValue(mb.testBank1, "r0", false, BigInteger.ZERO, 8); (TestTargetRegisterValue) mb.testBank1.getCachedAttribute("r0");
mb.testBank1.setElements(Set.of(targetPC, targetR0), "Test registers"); targetR0.changeAttributes(List.of(),
Map.of(TargetRegister.VALUE_ATTRIBUTE_NAME, tb.arr(0, 0, 0, 0, 0, 0, 0, 0)),
"Write R0=0x00000000");
flushAndWait(); flushAndWait();
TraceObjectThread thread = (TraceObjectThread) recorder.getTraceThread(mb.testThread1); TraceObjectThread thread = (TraceObjectThread) recorder.getTraceThread(mb.testThread1);
@ -517,18 +525,16 @@ public class ObjectBasedTraceRecorderTest extends AbstractGhidraHeadedDebuggerGU
.findAny() .findAny()
.orElseThrow(); .orElseThrow();
TraceObject pc = traceBank.getElement(recorder.getSnap(), "pc").getChild(); TraceObject pc = traceBank.getAttribute(recorder.getSnap(), "pc").getChild();
assertArrayEquals(tb.arr(0, 0, 0, 0, 0, 0x40, 0x01, 0x023), assertNotNull(pc);
assertArrayEquals(tb.arr(0, 0, 0, 0, 0, 0x40, 0x01, 0x23),
(byte[]) pc.getAttribute(recorder.getSnap(), TargetObject.VALUE_ATTRIBUTE_NAME) (byte[]) pc.getAttribute(recorder.getSnap(), TargetObject.VALUE_ATTRIBUTE_NAME)
.getValue()); .getValue());
TraceObject r0 = traceBank.getElement(recorder.getSnap(), "r0").getChild(); TraceObject r0 = traceBank.getAttribute(recorder.getSnap(), "r0").getChild();
assertNotNull(r0);
assertArrayEquals(tb.arr(0, 0, 0, 0, 0, 0, 0, 0), assertArrayEquals(tb.arr(0, 0, 0, 0, 0, 0, 0, 0),
(byte[]) r0.getAttribute(recorder.getSnap(), TargetObject.VALUE_ATTRIBUTE_NAME) (byte[]) r0.getAttribute(recorder.getSnap(), TargetObject.VALUE_ATTRIBUTE_NAME)
.getValue()); .getValue());
// TODO: Test interpretation, once mapping scheme is worked out
// TODO: How to annotate values with types, etc?
// TODO: Perhaps byte-array values are allocated in memory-like byte store?
// TODO: Brings endianness into the picture :/
} }
@Test @Test

View file

@ -31,7 +31,7 @@ import ghidra.dbg.util.PathUtils;
public interface TargetRegister extends TargetObject { public interface TargetRegister extends TargetObject {
String CONTAINER_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "container"; String CONTAINER_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "container";
String LENGTH_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "length"; String BIT_LENGTH_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "length";
/** /**
* Get the container of this register. * Get the container of this register.
@ -60,12 +60,25 @@ public interface TargetRegister extends TargetObject {
* @return the length of the register * @return the length of the register
*/ */
@TargetAttributeType( @TargetAttributeType(
name = LENGTH_ATTRIBUTE_NAME, name = BIT_LENGTH_ATTRIBUTE_NAME,
required = true, required = true,
fixed = true, fixed = true,
hidden = true) hidden = true)
default int getBitLength() { default int getBitLength() {
return getTypedAttributeNowByName(LENGTH_ATTRIBUTE_NAME, Integer.class, 0); return getTypedAttributeNowByName(BIT_LENGTH_ATTRIBUTE_NAME, Integer.class, 0);
}
/**
* Get the length, in bytes, of the register
*
* <p>
* For registers whose bit lengths are not a multiple of 8, this should be the minimum number of
* bytes required to bit all the bits, i.e., it should divide by 8 rounding up.
*
* @return the length of the register
*/
default int getByteLength() {
return (getBitLength() + 7) / 8;
} }
/** /**

View file

@ -317,12 +317,8 @@ public interface TargetObjectSchema {
* @return the named schema * @return the named schema
*/ */
default SchemaName getElementSchema(String index) { default SchemaName getElementSchema(String index) {
for (Entry<String, SchemaName> ent : getElementSchemas().entrySet()) { SchemaName schemaName = getElementSchemas().get(index);
if (ent.getKey().equals(index)) { return schemaName == null ? getDefaultElementSchema() : schemaName;
return ent.getValue();
}
}
return getDefaultElementSchema();
} }
/** /**
@ -365,12 +361,8 @@ public interface TargetObjectSchema {
* @return the attribute schema * @return the attribute schema
*/ */
default AttributeSchema getAttributeSchema(String name) { default AttributeSchema getAttributeSchema(String name) {
for (Entry<String, AttributeSchema> ent : getAttributeSchemas().entrySet()) { AttributeSchema attributeSchema = getAttributeSchemas().get(name);
if (ent.getKey().equals(name)) { return attributeSchema == null ? getDefaultAttributeSchema() : attributeSchema;
return ent.getValue();
}
}
return getDefaultAttributeSchema();
} }
/** /**
@ -935,46 +927,46 @@ public interface TargetObjectSchema {
* This places some conventional restrictions / expectations on models where registers are given * This places some conventional restrictions / expectations on models where registers are given
* on a frame-by-frame basis. The schema should present the {@link TargetRegisterContainer} as * on a frame-by-frame basis. The schema should present the {@link TargetRegisterContainer} as
* the same object or a successor to {@link TargetStackFrame}, which must in turn be a successor * the same object or a successor to {@link TargetStackFrame}, which must in turn be a successor
* to {@link TargetThread}. The frame level (usually an index) must be in the path from thread * to {@link TargetStack}. The frame level (an index) must be in the path from stack to frame.
* to stack frame. There can be no wild cards between the frame and the register container. For * There can be no wild cards between the frame and the register container. For example, the
* example, the container for {@code Threads[1]} may be {@code Threads[1].Stack[n].Registers}, * container for {@code Threads[1]} may be {@code Threads[1].Stack[n].Registers}, where
* where {@code n} is the frame level. {@code Threads[1]} would have the {@link TargetThread} * {@code n} is the frame level. {@code Threads[1].Stack} would have the {@link TargetStack}
* interface, {@code Threads[1].Stack[0]} would have the {@link TargetStackFrame} interface, and * interface, {@code Threads[1].Stack[0]} would have the {@link TargetStackFrame} interface, and
* {@code Threads[1].Stack[0].Registers} would have the {@link TargetRegisterContainer} * {@code Threads[1].Stack[0].Registers} would have the {@link TargetRegisterContainer}
* interface. Note it is not sufficient for {@link TargetRegisterContainer} to be a successor of * interface. Note it is not sufficient for {@link TargetRegisterContainer} to be a successor of
* {@link TargetThread} with a single index between. There <em>must</em> be an intervening * {@link TargetStack} with a single index between. There <em>must</em> be an intervening
* {@link TargetStackFrame}, and the frame level (index) must precede it. * {@link TargetStackFrame}, and the frame level (index) must precede it.
* *
* @param frameLevel the frameLevel, must be 0 if not applicable * @param frameLevel the frame level. May be ignored if not applicable
* @path the path of the seed object relative to the root * @path the path of the seed object relative to the root
* @return the predicates where the register container should be found, possibly empty * @return the predicates where the register container should be found, possibly empty
*/ */
default PathPredicates searchForRegisterContainer(int frameLevel, List<String> path) { default PathPredicates searchForRegisterContainer(int frameLevel, List<String> path) {
List<String> simple = searchForSuitable(TargetRegisterContainer.class, path); List<String> simple = searchForSuitable(TargetRegisterContainer.class, path);
if (simple != null) { if (simple != null) {
return frameLevel == 0 ? PathPredicates.pattern(simple) : PathPredicates.EMPTY; return PathPredicates.pattern(simple);
} }
List<String> threadPath = searchForAncestor(TargetThread.class, path); List<String> stackPath = searchForSuitable(TargetStack.class, path);
if (threadPath == null) { if (stackPath == null) {
return PathPredicates.EMPTY; return PathPredicates.EMPTY;
} }
PathPattern framePatternRelThread = PathPattern framePatternRelStack =
getSuccessorSchema(threadPath).searchFor(TargetStackFrame.class, false) getSuccessorSchema(stackPath).searchFor(TargetStackFrame.class, false)
.getSingletonPattern(); .getSingletonPattern();
if (framePatternRelThread == null) { if (framePatternRelStack == null) {
return PathPredicates.EMPTY; return PathPredicates.EMPTY;
} }
if (framePatternRelThread.countWildcards() != 1) { if (framePatternRelStack.countWildcards() != 1) {
return null; return null;
} }
PathMatcher result = new PathMatcher(); PathMatcher result = new PathMatcher();
for (String index : List.of(Integer.toString(frameLevel), for (String index : List.of(Integer.toString(frameLevel),
"0x" + Integer.toHexString(frameLevel))) { "0x" + Integer.toHexString(frameLevel))) {
List<String> framePathRelThread = List<String> framePathRelStack =
framePatternRelThread.applyKeys(index).getSingletonPath(); framePatternRelStack.applyKeys(index).getSingletonPath();
List<String> framePath = PathUtils.extend(threadPath, framePathRelThread); List<String> framePath = PathUtils.extend(stackPath, framePathRelStack);
List<String> regsPath = List<String> regsPath =
searchForSuitable(TargetRegisterContainer.class, framePath); searchForSuitable(TargetRegisterContainer.class, framePath);
if (regsPath != null) { if (regsPath != null) {
@ -983,4 +975,36 @@ public interface TargetObjectSchema {
} }
return result; return result;
} }
/**
* Compute the frame level of the object at the given path relative to this schema
*
* <p>
* If there is no {@link TargetStackFrame} in the path, this will return 0 since it is not
* applicable to the object. If there is a stack frame in the path, this will examine its
* ancestry, up to and excluding the {@link TargetStack} for an index. If there isn't a stack in
* the path, it is assumed to be an ancestor of this schema, meaning the examination will
* exhaust the ancestry provided in the path. If no index is found, an exception is thrown,
* because the frame level is applicable, but couldn't be computed from the path given. In that
* case, the client should include more ancestry in the path. Ideally, this is invoked relative
* to the root schema.
*
* @param path the path
* @return the frame level, or 0 if not applicable
* @throws IllegalArgumentException if frame level is applicable but not given in the path
*/
default int computeFrameLevel(List<String> path) {
List<String> framePath = searchForAncestor(TargetStackFrame.class, path);
if (framePath == null) {
return 0;
}
List<String> stackPath = searchForAncestor(TargetStack.class, framePath);
for (int i = stackPath == null ? 0 : stackPath.size(); i < framePath.size(); i++) {
String key = framePath.get(i);
if (PathUtils.isIndex(key)) {
return Integer.decode(PathUtils.parseIndex(key));
}
}
throw new IllegalArgumentException("No index between stack and frame");
}
} }

View file

@ -15,27 +15,35 @@
*/ */
package ghidra.dbg.model; package ghidra.dbg.model;
import java.math.BigInteger;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer; import java.util.function.Consumer;
import ghidra.dbg.error.DebuggerRegisterAccessException; import ghidra.dbg.error.DebuggerRegisterAccessException;
import ghidra.dbg.target.TargetRegisterBank; import ghidra.dbg.target.TargetRegisterBank;
import ghidra.dbg.target.TargetRegisterContainer;
import ghidra.dbg.util.PathUtils;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject> public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
extends DefaultTestTargetObject<TestTargetObject, P> implements TargetRegisterBank { extends DefaultTestTargetObject<TestTargetObject, P>
implements TargetRegisterBank, TargetRegisterContainer {
protected final TestTargetRegisterContainer regs; // TODO: Remove the separate descriptors idea
protected final TestTargetRegisterContainer descs;
public final Map<String, byte[]> regVals = new HashMap<>(); public final Map<String, byte[]> regVals = new HashMap<>();
public AbstractTestTargetRegisterBank(P parent, String name, String typeHint, public AbstractTestTargetRegisterBank(P parent, String name, String typeHint,
TestTargetRegisterContainer regs) { TestTargetRegisterContainer regs) {
super(parent, name, typeHint); super(parent, name, typeHint);
this.regs = regs; this.descs = regs;
this.descs.addBank(this);
changeAttributes(List.of(), Map.of( changeAttributes(List.of(), Map.of(
DESCRIPTIONS_ATTRIBUTE_NAME, regs // DESCRIPTIONS_ATTRIBUTE_NAME, this),
), "Initialized"); "Initialized");
initializeValues();
} }
public abstract TestTargetThread getThread(); public abstract TestTargetThread getThread();
@ -43,18 +51,19 @@ public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
@Override @Override
public CompletableFuture<? extends Map<String, byte[]>> readRegistersNamed( public CompletableFuture<? extends Map<String, byte[]>> readRegistersNamed(
Collection<String> names) { Collection<String> names) {
if (!regs.getDescs().keySet().containsAll(names)) { if (!descs.getDescs().keySet().containsAll(names)) {
throw new DebuggerRegisterAccessException("No such register"); throw new DebuggerRegisterAccessException("No such register");
} }
Map<String, byte[]> result = new LinkedHashMap<>(); Map<String, byte[]> result = new LinkedHashMap<>();
for (String n : names) { for (String n : names) {
byte[] v = regVals.get(n); byte[] v = regVals.get(n);
if (v == null) { if (v == null) {
v = regs.getDescs().get(n).defaultValue(); v = descs.getDescs().get(n).defaultValue();
} }
result.put(n, v); result.put(n, v);
} }
return model.gateFuture(regs.getModel().future(result).thenApply(__ -> { populateObjectValues(result, "Read registers");
return model.gateFuture(descs.getModel().future(result).thenApply(__ -> {
listeners.fire.registersUpdated(this, result); listeners.fire.registersUpdated(this, result);
return result; return result;
})); }));
@ -62,14 +71,14 @@ public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
protected CompletableFuture<Void> writeRegs(Map<String, byte[]> values, protected CompletableFuture<Void> writeRegs(Map<String, byte[]> values,
Consumer<Address> setPC) { Consumer<Address> setPC) {
if (!regs.getDescs().keySet().containsAll(values.keySet())) { if (!descs.getDescs().keySet().containsAll(values.keySet())) {
throw new DebuggerRegisterAccessException("No such register"); throw new DebuggerRegisterAccessException("No such register");
} }
Map<String, byte[]> updates = new LinkedHashMap<>(); Map<String, byte[]> updates = new LinkedHashMap<>();
CompletableFuture<Void> future = regs.getModel().future(null); CompletableFuture<Void> future = descs.getModel().future(null);
for (Map.Entry<String, byte[]> ent : values.entrySet()) { for (Map.Entry<String, byte[]> ent : values.entrySet()) {
String n = ent.getKey(); String n = ent.getKey();
TestTargetRegister desc = regs.getDescs().get(n); TestTargetRegister desc = descs.getDescs().get(n);
byte[] v = desc.normalizeValue(ent.getValue()); byte[] v = desc.normalizeValue(ent.getValue());
regVals.put(n, v); regVals.put(n, v);
updates.put(n, v); updates.put(n, v);
@ -79,12 +88,53 @@ public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
}); });
} }
} }
populateObjectValues(updates, "Write registers");
future.thenAccept(__ -> { future.thenAccept(__ -> {
listeners.fire.registersUpdated(this, updates); listeners.fire.registersUpdated(this, updates);
}); });
return model.gateFuture(future); return model.gateFuture(future);
} }
protected void addObjectValues(Collection<TestTargetRegister> descs, String reason) {
Set<TestTargetRegisterValue> objVals = new HashSet<>();
for (TestTargetRegister rd : descs) {
if (attributes.containsKey(rd.getName())) {
continue;
}
TestTargetRegisterValue tv = new TestTargetRegisterValue(this, rd, (BigInteger) null);
objVals.add(tv);
}
changeAttributes(List.of(), objVals, Map.of(), reason);
}
protected void removeObjectValues(Collection<TestTargetRegister> descs, String reason) {
List<String> toRemove = new ArrayList<>();
for (TestTargetRegister rd : descs) {
toRemove.add(PathUtils.parseIndex(rd.getName()));
}
changeAttributes(toRemove, Map.of(), reason);
}
protected void populateObjectValues(Map<String, byte[]> values, String reason) {
Set<TestTargetRegisterValue> objVals = new HashSet<>();
for (Map.Entry<String, byte[]> ent : values.entrySet()) {
TestTargetRegister rd = descs.getDescs().get(ent.getKey());
byte[] value = ent.getValue();
TestTargetRegisterValue tv = new TestTargetRegisterValue(this, rd,
value == null ? null : Utils.bytesToBigInteger(value, rd.byteLength, true, false));
objVals.add(tv);
}
changeAttributes(List.of(), objVals, Map.of(), reason);
}
protected void initializeValues() {
Map<String, byte[]> values = new HashMap<>();
for (TestTargetRegister desc : descs.getDescs().values()) {
values.put(desc.getIndex(), null);
}
populateObjectValues(values, "Populate");
}
public void setFromBank(AbstractTestTargetRegisterBank<?> bank) { public void setFromBank(AbstractTestTargetRegisterBank<?> bank) {
//Map<String, byte[]> updates = new HashMap<>(); //Map<String, byte[]> updates = new HashMap<>();
//updates.putAll(bank.regVals); //updates.putAll(bank.regVals);
@ -98,4 +148,12 @@ public abstract class AbstractTestTargetRegisterBank<P extends TestTargetObject>
kit.remove(); kit.remove();
} }
} }
public void addRegisterDescs(Collection<TestTargetRegister> added, String reason) {
addObjectValues(added, reason);
}
public void removeRegisterDescs(Collection<TestTargetRegister> removed, String reason) {
removeObjectValues(removed, reason);
}
} }

View file

@ -44,8 +44,8 @@ public class TestTargetRegister
changeAttributes(List.of(), Map.of( changeAttributes(List.of(), Map.of(
CONTAINER_ATTRIBUTE_NAME, parent, CONTAINER_ATTRIBUTE_NAME, parent,
LENGTH_ATTRIBUTE_NAME, byteLength // BIT_LENGTH_ATTRIBUTE_NAME, byteLength * 8),
), "Initialized"); "Initialized");
} }
public byte[] normalizeValue(byte[] value) { public byte[] normalizeValue(byte[] value) {

View file

@ -19,6 +19,7 @@ import java.util.*;
import java.util.function.Predicate; import java.util.function.Predicate;
import ghidra.dbg.target.TargetRegisterContainer; import ghidra.dbg.target.TargetRegisterContainer;
import ghidra.dbg.util.CollectionUtils.Delta;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
@ -26,6 +27,8 @@ public class TestTargetRegisterContainer
extends DefaultTestTargetObject<TestTargetRegister, TestTargetProcess> extends DefaultTestTargetObject<TestTargetRegister, TestTargetProcess>
implements TargetRegisterContainer { implements TargetRegisterContainer {
private final Set<AbstractTestTargetRegisterBank<?>> banks = new HashSet<>();
public TestTargetRegisterContainer(TestTargetProcess parent) { public TestTargetRegisterContainer(TestTargetProcess parent) {
super(parent, "Registers", "RegisterContainer"); super(parent, "Registers", "RegisterContainer");
} }
@ -43,13 +46,50 @@ public class TestTargetRegisterContainer
} }
add.add(getModel().newTestTargetRegister(this, register)); add.add(getModel().newTestTargetRegister(this, register));
} }
changeElements(List.of(), add, "Added registers from Ghidra language: " + language); String reason = "Added registers from Ghidra language: " + language;
changeElements(List.of(), add, reason);
List<AbstractTestTargetRegisterBank<?>> banks;
synchronized (this.banks) {
banks = List.copyOf(this.banks);
}
for (AbstractTestTargetRegisterBank<?> bank : banks) {
bank.addRegisterDescs(add, reason);
}
return add; return add;
} }
public TestTargetRegister addRegister(Register register) { public TestTargetRegister addRegister(Register register) {
TestTargetRegister tr = getModel().newTestTargetRegister(this, register); TestTargetRegister tr =
changeElements(List.of(), List.of(tr), "Added " + register + " from Ghidra language"); getModel().newTestTargetRegister(this, Objects.requireNonNull(register));
String reason = "Added " + register + " from Ghidra language";
changeElements(List.of(), List.of(tr), reason);
List<AbstractTestTargetRegisterBank<?>> banks;
synchronized (this.banks) {
banks = List.copyOf(this.banks);
}
for (AbstractTestTargetRegisterBank<?> bank : banks) {
bank.addRegisterDescs(List.of(tr), reason);
}
return tr; return tr;
} }
public Delta<TestTargetRegister, TestTargetRegister> removeRegister(Register register,
String reason) {
Delta<TestTargetRegister, TestTargetRegister> result =
changeElements(List.of(register.getName()), List.of(), reason);
List<AbstractTestTargetRegisterBank<?>> banks;
synchronized (this.banks) {
banks = List.copyOf(this.banks);
}
for (AbstractTestTargetRegisterBank<?> bank : banks) {
bank.removeRegisterDescs(result.removed.values(), reason);
}
return result;
}
public void addBank(AbstractTestTargetRegisterBank<?> bank) {
synchronized (this.banks) {
this.banks.add(bank);
}
}
} }

View file

@ -22,44 +22,42 @@ import java.util.Map;
import ghidra.dbg.target.TargetRegister; import ghidra.dbg.target.TargetRegister;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
import ghidra.pcode.utils.Utils; import ghidra.pcode.utils.Utils;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
public class TestTargetRegisterValue public class TestTargetRegisterValue
extends DefaultTestTargetObject<TestTargetObject, AbstractTestTargetRegisterBank<?>> extends DefaultTestTargetObject<TestTargetObject, AbstractTestTargetRegisterBank<?>>
implements TargetRegister { implements TargetRegister {
public static TestTargetRegisterValue fromRegisterValue( public final TestTargetRegister desc;
AbstractTestTargetRegisterBank<?> parent, RegisterValue rv) {
Register register = rv.getRegister(); public TestTargetRegisterValue(AbstractTestTargetRegisterBank<?> parent,
return new TestTargetRegisterValue(parent, PathUtils.makeKey(register.getName()), TestTargetRegister desc, BigInteger value) {
register.isProgramCounter(), rv.getUnsignedValue(), register.getBitLength() + 7 / 8); this(parent, desc,
value == null ? null : Utils.bigIntegerToBytes(value, desc.byteLength, true));
} }
protected final int byteLength; public TestTargetRegisterValue(AbstractTestTargetRegisterBank<?> parent,
protected final boolean isPC; TestTargetRegister desc, byte[] value) {
super(parent, PathUtils.parseIndex(desc.getName()), "Register");
this.desc = desc;
public TestTargetRegisterValue(AbstractTestTargetRegisterBank<?> parent, String name, if (value == null) {
boolean isPC, BigInteger value, int byteLength) { changeAttributes(List.of(), Map.of(
this(parent, name, isPC, Utils.bigIntegerToBytes(value, byteLength, true)); CONTAINER_ATTRIBUTE_NAME, parent,
} BIT_LENGTH_ATTRIBUTE_NAME, desc.byteLength * 8),
"Populated");
public TestTargetRegisterValue(AbstractTestTargetRegisterBank<?> parent, String name, }
boolean isPC, byte[] value) { else {
super(parent, name, "Register"); changeAttributes(List.of(), Map.of(
this.byteLength = value.length; CONTAINER_ATTRIBUTE_NAME, parent,
this.isPC = isPC; BIT_LENGTH_ATTRIBUTE_NAME, desc.byteLength * 8,
VALUE_ATTRIBUTE_NAME, value),
changeAttributes(List.of(), Map.of( "Initialized");
CONTAINER_ATTRIBUTE_NAME, parent, }
LENGTH_ATTRIBUTE_NAME, byteLength,
VALUE_ATTRIBUTE_NAME, value //
), "Initialized");
} }
public void setValue(BigInteger value) { public void setValue(BigInteger value) {
changeAttributes(List.of(), Map.of( changeAttributes(List.of(), Map.of(
VALUE_ATTRIBUTE_NAME, Utils.bigIntegerToBytes(value, byteLength, true) // VALUE_ATTRIBUTE_NAME, Utils.bigIntegerToBytes(value, desc.byteLength, true)),
), "Set value"); "Set value");
} }
} }

View file

@ -25,8 +25,8 @@ import ghidra.dbg.util.PathUtils;
public class TestTargetThread public class TestTargetThread
extends DefaultTestTargetObject<TestTargetObject, TestTargetThreadContainer> extends DefaultTestTargetObject<TestTargetObject, TestTargetThreadContainer>
implements TargetThread, TargetExecutionStateful, TargetSteppable, TargetResumable, implements TargetThread, TargetAggregate, TargetExecutionStateful, TargetSteppable,
TargetInterruptible, TargetKillable { TargetResumable, TargetInterruptible, TargetKillable {
public static final TargetStepKindSet SUPPORTED_KINDS = public static final TargetStepKindSet SUPPORTED_KINDS =
TargetStepKindSet.of(TargetStepKind.values()); TargetStepKindSet.of(TargetStepKind.values());

View file

@ -94,7 +94,7 @@
<attribute name="Breakpoints" schema="BreakpointContainer" required="yes" fixed="yes" /> <attribute name="Breakpoints" schema="BreakpointContainer" required="yes" fixed="yes" />
<attribute name="Memory" schema="Memory" required="yes" fixed="yes" /> <attribute name="Memory" schema="Memory" required="yes" fixed="yes" />
<attribute name="Modules" schema="ModuleContainer" required="yes" fixed="yes" /> <attribute name="Modules" schema="ModuleContainer" required="yes" fixed="yes" />
<attribute name="Registers" schema="RegisterContainer" required="yes" fixed="yes" /> <!-- <attribute name="Registers" schema="RegisterContainer" required="yes" fixed="yes" /> -->
<attribute name="Threads" schema="ThreadContainer" required="yes" fixed="yes" /> <attribute name="Threads" schema="ThreadContainer" required="yes" fixed="yes" />
<attribute name="Devices" schema="OBJECT" /> <attribute name="Devices" schema="OBJECT" />
<attribute name="Environment" schema="OBJECT" /> <attribute name="Environment" schema="OBJECT" />
@ -171,6 +171,7 @@
</schema> </schema>
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER"> <schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
<interface name="Thread" /> <interface name="Thread" />
<interface name="Aggregate" />
<interface name="ExecutionStateful" /> <interface name="ExecutionStateful" />
<interface name="Steppable" /> <interface name="Steppable" />
<interface name="Resumable" /> <interface name="Resumable" />
@ -269,7 +270,7 @@
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" /> <attribute schema="ANY" />
</schema> </schema>
<schema name="RegisterContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER"> <schema name="RegisterContainer" elementResync="NEVER" attributeResync="NEVER">
<interface name="RegisterContainer" /> <interface name="RegisterContainer" />
<element schema="RegisterDescriptor" /> <element schema="RegisterDescriptor" />
<attribute name="_descriptions" schema="RegisterContainer" /> <attribute name="_descriptions" schema="RegisterContainer" />
@ -280,12 +281,13 @@
<attribute name="_value" schema="ANY" hidden="yes" /> <attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" /> <attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema> </schema>
<schema name="RegisterBank" elementResync="NEVER" attributeResync="NEVER"> <schema name="RegisterBank" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="RegisterBank" /> <interface name="RegisterBank" />
<interface name="RegisterContainer" />
<!-- NB: registers are attributes, not elements here --> <!-- NB: registers are attributes, not elements here -->
<element schema="RegisterDescriptor" /> <element schema="VOID" />
<attribute schema="RegisterValue" />
<attribute name="_descriptions" schema="RegisterContainer" /> <attribute name="_descriptions" schema="RegisterContainer" />
<attribute name="_modified" schema="BOOL" hidden="yes" /> <attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" /> <attribute name="_display" schema="STRING" hidden="yes" />
@ -294,9 +296,9 @@
<attribute name="_value" schema="ANY" hidden="yes" /> <attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" /> <attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="OBJECT" />
</schema> </schema>
<schema name="Stack" canonical="yes" elementResync="NEVER" attributeResync="NEVER"> <schema name="Stack" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Stack" />
<element schema="StackFrame" /> <element schema="StackFrame" />
<attribute name="_modified" schema="BOOL" hidden="yes" /> <attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" /> <attribute name="_display" schema="STRING" hidden="yes" />
@ -394,4 +396,18 @@
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" /> <attribute schema="VOID" />
</schema> </schema>
</context> <schema name="RegisterValue" elementResync="NEVER" attributeResync="NEVER">
<interface name="Register" />
<element schema="VOID" />
<attribute name="_length" schema="INT" fixed="yes" hidden="yes" />
<attribute name="_container" schema="RegisterContainer" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" required="yes" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
</context>

View file

@ -25,6 +25,7 @@ import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.property.TracePropertyMap; import ghidra.trace.model.property.TracePropertyMap;
import ghidra.trace.model.property.TracePropertyMapSpace; import ghidra.trace.model.property.TracePropertyMapSpace;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceRegisterUtils;
import ghidra.trace.util.TraceTimeViewport; import ghidra.trace.util.TraceTimeViewport;
/** /**
@ -128,9 +129,7 @@ public class DefaultPcodeTraceRegistersAccess extends AbstractPcodeTraceDataAcce
return null; // client should bail anyway return null; // client should bail anyway
} }
AddressSpace space = ops.getAddressSpace(); AddressSpace space = ops.getAddressSpace();
return new AddressRangeImpl( return TraceRegisterUtils.getOverlayRange(space, range);
space.getOverlayAddress(range.getMinAddress()),
space.getOverlayAddress(range.getMaxAddress()));
} }
@Override @Override
@ -140,12 +139,6 @@ public class DefaultPcodeTraceRegistersAccess extends AbstractPcodeTraceDataAcce
return null; // client should bail anyway return null; // client should bail anyway
} }
AddressSpace space = ops.getAddressSpace(); AddressSpace space = ops.getAddressSpace();
AddressSet result = new AddressSet(); return TraceRegisterUtils.getOverlaySet(space, set);
for (AddressRange rng : set) {
result.add(
space.getOverlayAddress(rng.getMinAddress()),
space.getOverlayAddress(rng.getMaxAddress()));
}
return result;
} }
} }

View file

@ -227,6 +227,20 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
} }
} }
protected AddressSpace doCreateOverlaySpace(String name, AddressSpace base) {
TraceAddressFactory factory = trace.getInternalAddressFactory();
OverlayAddressSpace space =
factory.addOverlayAddressSpace(name, true, base, base.getMinAddress().getOffset(),
base.getMaxAddress().getOffset());
// Only if it succeeds do we store the record
DBTraceOverlaySpaceEntry ent = overlayStore.create();
ent.set(space.getName(), base.getName());
trace.updateViewsAddSpaceBlock(space);
trace.setChanged(new TraceChangeRecord<>(TraceOverlaySpaceChangeType.ADDED, null,
trace, null, space));
return space;
}
public AddressSpace createOverlayAddressSpace(String name, AddressSpace base) public AddressSpace createOverlayAddressSpace(String name, AddressSpace base)
throws DuplicateNameException { throws DuplicateNameException {
// TODO: Exclusive lock? // TODO: Exclusive lock?
@ -235,17 +249,19 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
if (factory.getAddressSpace(name) != null) { if (factory.getAddressSpace(name) != null) {
throw new DuplicateNameException("Address space " + name + " already exists."); throw new DuplicateNameException("Address space " + name + " already exists.");
} }
return doCreateOverlaySpace(name, base);
}
}
OverlayAddressSpace space = public AddressSpace getOrCreateOverlayAddressSpace(String name, AddressSpace base) {
factory.addOverlayAddressSpace(name, true, base, base.getMinAddress().getOffset(), // TODO: Exclusive lock?
base.getMaxAddress().getOffset()); try (LockHold hold = LockHold.lock(lock.writeLock())) {
// Only if it succeeds do we store the record TraceAddressFactory factory = trace.getInternalAddressFactory();
DBTraceOverlaySpaceEntry ent = overlayStore.create(); AddressSpace space = factory.getAddressSpace(name);
ent.set(space.getName(), base.getName()); if (space != null) {
trace.updateViewsAddSpaceBlock(space); return space.getPhysicalSpace() == base ? space : null;
trace.setChanged(new TraceChangeRecord<>(TraceOverlaySpaceChangeType.ADDED, null, }
trace, null, space)); return doCreateOverlaySpace(name, base);
return space;
} }
} }

View file

@ -66,7 +66,7 @@ public enum DBTraceObjectRegisterSupport {
static class LazyValues { static class LazyValues {
private final TraceObjectValue registerValue; private final TraceObjectValue registerValue;
private BigInteger value; private BigInteger value;
private int length = -1; private int bitLength = -1;
private byte[] be; private byte[] be;
private byte[] le; private byte[] le;
@ -85,7 +85,11 @@ public enum DBTraceObjectRegisterSupport {
"Invalid register value " + s + ". Must be hex digits only."); "Invalid register value " + s + ". Must be hex digits only.");
} }
} }
if (val instanceof Byte b) { else if (val instanceof byte[] arr) {
// NOTE: Reg object values are always big endian
return new BigInteger(1, arr);
}
else if (val instanceof Byte b) {
return BigInteger.valueOf(b); return BigInteger.valueOf(b);
} }
else if (val instanceof Short s) { else if (val instanceof Short s) {
@ -106,16 +110,16 @@ public enum DBTraceObjectRegisterSupport {
"'"); "'");
} }
int getRegisterValueLength() throws RegisterValueException { int getRegisterValueBitLength() throws RegisterValueException {
Object objLength = registerValue.getParent() Object objBitLength = registerValue.getParent()
.getValue(registerValue.getMinSnap(), TargetRegister.LENGTH_ATTRIBUTE_NAME) .getValue(registerValue.getMinSnap(), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME)
.getValue(); .getValue();
if (!(objLength instanceof Number)) { if (!(objBitLength instanceof Number)) {
throw new RegisterValueException( throw new RegisterValueException(
"Register length is not numeric: (" + objLength.getClass() + ") '" + objLength + "Register length is not numeric: (" + objBitLength.getClass() + ") '" +
"'"); objBitLength + "'");
} }
return ((Number) objLength).intValue(); return ((Number) objBitLength).intValue();
} }
BigInteger getValue() throws RegisterValueException { BigInteger getValue() throws RegisterValueException {
@ -125,25 +129,29 @@ public enum DBTraceObjectRegisterSupport {
return value = convertRegisterValueToBigInteger(); return value = convertRegisterValueToBigInteger();
} }
int getLength() throws RegisterValueException { int getBitLength() throws RegisterValueException {
if (length != -1) { if (bitLength != -1) {
return length; return bitLength;
} }
return length = getRegisterValueLength(); return bitLength = getRegisterValueBitLength();
}
int getByteLength() throws RegisterValueException {
return (getBitLength() + 7) / 8;
} }
byte[] getBytesBigEndian() throws RegisterValueException { byte[] getBytesBigEndian() throws RegisterValueException {
if (be != null) { if (be != null) {
return be; return be;
} }
return be = Utils.bigIntegerToBytes(getValue(), getLength(), true); return be = Utils.bigIntegerToBytes(getValue(), getByteLength(), true);
} }
byte[] getBytesLittleEndian() throws RegisterValueException { byte[] getBytesLittleEndian() throws RegisterValueException {
if (le != null) { if (le != null) {
return le; return le;
} }
return le = Utils.bigIntegerToBytes(getValue(), getLength(), false); return le = Utils.bigIntegerToBytes(getValue(), getByteLength(), false);
} }
public byte[] getBytes(boolean isBigEndian) throws RegisterValueException { public byte[] getBytes(boolean isBigEndian) throws RegisterValueException {
@ -160,14 +168,10 @@ public enum DBTraceObjectRegisterSupport {
return null; return null;
} }
String pathStr = container.getCanonicalPath().toString(); String pathStr = container.getCanonicalPath().toString();
AddressSpace space = object.getTrace().getBaseAddressFactory().getAddressSpace(pathStr); Trace trace = object.getTrace();
if (space == null) { return trace.getMemoryManager()
return null; .getOrCreateOverlayAddressSpace(pathStr,
} trace.getBaseAddressFactory().getRegisterSpace());
if (!space.isRegisterSpace()) {
return null;
}
return space;
} }
protected AddressSpace findRegisterOverlay(TraceObjectValue objectValue) { protected AddressSpace findRegisterOverlay(TraceObjectValue objectValue) {

View file

@ -18,6 +18,7 @@ package ghidra.trace.database.guest;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.TargetRegister; import ghidra.dbg.target.TargetRegister;
import ghidra.dbg.target.schema.TargetObjectSchema; import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.dbg.util.PathMatcher; import ghidra.dbg.util.PathMatcher;
@ -30,6 +31,7 @@ import ghidra.program.model.symbol.SourceType;
import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage; import ghidra.trace.database.guest.DBTraceGuestPlatform.DBTraceGuestLanguage;
import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.symbol.*; import ghidra.trace.model.symbol.*;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.util.TraceRegisterUtils; import ghidra.trace.util.TraceRegisterUtils;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@ -78,6 +80,48 @@ public interface InternalTracePlatform extends TracePlatform {
return result; return result;
} }
@Override
default String getConventionalRegisterObjectName(Register register) {
Address pmin = mapGuestToHost(register.getAddress());
if (pmin == null) {
return register.getName();
}
TraceSymbolManager symbolManager = getTrace().getSymbolManager();
TraceNamespaceSymbol nsRegMap = symbolManager.namespaces().getGlobalNamed(regMap(register));
Collection<? extends TraceLabelSymbol> labels = symbolManager.labels()
.getAt(0, null, pmin, false)
.stream()
.filter(s -> s.getParentNamespace() == nsRegMap)
.toList();
if (labels.isEmpty()) {
return register.getName();
}
// primary is listed first, so take it
return labels.iterator().next().getName();
}
@Override
default PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
Register register) {
PathMatcher matcher = schema.searchFor(TargetRegister.class, path, true);
if (matcher.isEmpty()) {
return matcher;
}
String name = getConventionalRegisterObjectName(register);
return matcher.applyKeys(Align.RIGHT, List.of(name));
}
@Override
default PathMatcher getConventionalRegisterPath(TraceObject container, Register register) {
return getConventionalRegisterPath(container.getTargetSchema(),
container.getCanonicalPath().getKeyList(), register);
}
@Override
default PathMatcher getConventionalRegisterPath(TargetObject container, Register register) {
return getConventionalRegisterPath(container.getSchema(), container.getPath(), register);
}
@Override @Override
default PathMatcher getConventionalRegisterPath(AddressSpace space, Register register) { default PathMatcher getConventionalRegisterPath(AddressSpace space, Register register) {
List<String> path = PathUtils.parse(space.getName()); List<String> path = PathUtils.parse(space.getName());
@ -87,20 +131,7 @@ public interface InternalTracePlatform extends TracePlatform {
} }
TargetObjectSchema schema = rootSchema TargetObjectSchema schema = rootSchema
.getSuccessorSchema(path); .getSuccessorSchema(path);
PathMatcher matcher = schema.searchFor(TargetRegister.class, path, true); return getConventionalRegisterPath(schema, path, register);
if (matcher.isEmpty()) {
return matcher;
}
Address pmin = mapGuestToHost(register.getAddress());
TraceSymbolManager symbolManager = getTrace().getSymbolManager();
TraceNamespaceSymbol nsRegMap = symbolManager.namespaces().getGlobalNamed(regMap(register));
Collection<? extends TraceLabelSymbol> labels = symbolManager.labels()
.getAt(0, null, pmin, false)
.stream()
.filter(s -> s.getParentNamespace() == nsRegMap)
.toList();
String name = labels.isEmpty() ? register.getName() : labels.iterator().next().getName();
return matcher.applyKeys(Align.RIGHT, List.of(name));
} }
@Override @Override

View file

@ -55,6 +55,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsMemoryView<T extends DBTraceCo
Collections2.transform(manager.getActiveMemorySpaces(), this::getView); Collections2.transform(manager.getActiveMemorySpaces(), this::getView);
} }
public AddressSpace getSpace() {
return null;
}
/** /**
* @see TraceBaseCodeUnitsView#getTrace() * @see TraceBaseCodeUnitsView#getTrace()
*/ */

View file

@ -47,6 +47,10 @@ public abstract class AbstractBaseDBTraceCodeUnitsView<T extends DBTraceCodeUnit
this.space = space; this.space = space;
} }
public AddressSpace getSpace() {
return getAddressSpace();
}
/** /**
* Get the address space for this view * Get the address space for this view
* *

View file

@ -16,15 +16,14 @@
package ghidra.trace.database.listing; package ghidra.trace.database.listing;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.trace.model.listing.TraceCodeManager; import ghidra.trace.model.listing.*;
import ghidra.trace.model.listing.TraceCodeUnitsView;
/** /**
* The implementation of {@link TraceCodeManager#codeUnits()} * The implementation of {@link TraceCodeManager#codeUnits()}
*/ */
public class DBTraceCodeUnitsMemoryView extends public class DBTraceCodeUnitsMemoryView extends
AbstractWithUndefinedDBTraceCodeUnitsMemoryView<DBTraceCodeUnitAdapter, DBTraceCodeUnitsView> AbstractWithUndefinedDBTraceCodeUnitsMemoryView<DBTraceCodeUnitAdapter, DBTraceCodeUnitsView>
implements TraceCodeUnitsView { implements TraceCodeUnitsView, InternalBaseCodeUnitsView<TraceCodeUnit> {
/** /**
* Construct the view * Construct the view

View file

@ -20,15 +20,14 @@ import java.util.List;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.listing.TraceCodeSpace; import ghidra.trace.model.listing.*;
import ghidra.trace.model.listing.TraceCodeUnitsView;
/** /**
* The implementation of {@link TraceCodeSpace#codeUnits()} * The implementation of {@link TraceCodeSpace#codeUnits()}
*/ */
public class DBTraceCodeUnitsView extends public class DBTraceCodeUnitsView extends
AbstractComposedDBTraceCodeUnitsView<DBTraceCodeUnitAdapter, AbstractSingleDBTraceCodeUnitsView<? extends DBTraceCodeUnitAdapter>> AbstractComposedDBTraceCodeUnitsView<DBTraceCodeUnitAdapter, AbstractSingleDBTraceCodeUnitsView<? extends DBTraceCodeUnitAdapter>>
implements TraceCodeUnitsView { implements TraceCodeUnitsView, InternalBaseCodeUnitsView<TraceCodeUnit> {
/** /**
* Construct the view * Construct the view

View file

@ -15,15 +15,14 @@
*/ */
package ghidra.trace.database.listing; package ghidra.trace.database.listing;
import ghidra.trace.model.listing.TraceCodeManager; import ghidra.trace.model.listing.*;
import ghidra.trace.model.listing.TraceDataView;
/** /**
* The implementation of {@link TraceCodeManager#data()} * The implementation of {@link TraceCodeManager#data()}
*/ */
public class DBTraceDataMemoryView public class DBTraceDataMemoryView
extends AbstractWithUndefinedDBTraceCodeUnitsMemoryView<DBTraceDataAdapter, DBTraceDataView> extends AbstractWithUndefinedDBTraceCodeUnitsMemoryView<DBTraceDataAdapter, DBTraceDataView>
implements TraceDataView { implements TraceDataView, InternalBaseCodeUnitsView<TraceData> {
/** /**
* Construct the view * Construct the view

View file

@ -20,15 +20,14 @@ import java.util.List;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.listing.TraceCodeSpace; import ghidra.trace.model.listing.*;
import ghidra.trace.model.listing.TraceDataView;
/** /**
* The implementation of {@link TraceCodeSpace#data()} * The implementation of {@link TraceCodeSpace#data()}
*/ */
public class DBTraceDataView extends public class DBTraceDataView extends
AbstractComposedDBTraceCodeUnitsView<DBTraceDataAdapter, AbstractSingleDBTraceCodeUnitsView<? extends DBTraceDataAdapter>> AbstractComposedDBTraceCodeUnitsView<DBTraceDataAdapter, AbstractSingleDBTraceCodeUnitsView<? extends DBTraceDataAdapter>>
implements TraceDataView { implements TraceDataView, InternalBaseCodeUnitsView<TraceData> {
/** /**
* Construct the view * Construct the view

View file

@ -17,12 +17,10 @@ package ghidra.trace.database.listing;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import ghidra.program.model.address.Address; import ghidra.program.model.address.*;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.listing.TraceCodeManager; import ghidra.trace.model.listing.TraceCodeManager;
import ghidra.trace.model.listing.TraceDefinedDataView;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -31,7 +29,7 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class DBTraceDefinedDataMemoryView public class DBTraceDefinedDataMemoryView
extends AbstractBaseDBTraceCodeUnitsMemoryView<DBTraceData, DBTraceDefinedDataView> extends AbstractBaseDBTraceCodeUnitsMemoryView<DBTraceData, DBTraceDefinedDataView>
implements TraceDefinedDataView { implements InternalTraceDefinedDataView {
/** /**
* Construct the view * Construct the view

View file

@ -28,7 +28,6 @@ import ghidra.trace.model.Trace.TraceCodeChangeType;
import ghidra.trace.model.Trace.TraceCompositeDataChangeType; import ghidra.trace.model.Trace.TraceCompositeDataChangeType;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.listing.TraceCodeSpace; import ghidra.trace.model.listing.TraceCodeSpace;
import ghidra.trace.model.listing.TraceDefinedDataView;
import ghidra.trace.util.TraceChangeRecord; import ghidra.trace.util.TraceChangeRecord;
import ghidra.util.LockHold; import ghidra.util.LockHold;
@ -36,7 +35,7 @@ import ghidra.util.LockHold;
* The implementation of {@link TraceCodeSpace#definedData()} * The implementation of {@link TraceCodeSpace#definedData()}
*/ */
public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView<DBTraceData> public class DBTraceDefinedDataView extends AbstractBaseDBTraceDefinedUnitsView<DBTraceData>
implements TraceDefinedDataView { implements InternalTraceDefinedDataView {
/** /**
* Construct the view * Construct the view
* *

View file

@ -18,8 +18,7 @@ package ghidra.trace.database.listing;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.listing.TraceCodeManager; import ghidra.trace.model.listing.*;
import ghidra.trace.model.listing.TraceDefinedUnitsView;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -28,7 +27,7 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class DBTraceDefinedUnitsMemoryView extends public class DBTraceDefinedUnitsMemoryView extends
AbstractBaseDBTraceCodeUnitsMemoryView<AbstractDBTraceCodeUnit<?>, DBTraceDefinedUnitsView> AbstractBaseDBTraceCodeUnitsMemoryView<AbstractDBTraceCodeUnit<?>, DBTraceDefinedUnitsView>
implements TraceDefinedUnitsView { implements TraceDefinedUnitsView, InternalTraceBaseDefinedUnitsView<TraceCodeUnit> {
/** /**
* Construct the view * Construct the view

View file

@ -22,8 +22,7 @@ import com.google.common.collect.Range;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.listing.TraceCodeSpace; import ghidra.trace.model.listing.*;
import ghidra.trace.model.listing.TraceDefinedUnitsView;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -33,7 +32,7 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class DBTraceDefinedUnitsView extends public class DBTraceDefinedUnitsView extends
AbstractComposedDBTraceCodeUnitsView<AbstractDBTraceCodeUnit<?>, AbstractBaseDBTraceDefinedUnitsView<? extends AbstractDBTraceCodeUnit<?>>> AbstractComposedDBTraceCodeUnitsView<AbstractDBTraceCodeUnit<?>, AbstractBaseDBTraceDefinedUnitsView<? extends AbstractDBTraceCodeUnit<?>>>
implements TraceDefinedUnitsView { implements TraceDefinedUnitsView, InternalTraceBaseDefinedUnitsView<TraceCodeUnit> {
/** /**
* Construct the view * Construct the view

View file

@ -25,8 +25,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceCodeManager; import ghidra.trace.model.listing.*;
import ghidra.trace.model.listing.TraceInstructionsView;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -36,7 +35,7 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class DBTraceInstructionsMemoryView public class DBTraceInstructionsMemoryView
extends AbstractBaseDBTraceCodeUnitsMemoryView<DBTraceInstruction, DBTraceInstructionsView> extends AbstractBaseDBTraceCodeUnitsMemoryView<DBTraceInstruction, DBTraceInstructionsView>
implements TraceInstructionsView { implements TraceInstructionsView, InternalTraceBaseDefinedUnitsView<TraceInstruction> {
/** /**
* Construct the view * Construct the view

View file

@ -46,7 +46,7 @@ import ghidra.util.task.TaskMonitor;
* The implementation of {@link TraceCodeSpace#instructions()} * The implementation of {@link TraceCodeSpace#instructions()}
*/ */
public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView<DBTraceInstruction> public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView<DBTraceInstruction>
implements TraceInstructionsView { implements TraceInstructionsView, InternalTraceBaseDefinedUnitsView<TraceInstruction> {
protected static <T> T replaceIfNotNull(T cur, T rep) { protected static <T> T replaceIfNotNull(T cur, T rep) {
return rep != null ? rep : cur; return rep != null ? rep : cur;

View file

@ -15,15 +15,14 @@
*/ */
package ghidra.trace.database.listing; package ghidra.trace.database.listing;
import ghidra.trace.model.listing.TraceCodeManager; import ghidra.trace.model.listing.*;
import ghidra.trace.model.listing.TraceUndefinedDataView;
/** /**
* The implementation of {@link TraceCodeManager#undefinedData()} * The implementation of {@link TraceCodeManager#undefinedData()}
*/ */
public class DBTraceUndefinedDataMemoryView extends public class DBTraceUndefinedDataMemoryView extends
AbstractWithUndefinedDBTraceCodeUnitsMemoryView<UndefinedDBTraceData, DBTraceUndefinedDataView> AbstractWithUndefinedDBTraceCodeUnitsMemoryView<UndefinedDBTraceData, DBTraceUndefinedDataView>
implements TraceUndefinedDataView { implements TraceUndefinedDataView, InternalBaseCodeUnitsView<TraceData> {
/** /**
* Construct the view * Construct the view

View file

@ -26,15 +26,15 @@ import com.google.common.collect.Range;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.listing.TraceCodeSpace; import ghidra.trace.model.listing.*;
import ghidra.trace.model.listing.TraceUndefinedDataView;
import ghidra.util.*; import ghidra.util.*;
/** /**
* The implementation of {@link TraceCodeSpace#undefinedData()} * The implementation of {@link TraceCodeSpace#undefinedData()}
*/ */
public class DBTraceUndefinedDataView extends public class DBTraceUndefinedDataView extends
AbstractSingleDBTraceCodeUnitsView<UndefinedDBTraceData> implements TraceUndefinedDataView { AbstractSingleDBTraceCodeUnitsView<UndefinedDBTraceData>
implements TraceUndefinedDataView, InternalBaseCodeUnitsView<TraceData> {
protected final static int CACHE_MAX_SNAPS = 5; protected final static int CACHE_MAX_SNAPS = 5;

View file

@ -0,0 +1,67 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.listing;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.*;
import ghidra.trace.util.TraceRegisterUtils;
public interface InternalBaseCodeUnitsView<T extends TraceCodeUnit>
extends TraceBaseCodeUnitsView<T> {
AddressSpace getSpace();
@Override
@SuppressWarnings("unchecked")
default T getForRegister(TracePlatform platform, long snap, Register register) {
// Find a code unit which contains the register completely
AddressRange range = platform.getConventionalRegisterRange(getSpace(), register);
T candidate = getContaining(snap, range.getMinAddress());
if (candidate == null) {
return null;
}
int cmpMax = range.getMaxAddress().compareTo(candidate.getMaxAddress());
if (cmpMax > 0) {
return null;
}
if (cmpMax == 0 && candidate.getMinAddress().equals(range.getMinAddress())) {
return candidate;
}
if (!(candidate instanceof TraceData)) {
return null;
}
TraceData data = (TraceData) candidate;
// Cast because if candidate is TraceData, T is, too
// NOTE: It may not be a primitive
return (T) TraceRegisterUtils.seekComponent(data, range);
}
@Override
default T getContaining(TracePlatform platform, long snap, Register register) {
AddressRange range = platform.getConventionalRegisterRange(getSpace(), register);
T candidate = getContaining(snap, range.getMinAddress());
if (candidate == null) {
return null;
}
int cmpMax = range.getMaxAddress().compareTo(candidate.getMaxAddress());
if (cmpMax > 0) {
return null;
}
return candidate;
}
}

View file

@ -0,0 +1,37 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.listing;
import com.google.common.collect.Range;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.lang.Register;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceBaseDefinedUnitsView;
import ghidra.trace.model.listing.TraceCodeUnit;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public interface InternalTraceBaseDefinedUnitsView<T extends TraceCodeUnit>
extends TraceBaseDefinedUnitsView<T>, InternalBaseCodeUnitsView<T> {
@Override
default void clear(TracePlatform platform, Range<Long> span, Register register,
TaskMonitor monitor) throws CancelledException {
AddressRange range = platform.getConventionalRegisterRange(getSpace(), register);
clear(span, range, true, monitor);
}
}

View file

@ -0,0 +1,39 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.listing;
import com.google.common.collect.Range;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Register;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceData;
import ghidra.trace.model.listing.TraceDefinedDataView;
import ghidra.trace.util.TraceRegisterUtils;
public interface InternalTraceDefinedDataView
extends TraceDefinedDataView, InternalTraceBaseDefinedUnitsView<TraceData> {
@Override
default TraceData create(TracePlatform platform, Range<Long> lifespan, Register register,
DataType dataType) throws CodeUnitInsertionException {
TraceRegisterUtils.requireByteBound(register);
AddressRange range = platform.getConventionalRegisterRange(getSpace(), register);
return create(lifespan, range.getMinAddress(), dataType, (int) range.getLength());
}
}

View file

@ -77,6 +77,11 @@ public class DBTraceMemoryManager extends AbstractDBTraceSpaceBasedManager<DBTra
return overlayAdapter.createOverlayAddressSpace(name, base); return overlayAdapter.createOverlayAddressSpace(name, base);
} }
@Override
public AddressSpace getOrCreateOverlayAddressSpace(String name, AddressSpace base) {
return overlayAdapter.getOrCreateOverlayAddressSpace(name, base);
}
@Override @Override
public void deleteOverlayAddressSpace(String name) { public void deleteOverlayAddressSpace(String name) {
overlayAdapter.deleteOverlayAddressSpace(name); overlayAdapter.deleteOverlayAddressSpace(name);

View file

@ -60,14 +60,14 @@ public class DBTraceObjectRegister implements TraceObjectRegister, DBTraceObject
} }
@Override @Override
public int getLength() { public int getBitLength() {
return TraceObjectInterfaceUtils.getValue(object, computeMinSnap(), return TraceObjectInterfaceUtils.getValue(object, computeMinSnap(),
TargetRegister.LENGTH_ATTRIBUTE_NAME, Integer.class, 0); TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, Integer.class, 0);
} }
@Override @Override
public void setValue(Range<Long> lifespan, byte[] value) { public void setValue(Range<Long> lifespan, byte[] value) {
int length = getLength(); int length = getByteLength();
if (length != 0 && value.length != length) { if (length != 0 && value.length != length) {
throw new IllegalArgumentException("Length must match the register"); throw new IllegalArgumentException("Length must match the register");
} }
@ -88,7 +88,7 @@ public class DBTraceObjectRegister implements TraceObjectRegister, DBTraceObject
if (val instanceof String) { if (val instanceof String) {
// Always base 16. Model API says byte array for register value is big endian. // Always base 16. Model API says byte array for register value is big endian.
BigInteger bigVal = new BigInteger((String) val, 16); BigInteger bigVal = new BigInteger((String) val, 16);
return Utils.bigIntegerToBytes(bigVal, getLength(), true); return Utils.bigIntegerToBytes(bigVal, getByteLength(), true);
} }
throw new ClassCastException("Cannot convert " + val + " to byte array for register value"); throw new ClassCastException("Cannot convert " + val + " to byte array for register value");
} }

View file

@ -26,8 +26,6 @@ import db.DBHandle;
import db.DBRecord; import db.DBRecord;
import generic.CatenatedCollection; import generic.CatenatedCollection;
import ghidra.dbg.target.TargetRegisterContainer; import ghidra.dbg.target.TargetRegisterContainer;
import ghidra.dbg.util.PathPattern;
import ghidra.dbg.util.PathPredicates;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.trace.database.*; import ghidra.trace.database.*;
@ -35,7 +33,6 @@ import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.stack.TraceObjectStackFrame; import ghidra.trace.model.stack.TraceObjectStackFrame;
import ghidra.trace.model.stack.TraceStackFrame; import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObjectKeyPath;
import ghidra.trace.model.thread.TraceObjectThread; import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace; import ghidra.trace.util.TraceAddressSpace;
@ -247,22 +244,6 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
return getForRegisterSpace(frame.getStack().getThread(), frame.getLevel(), createIfAbsent); return getForRegisterSpace(frame.getStack().getThread(), frame.getLevel(), createIfAbsent);
} }
private TraceObject searchForRegisterContainer(TraceObject object, int frameLevel) {
PathPredicates regsMatcher = object.getRoot()
.getTargetSchema()
.searchForRegisterContainer(frameLevel, object.getCanonicalPath().getKeyList());
for (PathPattern regsPattern : regsMatcher.getPatterns()) {
TraceObject regsObj = trace.getObjectManager()
.getObjectByCanonicalPath(
TraceObjectKeyPath.of(regsPattern.getSingletonPath()));
if (regsObj != null) {
return regsObj;
}
}
return null;
}
private M doGetForRegisterSpaceFoundContainer(TraceObject object, TraceObject objRegs, private M doGetForRegisterSpaceFoundContainer(TraceObject object, TraceObject objRegs,
boolean createIfAbsent) { boolean createIfAbsent) {
String name = objRegs.getCanonicalPath().toString(); String name = objRegs.getCanonicalPath().toString();
@ -284,21 +265,15 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
} }
} }
try (LockHold hold = LockHold.lock(lock.writeLock())) { try (LockHold hold = LockHold.lock(lock.writeLock())) {
AddressSpace as = trace.getBaseAddressFactory().getAddressSpace(name); AddressSpace as = trace.getMemoryManager()
if (as == null) { .getOrCreateOverlayAddressSpace(name,
as = trace.getMemoryManager() trace.getBaseAddressFactory().getRegisterSpace());
.createOverlayAddressSpace(name,
trace.getBaseAddressFactory().getRegisterSpace());
}
M space = getForSpace(as, createIfAbsent); M space = getForSpace(as, createIfAbsent);
synchronized (regSpacesByObject) { synchronized (regSpacesByObject) {
regSpacesByObject.put(object, space); regSpacesByObject.put(object, space);
} }
return space; return space;
} }
catch (DuplicateNameException e) {
throw new AssertionError(e); // I checked for it first, with a lock
}
} }
protected M getForRegisterSpaceObjectThread(TraceObjectThread thread, int frameLevel, protected M getForRegisterSpaceObjectThread(TraceObjectThread thread, int frameLevel,
@ -319,7 +294,7 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
if (object.getTargetSchema().getInterfaces().contains(TargetRegisterContainer.class)) { if (object.getTargetSchema().getInterfaces().contains(TargetRegisterContainer.class)) {
return doGetForRegisterSpaceFoundContainer(object, object, createIfAbsent); return doGetForRegisterSpaceFoundContainer(object, object, createIfAbsent);
} }
TraceObject objRegs = searchForRegisterContainer(object, frameLevel); TraceObject objRegs = object.queryRegisterContainer(frameLevel);
if (objRegs != null) { if (objRegs != null) {
return doGetForRegisterSpaceFoundContainer(object, objRegs, createIfAbsent); return doGetForRegisterSpaceFoundContainer(object, objRegs, createIfAbsent);
} }

View file

@ -36,10 +36,18 @@ public interface DBTraceSpaceBased extends DBTraceSpaceKey {
return false; return false;
} }
default String explainLanguages(AddressSpace space) {
if (space.getName().equals(getAddressSpace().getName())) {
return ". It's likely they come from different languages. Check the platform.";
}
return "";
}
default long assertInSpace(Address addr) { default long assertInSpace(Address addr) {
if (!isMySpace(addr.getAddressSpace())) { if (!isMySpace(addr.getAddressSpace())) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Address '" + addr + "' is not in this space: '" + getAddressSpace() + "'"); "Address '" + addr + "' is not in this space: '" + getAddressSpace() + "'" +
explainLanguages(addr.getAddressSpace()));
} }
return addr.getOffset(); return addr.getOffset();
} }
@ -47,7 +55,8 @@ public interface DBTraceSpaceBased extends DBTraceSpaceKey {
default void assertInSpace(AddressRange range) { default void assertInSpace(AddressRange range) {
if (!isMySpace(range.getAddressSpace())) { if (!isMySpace(range.getAddressSpace())) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Address Range '" + range + "' is not in this space: '" + getAddressSpace() + "'"); "Address Range '" + range + "' is not in this space: '" + getAddressSpace() + "'" +
explainLanguages(range.getAddressSpace()));
} }
} }

View file

@ -859,13 +859,25 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
.map(o -> o.queryInterface(ifClass)); .map(o -> o.queryInterface(ifClass));
} }
// TODO: Post filter until GP-1301
private boolean isActuallyInterface(TraceObjectValPath path,
Class<? extends TargetObject> targetIf) {
TraceObjectValue lastEntry = path.getLastEntry();
if (lastEntry == null) {
// TODO: This assumes the client will call getDestination(this)
return this.getTargetSchema().getInterfaces().contains(targetIf);
}
if (!lastEntry.isObject()) {
return false;
}
return lastEntry.getChild().getTargetSchema().getInterfaces().contains(targetIf);
}
@Override @Override
public Stream<? extends TraceObjectValPath> querySuccessorsTargetInterface(Range<Long> span, public Stream<? extends TraceObjectValPath> querySuccessorsTargetInterface(Range<Long> span,
Class<? extends TargetObject> targetIf) { Class<? extends TargetObject> targetIf) {
PathMatcher matcher = getTargetSchema().searchFor(targetIf, true); PathMatcher matcher = getTargetSchema().searchFor(targetIf, true);
// TODO: Post filter until GP-1301 return getSuccessors(span, matcher).filter(p -> isActuallyInterface(p, targetIf));
return getSuccessors(span, matcher).filter(
p -> p.getDestination(this).getTargetSchema().getInterfaces().contains(targetIf));
} }
@Override @Override

View file

@ -15,13 +15,18 @@
*/ */
package ghidra.trace.model.guest; package ghidra.trace.model.guest;
import java.util.List;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.TargetRegister; import ghidra.dbg.target.TargetRegister;
import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.dbg.util.PathMatcher; import ghidra.dbg.util.PathMatcher;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.symbol.TraceLabelSymbol; import ghidra.trace.model.symbol.TraceLabelSymbol;
import ghidra.trace.model.target.TraceObject;
/** /**
* A platform within a trace * A platform within a trace
@ -119,7 +124,7 @@ public interface TracePlatform {
* Translate a set from host to guest * Translate a set from host to guest
* *
* <p> * <p>
* Only those ranges (or parts of ranges) that map are included. * Only those ranges (or parts of ranges) that mapped are included.
* *
* @param hostSet the host set * @param hostSet the host set
* @return the guest set * @return the guest set
@ -149,7 +154,7 @@ public interface TracePlatform {
* Translate a set from guest to host * Translate a set from guest to host
* *
* <p> * <p>
* Only those ranges (or parts of ranges) that map are included. * Only those ranges (or parts of ranges) that mapped are included.
* *
* @param guestSet the guest set * @param guestSet the guest set
* @return the host set * @return the host set
@ -165,13 +170,57 @@ public interface TracePlatform {
*/ */
AddressRange getConventionalRegisterRange(AddressSpace overlay, Register register); AddressRange getConventionalRegisterRange(AddressSpace overlay, Register register);
/**
* Get the name or index of the register object for the given platform register
*
* <p>
* This will check for a label in the host physical space, allowing a mapper to specify an
* alternative register object name. See {@link #addRegisterMapOverride(Register, String)}.
*
* @param register the platform register
* @return the mapped name
*/
String getConventionalRegisterObjectName(Register register);
/** /**
* Get the expected path where an object defining the register value would be * Get the expected path where an object defining the register value would be
* *
* <p> * <p>
* This will check for a label in the host physical space, allowing a mapper to specify an * This will check for a label in the host physical space, allowing a mapper to specify an
* alternative register name. * alternative register object name. See {@link #addRegisterMapOverride(Register, String)}.
* *
* @param schema the schema of the register container
* @param path the path to the register container
* @param register the platform register
* @return the path matcher, possibly empty
*/
PathMatcher getConventionalRegisterPath(TargetObjectSchema schema, List<String> path,
Register register);
/**
* Get the expected path where an object defining the register value would be
*
* @see #getConventionalRegisterPath(TargetObjectSchema, List, Register)
* @param container the register container
* @param register the platform register
* @return that path matcher, possibly empty, or null if the trace has no root schema
*/
PathMatcher getConventionalRegisterPath(TraceObject container, Register register);
/**
* Get the expected path where an object defining the register value would be
*
* @see #getConventionalRegisterPath(TargetObjectSchema, List, Register)
* @param container the target register container
* @param register the platform register
* @return the path matcher, possibly empty
*/
PathMatcher getConventionalRegisterPath(TargetObject container, Register register);
/**
* Get the expected path where an object defining the register value would be
*
* @see #getConventionalRegisterPath(TargetObjectSchema, List, Register)
* @param overlay the overlay space allocated for a thread or frame * @param overlay the overlay space allocated for a thread or frame
* @param register the platform register * @param register the platform register
* @return the path matcher, or null if there is no root schema * @return the path matcher, or null if there is no root schema

View file

@ -15,15 +15,13 @@
*/ */
package ghidra.trace.model.listing; package ghidra.trace.model.listing;
import java.util.HashSet;
import java.util.Set;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.util.TraceRegisterUtils; import ghidra.trace.util.TraceRegisterUtils;
import ghidra.util.IntersectionAddressSetView; import ghidra.util.IntersectionAddressSetView;
import ghidra.util.UnionAddressSetView; import ghidra.util.UnionAddressSetView;
@ -251,15 +249,6 @@ public interface TraceBaseCodeUnitsView<T extends TraceCodeUnit> {
*/ */
boolean intersectsRange(TraceAddressSnapRange range); boolean intersectsRange(TraceAddressSnapRange range);
/**
* Get the set of registers for the trace's base language
*
* @return the register set
*/
default Set<Register> getRegisters() {
return new HashSet<>(getTrace().getBaseLanguage().getRegisters());
}
/** /**
* Get the unit (or component of a structure) which spans exactly the addresses of the given * Get the unit (or component of a structure) which spans exactly the addresses of the given
* register * register
@ -267,28 +256,32 @@ public interface TraceBaseCodeUnitsView<T extends TraceCodeUnit> {
* @param register the register * @param register the register
* @return the unit or {@code null} * @return the unit or {@code null}
*/ */
@SuppressWarnings("unchecked")
default T getForRegister(long snap, Register register) { default T getForRegister(long snap, Register register) {
// Find a code unit which contains the register completely return getForRegister(getTrace().getPlatformManager().getHostPlatform(), snap, register);
T candidate = getContaining(snap, register.getAddress()); }
if (candidate == null) {
return null; /**
} * Get the unit (or component of a structure) which spans exactly the addresses of the given
AddressRange range = TraceRegisterUtils.rangeForRegister(register); * platform register
int cmpMax = range.getMaxAddress().compareTo(candidate.getMaxAddress()); *
if (cmpMax > 0) { * @param platform the platform whose language defines the register
return null; * @param register the register
} * @return the unit or {@code null}
if (cmpMax == 0 && candidate.getMinAddress().equals(register.getAddress())) { */
return candidate; T getForRegister(TracePlatform platform, long snap, Register register);
}
if (!(candidate instanceof TraceData)) { /**
return null; * Get the unit which completely contains the given register
} *
TraceData data = (TraceData) candidate; * <p>
// Cast because if candidate is TraceData, T is, too * This does not descend into structures.
// NOTE: It may not be a primitive *
return (T) TraceRegisterUtils.seekComponent(data, range); * @param snap the snap during which the unit must be alive
* @param register the register
* @return the unit or {@code unit}
*/
default T getContaining(long snap, Register register) {
return getContaining(getTrace().getPlatformManager().getHostPlatform(), snap, register);
} }
/** /**
@ -297,21 +290,12 @@ public interface TraceBaseCodeUnitsView<T extends TraceCodeUnit> {
* <p> * <p>
* This does not descend into structures. * This does not descend into structures.
* *
* @platform the platform whose language defines the register
* @param snap the snap during which the unit must be alive
* @param register the register * @param register the register
* @return the unit or {@code unit} * @return the unit or {@code unit}
*/ */
default T getContaining(long snap, Register register) { T getContaining(TracePlatform platform, long snap, Register register);
T candidate = getContaining(snap, register.getAddress());
if (candidate == null) {
return null;
}
AddressRange range = TraceRegisterUtils.rangeForRegister(register);
int cmpMax = range.getMaxAddress().compareTo(candidate.getMaxAddress());
if (cmpMax > 0) {
return null;
}
return candidate;
}
/** /**
* Get the live units whose start addresses are within the given register * Get the live units whose start addresses are within the given register

View file

@ -19,6 +19,7 @@ import com.google.common.collect.Range;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.util.TraceRegisterUtils; import ghidra.trace.util.TraceRegisterUtils;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -64,4 +65,19 @@ public interface TraceBaseDefinedUnitsView<T extends TraceCodeUnit>
throws CancelledException { throws CancelledException {
clear(span, TraceRegisterUtils.rangeForRegister(register), true, monitor); clear(span, TraceRegisterUtils.rangeForRegister(register), true, monitor);
} }
/**
* Clear the units contained within the given span and platform register
*
* <p>
* Any units alive before the given span are truncated instead of deleted.
*
* @param platform the platform whose language defines the register
* @param span the span to clear
* @param register the register
* @param monitor a monitor for progress and cancellation
* @throws CancelledException if the clear is cancelled
*/
void clear(TracePlatform platform, Range<Long> span, Register register, TaskMonitor monitor)
throws CancelledException;
} }

View file

@ -21,7 +21,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.Trace; import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.util.TraceRegisterUtils; import ghidra.trace.util.TraceRegisterUtils;
/** /**
@ -75,17 +75,24 @@ public interface TraceDefinedDataView extends TraceBaseDefinedUnitsView<TraceDat
*/ */
default TraceData create(Range<Long> lifespan, Register register, DataType dataType) default TraceData create(Range<Long> lifespan, Register register, DataType dataType)
throws CodeUnitInsertionException { throws CodeUnitInsertionException {
// TODO: A better way to handle memory-mapped registers?
Trace trace = getTrace();
if (register.getAddressSpace() != trace
.getBaseLanguage()
.getAddressFactory()
.getRegisterSpace()) {
return trace.getCodeManager()
.definedData()
.create(lifespan, register.getAddress(), dataType, register.getNumBytes());
}
TraceRegisterUtils.requireByteBound(register); TraceRegisterUtils.requireByteBound(register);
return create(lifespan, register.getAddress(), dataType, register.getNumBytes()); return create(lifespan, register.getAddress(), dataType, register.getNumBytes());
} }
/**
* Create a data unit on the given platform register
*
* <p>
* If the register is memory mapped, this will delegate to the appropriate space. In those
* cases, the assignment affects all threads.
*
* @param platform the platform whose language defines the register
* @param lifespan the span for which the unit is effective
* @param register the register to assign a data type
* @param dataType the data type for the register
* @return the new data unit
* @throws CodeUnitInsertionException if there's a conflict
*/
TraceData create(TracePlatform platform, Range<Long> lifespan, Register register,
DataType dataType) throws CodeUnitInsertionException;
} }

View file

@ -49,6 +49,12 @@ public interface TraceMemoryManager extends TraceMemoryOperations {
* space named after the path of each memory being recorded. Of course, the mapping still needs * space named after the path of each memory being recorded. Of course, the mapping still needs
* to occur between the trace and parts of the display and during emulation. * to occur between the trace and parts of the display and during emulation.
* *
* <p>
* NOTE: We are also moving away from (space, thread, frame) triples to uniquely identify
* register storage. Instead, that will be encoded into the address space itself. Register
* overlays will overlay register space as be named after the register container object, which
* subsumes thread and frame when applicable.
*
* @param name the name of the new address space * @param name the name of the new address space
* @param base the space after which this is modeled * @param base the space after which this is modeled
* @return the create space * @return the create space
@ -57,6 +63,21 @@ public interface TraceMemoryManager extends TraceMemoryOperations {
AddressSpace createOverlayAddressSpace(String name, AddressSpace base) AddressSpace createOverlayAddressSpace(String name, AddressSpace base)
throws DuplicateNameException; throws DuplicateNameException;
/**
* Get or create an overlay address space
*
* <p>
* If the space already exists, and it overlays the given base, the existing space is returned.
* If it overlays a different space, null is returned. If the space does not exist, it is
* created with the given base space.
*
* @see #createOverlayAddressSpace(String, AddressSpace)
* @param name the name of the address space
* @param base the expected base space
* @return the space, or null
*/
AddressSpace getOrCreateOverlayAddressSpace(String name, AddressSpace base);
/** /**
* Delete an overlay address space * Delete an overlay address space
* *

View file

@ -27,7 +27,7 @@ import ghidra.trace.model.thread.TraceObjectThread;
targetIf = TargetRegister.class, targetIf = TargetRegister.class,
shortName = "register", shortName = "register",
fixedKeys = { fixedKeys = {
TargetRegister.LENGTH_ATTRIBUTE_NAME TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME
}) })
public interface TraceObjectRegister extends TraceObjectInterface { public interface TraceObjectRegister extends TraceObjectInterface {
String KEY_STATE = "_state"; String KEY_STATE = "_state";
@ -36,7 +36,11 @@ public interface TraceObjectRegister extends TraceObjectInterface {
String getName(); String getName();
int getLength(); int getBitLength();
default int getByteLength() {
return (getBitLength() + 7) / 8;
}
void setValue(Range<Long> lifespan, byte[] value); void setValue(Range<Long> lifespan, byte[] value);

View file

@ -25,6 +25,7 @@ import com.google.common.collect.RangeSet;
import ghidra.dbg.target.TargetMethod; import ghidra.dbg.target.TargetMethod;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.TargetObjectSchema; import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.dbg.util.PathPattern;
import ghidra.dbg.util.PathPredicates; import ghidra.dbg.util.PathPredicates;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceUniqueObject; import ghidra.trace.model.TraceUniqueObject;
@ -548,4 +549,25 @@ public interface TraceObject extends TraceUniqueObject {
} }
return getTrace().getObjectManager().getObjectByCanonicalPath(TraceObjectKeyPath.of(path)); return getTrace().getObjectManager().getObjectByCanonicalPath(TraceObjectKeyPath.of(path));
} }
/**
* Search for a suitable register container
*
* @see TargetObjectSchema#searchForRegisterContainer(int, List)
* @param frameLevel the frame level. Must be 0 if not applicable
* @return the register container, or null
*/
default TraceObject queryRegisterContainer(int frameLevel) {
PathPredicates regsMatcher = getRoot().getTargetSchema()
.searchForRegisterContainer(frameLevel, getCanonicalPath().getKeyList());
for (PathPattern regsPattern : regsMatcher.getPatterns()) {
TraceObject regsObj = getTrace().getObjectManager()
.getObjectByCanonicalPath(
TraceObjectKeyPath.of(regsPattern.getSingletonPath()));
if (regsObj != null) {
return regsObj;
}
}
return null;
}
} }

View file

@ -18,6 +18,7 @@ package ghidra.trace.model.time.schedule;
import java.util.*; import java.util.*;
import ghidra.pcode.emu.PcodeMachine; import ghidra.pcode.emu.PcodeMachine;
import ghidra.program.model.lang.Language;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.TraceSnapshot;
@ -523,16 +524,16 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
* @param sleigh a single line of sleigh, excluding the terminating semicolon. * @param sleigh a single line of sleigh, excluding the terminating semicolon.
* @return the resulting schedule * @return the resulting schedule
*/ */
public TraceSchedule patched(TraceThread thread, String sleigh) { public TraceSchedule patched(TraceThread thread, Language language, String sleigh) {
if (!this.pSteps.isNop()) { if (!this.pSteps.isNop()) {
Sequence pTicks = this.pSteps.clone(); Sequence pTicks = this.pSteps.clone();
pTicks.advance(new PatchStep(thread.getKey(), sleigh)); pTicks.advance(new PatchStep(thread.getKey(), sleigh));
pTicks.coalescePatches(thread.getTrace().getBaseLanguage()); pTicks.coalescePatches(language);
return new TraceSchedule(snap, steps.clone(), pTicks); return new TraceSchedule(snap, steps.clone(), pTicks);
} }
Sequence ticks = this.steps.clone(); Sequence ticks = this.steps.clone();
ticks.advance(new PatchStep(keyOf(thread), sleigh)); ticks.advance(new PatchStep(keyOf(thread), sleigh));
ticks.coalescePatches(thread.getTrace().getBaseLanguage()); ticks.coalescePatches(language);
return new TraceSchedule(snap, ticks, new Sequence()); return new TraceSchedule(snap, ticks, new Sequence());
} }
@ -543,20 +544,20 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
* @param sleigh the lines of sleigh, excluding the terminating semicolons. * @param sleigh the lines of sleigh, excluding the terminating semicolons.
* @return the resulting schedule * @return the resulting schedule
*/ */
public TraceSchedule patched(TraceThread thread, List<String> sleigh) { public TraceSchedule patched(TraceThread thread, Language language, List<String> sleigh) {
if (!this.pSteps.isNop()) { if (!this.pSteps.isNop()) {
Sequence pTicks = this.pSteps.clone(); Sequence pTicks = this.pSteps.clone();
for (String line : sleigh) { for (String line : sleigh) {
pTicks.advance(new PatchStep(thread.getKey(), line)); pTicks.advance(new PatchStep(thread.getKey(), line));
} }
pTicks.coalescePatches(thread.getTrace().getBaseLanguage()); pTicks.coalescePatches(language);
return new TraceSchedule(snap, steps.clone(), pTicks); return new TraceSchedule(snap, steps.clone(), pTicks);
} }
Sequence ticks = this.steps.clone(); Sequence ticks = this.steps.clone();
for (String line : sleigh) { for (String line : sleigh) {
ticks.advance(new PatchStep(thread.getKey(), line)); ticks.advance(new PatchStep(thread.getKey(), line));
} }
ticks.coalescePatches(thread.getTrace().getBaseLanguage()); ticks.coalescePatches(language);
return new TraceSchedule(snap, ticks, new Sequence()); return new TraceSchedule(snap, ticks, new Sequence());
} }

View file

@ -27,6 +27,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue; import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.listing.TraceData; import ghidra.trace.model.listing.TraceData;
import ghidra.trace.model.memory.TraceMemorySpace; import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.memory.TraceMemoryState; import ghidra.trace.model.memory.TraceMemoryState;
@ -39,6 +40,27 @@ public enum TraceRegisterUtils {
return new AddressRangeImpl(address, address.add(register.getNumBytes() - 1)); return new AddressRangeImpl(address, address.add(register.getNumBytes() - 1));
} }
public static AddressRange getOverlayRange(AddressSpace space, AddressRange range) {
AddressSpace physical = space.getPhysicalSpace();
if (physical == space || physical != range.getAddressSpace()) {
return range;
}
return new AddressRangeImpl(
space.getAddress(range.getMinAddress().getOffset()),
space.getAddress(range.getMaxAddress().getOffset()));
}
public static AddressSetView getOverlaySet(AddressSpace space, AddressSetView set) {
if (!space.isOverlaySpace()) {
return set;
}
AddressSet result = new AddressSet();
for (AddressRange rng : set) {
result.add(getOverlayRange(space, rng));
}
return result;
}
public static byte[] padOrTruncate(byte[] arr, int length) { public static byte[] padOrTruncate(byte[] arr, int length) {
if (arr.length == length) { if (arr.length == length) {
return arr; return arr;
@ -141,8 +163,8 @@ public enum TraceRegisterUtils {
return new RegisterValue(register, addr.getOffsetAsBigInteger()); return new RegisterValue(register, addr.getOffsetAsBigInteger());
} }
public static RegisterValue combineWithTraceBaseRegisterValue(RegisterValue rv, long snap, public static RegisterValue combineWithTraceBaseRegisterValue(RegisterValue rv,
TraceMemorySpace regs, boolean requireKnown) { TracePlatform platform, long snap, TraceMemorySpace regs, boolean requireKnown) {
Register reg = rv.getRegister(); Register reg = rv.getRegister();
if (reg.isBaseRegister()) { if (reg.isBaseRegister()) {
return rv; return rv;
@ -154,11 +176,11 @@ public enum TraceRegisterUtils {
return rv.getBaseRegisterValue(); return rv.getBaseRegisterValue();
} }
if (requireKnown) { if (requireKnown) {
if (TraceMemoryState.KNOWN != regs.getState(snap, reg.getBaseRegister())) { if (TraceMemoryState.KNOWN != regs.getState(platform, snap, reg.getBaseRegister())) {
throw new IllegalStateException("Must fetch base register before setting a child"); throw new IllegalStateException("Must fetch base register before setting a child");
} }
} }
return regs.getValue(snap, reg.getBaseRegister()).combineValues(rv); return regs.getValue(platform, snap, reg.getBaseRegister()).combineValues(rv);
} }
public static ByteBuffer prepareBuffer(Register register) { public static ByteBuffer prepareBuffer(Register register) {

View file

@ -128,11 +128,11 @@ public class ToyDBTraceBuilder implements AutoCloseable {
* Manipulate the trace's memory and registers using Sleigh * Manipulate the trace's memory and registers using Sleigh
* *
* @param snap the snap to modify * @param snap the snap to modify
* @param frame the frame to modify
* @param thread the thread to modify, can be {@code null} if only memory is used * @param thread the thread to modify, can be {@code null} if only memory is used
* @param frame the frame to modify
* @param sleigh the Sleigh source * @param sleigh the Sleigh source
*/ */
public void exec(long snap, int frame, TraceThread thread, String sleigh) { public void exec(long snap, TraceThread thread, int frame, String sleigh) {
PcodeProgram program = SleighProgramCompiler.compileProgram((SleighLanguage) language, PcodeProgram program = SleighProgramCompiler.compileProgram((SleighLanguage) language,
"builder", sleigh, PcodeUseropLibrary.nil()); "builder", sleigh, PcodeUseropLibrary.nil());
TraceSleighUtils.buildByteExecutor(trace, snap, thread, frame) TraceSleighUtils.buildByteExecutor(trace, snap, thread, frame)
@ -144,8 +144,8 @@ public class ToyDBTraceBuilder implements AutoCloseable {
* *
* @param platform the platform whose language to use * @param platform the platform whose language to use
* @param snap the snap to modify * @param snap the snap to modify
* @param frame the frame to modify
* @param thread the thread to modify, can be {@code null} if only memory is used * @param thread the thread to modify, can be {@code null} if only memory is used
* @param frame the frame to modify
* @param sleigh the lines of Sleigh, including semicolons. * @param sleigh the lines of Sleigh, including semicolons.
*/ */
public void exec(TracePlatform platform, long snap, TraceThread thread, int frame, public void exec(TracePlatform platform, long snap, TraceThread thread, int frame,

View file

@ -76,7 +76,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
.createOverlayAddressSpace("Targets[0].Threads[0].Registers", .createOverlayAddressSpace("Targets[0].Threads[0].Registers",
b.trace.getBaseAddressFactory().getRegisterSpace()); b.trace.getBaseAddressFactory().getRegisterSpace());
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8); regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234); regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
} }
@ -109,7 +109,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
getSLEIGH_X86_64_LANGUAGE().getCompilerSpecByID(new CompilerSpecID("gcc"))); getSLEIGH_X86_64_LANGUAGE().getCompilerSpecByID(new CompilerSpecID("gcc")));
amd64.addMappedRegisterRange(); amd64.addMappedRegisterRange();
regRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8); regRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
regRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234); regRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
} }
@ -145,7 +145,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
RAX = amd64.getLanguage().getRegister("RAX"); RAX = amd64.getLanguage().getRegister("RAX");
amd64.addRegisterMapOverride(RAX, "orig_rax"); amd64.addRegisterMapOverride(RAX, "orig_rax");
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8); regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234); regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
} }
@ -177,7 +177,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
avr8.addMappedRange(b.addr(0), avr8.addMappedRange(b.addr(0),
avr8.getLanguage().getDefaultDataSpace().getAddress(0), 0x1000); avr8.getLanguage().getDefaultDataSpace().getAddress(0), 0x1000);
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 1); regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 8);
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12); regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12);
} }
@ -215,7 +215,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
avr8.addMappedRange(b.addr(overlay, 0), avr8.addMappedRange(b.addr(overlay, 0),
avr8.getLanguage().getDefaultDataSpace().getAddress(0), 0x1000); avr8.getLanguage().getDefaultDataSpace().getAddress(0), 0x1000);
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 1); regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 8);
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12); regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12);
} }
@ -251,7 +251,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
R0 = avr8.getLanguage().getRegister("R0"); R0 = avr8.getLanguage().getRegister("R0");
avr8.addRegisterMapOverride(R0, "orig_r0"); avr8.addRegisterMapOverride(R0, "orig_r0");
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 1); regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 8);
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12); regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12);
} }
@ -285,7 +285,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
getSLEIGH_X86_64_LANGUAGE().getCompilerSpecByID(new CompilerSpecID("gcc"))); getSLEIGH_X86_64_LANGUAGE().getCompilerSpecByID(new CompilerSpecID("gcc")));
amd64.addMappedRegisterRange(); amd64.addMappedRegisterRange();
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8); regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234); regOrigRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
RAX = amd64.getLanguage().getRegister("RAX"); RAX = amd64.getLanguage().getRegister("RAX");
@ -321,7 +321,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
avr8.addMappedRange(b.addr(0), avr8.addMappedRange(b.addr(0),
avr8.getLanguage().getDefaultDataSpace().getAddress(0), 0x1000); avr8.getLanguage().getDefaultDataSpace().getAddress(0), 0x1000);
regR0.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 1); regR0.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 8);
regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12); regR0.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x12);
R0 = avr8.getLanguage().getRegister("R0"); R0 = avr8.getLanguage().getRegister("R0");
@ -352,7 +352,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
.createOverlayAddressSpace("Targets[0].Threads[0].Registers", .createOverlayAddressSpace("Targets[0].Threads[0].Registers",
b.trace.getBaseAddressFactory().getRegisterSpace()); b.trace.getBaseAddressFactory().getRegisterSpace());
regRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8); regRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
regRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234); regRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
amd64 = b.trace.getPlatformManager() amd64 = b.trace.getPlatformManager()
@ -382,7 +382,7 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
TraceObjectKeyPath.parse("Targets[0].Threads[0].Registers.User[RAX]")); TraceObjectKeyPath.parse("Targets[0].Threads[0].Registers.User[RAX]"));
regRAX.insert(Range.atLeast(0L), ConflictResolution.DENY); regRAX.insert(Range.atLeast(0L), ConflictResolution.DENY);
regRAX.setValue(Range.atLeast(0L), TargetRegister.LENGTH_ATTRIBUTE_NAME, 8); regRAX.setValue(Range.atLeast(0L), TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME, 64);
regRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234); regRAX.setValue(Range.atLeast(0L), TargetRegister.VALUE_ATTRIBUTE_NAME, 0x1234);
amd64 = b.trace.getPlatformManager() amd64 = b.trace.getPlatformManager()

View file

@ -445,15 +445,16 @@ public class TraceScheduleTest extends AbstractGhidraHeadlessIntegrationTest {
thread = tb.trace.getThreadManager().createThread("Threads[0]", 0); thread = tb.trace.getThreadManager().createThread("Threads[0]", 0);
} }
TraceSchedule time = TraceSchedule.parse("0"); TraceSchedule time = TraceSchedule.parse("0");
time = time.patched(thread, "r0l=1"); time = time.patched(thread, tb.language, "r0l=1");
assertEquals("0:t0-{r0l=0x1}", time.toString()); assertEquals("0:t0-{r0l=0x1}", time.toString());
time = time.patched(thread, "r0h=2"); time = time.patched(thread, tb.language, "r0h=2");
assertEquals("0:t0-{r0=0x200000001}", time.toString()); assertEquals("0:t0-{r0=0x200000001}", time.toString());
time = time.patched(thread, "r1l=3").patched(thread, "*[ram]:4 0xcafe:8=0xdeadbeef"); time = time.patched(thread, tb.language, "r1l=3")
.patched(thread, tb.language, "*[ram]:4 0xcafe:8=0xdeadbeef");
assertEquals("0:t0-{*:4 0xcafe:8=0xdeadbeef};t0-{r0=0x200000001};t0-{r1l=0x3}", assertEquals("0:t0-{*:4 0xcafe:8=0xdeadbeef};t0-{r0=0x200000001};t0-{r1l=0x3}",
time.toString()); time.toString());
time = time.patched(thread, "*:8 0xcb00:8 = 0x1122334455667788"); time = time.patched(thread, tb.language, "*:8 0xcb00:8 = 0x1122334455667788");
assertEquals("0:t0-{*:8 0xcafe:8=0xdead112233445566};t0-{*:2 0xcb06:8=0x7788};" + assertEquals("0:t0-{*:8 0xcafe:8=0xdead112233445566};t0-{*:2 0xcb06:8=0x7788};" +
"t0-{r0=0x200000001};t0-{r1l=0x3}", time.toString()); "t0-{r0=0x200000001};t0-{r1l=0x3}", time.toString());
} }

View file

@ -86,6 +86,16 @@ public class CachedAddressSetView implements AddressSetView {
maxAddress = delegate.getMaxAddress(); maxAddress = delegate.getMaxAddress();
} }
protected static void addMixed(AddressSet set, Address min, Address max) {
if (min.getAddressSpace() == max.getAddressSpace()) {
set.add(min, max);
}
else {
set.add(min, min.getAddressSpace().getMaxAddress());
set.add(max.getAddressSpace().getMinAddress(), max);
}
}
protected void ensureKnown(Address min, Address max) { protected void ensureKnown(Address min, Address max) {
if (minAddress == null) { if (minAddress == null) {
return; return;
@ -101,21 +111,21 @@ public class CachedAddressSetView implements AddressSetView {
if (rangesBackward.hasNext()) { if (rangesBackward.hasNext()) {
AddressRange prev = rangesBackward.next(); AddressRange prev = rangesBackward.next();
cache.add(prev); cache.add(prev);
known.add(prev.getMinAddress(), min); addMixed(known, prev.getMinAddress(), min);
} }
else { else {
known.add(minAddress, min); addMixed(known, minAddress, min);
} }
AddressRangeIterator rangesForward = delegate.getAddressRanges(min, true); AddressRangeIterator rangesForward = delegate.getAddressRanges(min, true);
while (true) { while (true) {
if (!rangesForward.hasNext()) { if (!rangesForward.hasNext()) {
known.add(min, maxAddress); addMixed(known, min, maxAddress);
break; break;
} }
AddressRange next = rangesForward.next(); AddressRange next = rangesForward.next();
cache.add(next); cache.add(next);
if (next.getMaxAddress().compareTo(max) >= 0) { if (next.getMaxAddress().compareTo(max) >= 0) {
known.add(min, next.getMaxAddress()); addMixed(known, min, next.getMaxAddress());
break; break;
} }
} }

View file

@ -104,7 +104,7 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x55550000)), new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x55550000)),
new ProgramLocation(program, tb.addr(0x00400000)), 0x1000, false); new ProgramLocation(program, tb.addr(0x00400000)), 0x1000, false);
thread = tb.getOrAddThread("Threads[0]", 0); thread = tb.getOrAddThread("Threads[0]", 0);
tb.exec(0, 0, thread, "RIP = 0x55550000;"); tb.exec(0, thread, 0, "RIP = 0x55550000;");
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
waitForPass(() -> assertEquals(new ProgramLocation(program, tb.addr(0x00400000)), waitForPass(() -> assertEquals(new ProgramLocation(program, tb.addr(0x00400000)),