GP-1386: A generic object store in DBTrace for recording the object model tree

This commit is contained in:
Dan
2022-01-04 10:42:31 -05:00
parent e440e3333f
commit 675eabdd6e
179 changed files with 9971 additions and 797 deletions
@@ -121,28 +121,28 @@ public abstract class AbstractModelForDbgengBreakpointsTest
@Override @Override
protected void disableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter) protected void disableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String bpId = getBreakPattern().matchIndices(t.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(t.getPath()).get(BREAK_ID_POS);
waitOn(interpreter.execute("bd " + bpId)); waitOn(interpreter.execute("bd " + bpId));
} }
@Override @Override
protected void enableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter) protected void enableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String bpId = getBreakPattern().matchIndices(t.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(t.getPath()).get(BREAK_ID_POS);
waitOn(interpreter.execute("be " + bpId)); waitOn(interpreter.execute("be " + bpId));
} }
@Override @Override
protected void deleteViaInterpreter(TargetDeletable d, TargetInterpreter interpreter) protected void deleteViaInterpreter(TargetDeletable d, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String bpId = getBreakPattern().matchIndices(d.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(d.getPath()).get(BREAK_ID_POS);
waitOn(interpreter.execute("bc " + bpId)); waitOn(interpreter.execute("bc " + bpId));
} }
@Override @Override
protected void assertLocCoversViaInterpreter(AddressRange range, TargetBreakpointKind kind, protected void assertLocCoversViaInterpreter(AddressRange range, TargetBreakpointKind kind,
TargetBreakpointLocation loc, TargetInterpreter interpreter) throws Throwable { TargetBreakpointLocation loc, TargetInterpreter interpreter) throws Throwable {
String bpId = getBreakPattern().matchIndices(loc.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(loc.getPath()).get(BREAK_ID_POS);
String line = waitOn(interpreter.executeCapture("bl " + bpId)).trim(); String line = waitOn(interpreter.executeCapture("bl " + bpId)).trim();
assertFalse(line.contains("\n")); assertFalse(line.contains("\n"));
// NB. WinDbg numbers breakpoints in base 10, by default // NB. WinDbg numbers breakpoints in base 10, by default
@@ -153,7 +153,7 @@ public abstract class AbstractModelForDbgengBreakpointsTest
@Override @Override
protected void assertEnabledViaInterpreter(TargetTogglable t, boolean enabled, protected void assertEnabledViaInterpreter(TargetTogglable t, boolean enabled,
TargetInterpreter interpreter) throws Throwable { TargetInterpreter interpreter) throws Throwable {
String bpId = getBreakPattern().matchIndices(t.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(t.getPath()).get(BREAK_ID_POS);
String line = waitOn(interpreter.executeCapture("bl " + bpId)).trim(); String line = waitOn(interpreter.executeCapture("bl " + bpId)).trim();
assertFalse(line.contains("\n")); assertFalse(line.contains("\n"));
assertTrue(line.startsWith(bpId)); assertTrue(line.startsWith(bpId));
@@ -164,7 +164,7 @@ public abstract class AbstractModelForDbgengBreakpointsTest
@Override @Override
protected void assertDeletedViaInterpreter(TargetDeletable d, TargetInterpreter interpreter) protected void assertDeletedViaInterpreter(TargetDeletable d, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String bpId = getBreakPattern().matchIndices(d.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(d.getPath()).get(BREAK_ID_POS);
String line = waitOn(interpreter.executeCapture("bl " + bpId)).trim(); String line = waitOn(interpreter.executeCapture("bl " + bpId)).trim();
assertEquals("", line); assertEquals("", line);
} }
@@ -72,7 +72,7 @@ public abstract class AbstractModelForDbgengFrameActivationTest
String line = waitOn(interpreter.executeCapture(".frame")).trim(); String line = waitOn(interpreter.executeCapture(".frame")).trim();
assertFalse(line.contains("\n")); assertFalse(line.contains("\n"));
int frameId = Integer.parseInt(line.split("\\s+")[0], 16); int frameId = Integer.parseInt(line.split("\\s+")[0], 16);
int expId = Integer.decode(getStackPattern().matchIndices(expected.getPath()).get(2)); int expId = Integer.decode(getStackPattern().matchKeys(expected.getPath()).get(2));
assertEquals(expId, frameId); assertEquals(expId, frameId);
} }
@@ -63,7 +63,7 @@ public abstract class AbstractModelForDbgengProcessActivationTest
@Override @Override
protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter) protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String id = Unique.assertOne(getProcessPattern().matchIndices(obj.getPath())); String id = Unique.assertOne(getProcessPattern().matchKeys(obj.getPath()));
waitOn(interpreter.execute("|" + id + " s")); waitOn(interpreter.execute("|" + id + " s"));
} }
@@ -63,7 +63,7 @@ public abstract class AbstractModelForDbgengThreadActivationTest
@Override @Override
protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter) protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String threadId = getThreadPattern().matchIndices(obj.getPath()).get(1); String threadId = getThreadPattern().matchKeys(obj.getPath()).get(1);
// TODO: This test is imperfect, since processes are activated as well // TODO: This test is imperfect, since processes are activated as well
waitOn(interpreter.execute("~" + threadId + " s")); waitOn(interpreter.execute("~" + threadId + " s"));
} }
@@ -78,7 +78,7 @@ public abstract class AbstractModelForDbgengThreadActivationTest
.filter(l -> l.trim().startsWith(".")) .filter(l -> l.trim().startsWith("."))
.collect(Collectors.toList())).trim(); .collect(Collectors.toList())).trim();
String threadId = getIdFromCapture(line); String threadId = getIdFromCapture(line);
String expId = getThreadPattern().matchIndices(expected.getPath()).get(1); String expId = getThreadPattern().matchKeys(expected.getPath()).get(1);
assertEquals(expId, threadId); assertEquals(expId, threadId);
} }
} }
@@ -119,7 +119,7 @@ public abstract class AbstractModelForGdbBreakpointsTest
protected void disableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter) protected void disableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
assert t instanceof TargetBreakpointSpec; // TODO: or Location assert t instanceof TargetBreakpointSpec; // TODO: or Location
String index = Unique.assertOne(BREAK_PATTERN.matchIndices(t.getPath())); String index = Unique.assertOne(BREAK_PATTERN.matchKeys(t.getPath()));
waitOn(interpreter.execute("disable " + index)); waitOn(interpreter.execute("disable " + index));
} }
@@ -127,7 +127,7 @@ public abstract class AbstractModelForGdbBreakpointsTest
protected void enableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter) protected void enableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
assert t instanceof TargetBreakpointSpec; // TODO: or Location assert t instanceof TargetBreakpointSpec; // TODO: or Location
String index = Unique.assertOne(BREAK_PATTERN.matchIndices(t.getPath())); String index = Unique.assertOne(BREAK_PATTERN.matchKeys(t.getPath()));
waitOn(interpreter.execute("enable " + index)); waitOn(interpreter.execute("enable " + index));
} }
@@ -135,7 +135,7 @@ public abstract class AbstractModelForGdbBreakpointsTest
protected void deleteViaInterpreter(TargetDeletable d, TargetInterpreter interpreter) protected void deleteViaInterpreter(TargetDeletable d, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
assert d instanceof TargetBreakpointSpec; // TODO: or Location assert d instanceof TargetBreakpointSpec; // TODO: or Location
String index = Unique.assertOne(BREAK_PATTERN.matchIndices(d.getPath())); String index = Unique.assertOne(BREAK_PATTERN.matchKeys(d.getPath()));
waitOn(interpreter.execute("delete " + index)); waitOn(interpreter.execute("delete " + index));
} }
@@ -143,7 +143,7 @@ public abstract class AbstractModelForGdbBreakpointsTest
protected void assertLocCoversViaInterpreter(AddressRange range, TargetBreakpointKind kind, protected void assertLocCoversViaInterpreter(AddressRange range, TargetBreakpointKind kind,
TargetBreakpointLocation loc, TargetInterpreter interpreter) throws Throwable { TargetBreakpointLocation loc, TargetInterpreter interpreter) throws Throwable {
String index = String index =
Unique.assertOne(BREAK_PATTERN.matchIndices(loc.getSpecification().getPath())); Unique.assertOne(BREAK_PATTERN.matchKeys(loc.getSpecification().getPath()));
String output = waitOn(interpreter.executeCapture("info break " + index)); String output = waitOn(interpreter.executeCapture("info break " + index));
String line = Unique.assertOne(Stream.of(output.split("\n")) String line = Unique.assertOne(Stream.of(output.split("\n"))
.filter(l -> !l.trim().startsWith("Num")) .filter(l -> !l.trim().startsWith("Num"))
@@ -156,7 +156,7 @@ public abstract class AbstractModelForGdbBreakpointsTest
protected void assertEnabledViaInterpreter(TargetTogglable t, boolean enabled, protected void assertEnabledViaInterpreter(TargetTogglable t, boolean enabled,
TargetInterpreter interpreter) throws Throwable { TargetInterpreter interpreter) throws Throwable {
assert t instanceof TargetBreakpointSpec; // TODO: or Location assert t instanceof TargetBreakpointSpec; // TODO: or Location
String index = Unique.assertOne(BREAK_PATTERN.matchIndices(t.getPath())); String index = Unique.assertOne(BREAK_PATTERN.matchKeys(t.getPath()));
String output = waitOn(interpreter.executeCapture("info break " + index)); String output = waitOn(interpreter.executeCapture("info break " + index));
String line = Unique.assertOne(Stream.of(output.split("\n")) String line = Unique.assertOne(Stream.of(output.split("\n"))
.filter(l -> !l.trim().startsWith("Num")) .filter(l -> !l.trim().startsWith("Num"))
@@ -170,7 +170,7 @@ public abstract class AbstractModelForGdbBreakpointsTest
protected void assertDeletedViaInterpreter(TargetDeletable d, TargetInterpreter interpreter) protected void assertDeletedViaInterpreter(TargetDeletable d, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
assert d instanceof TargetBreakpointSpec; // TODO: or Location assert d instanceof TargetBreakpointSpec; // TODO: or Location
String index = Unique.assertOne(BREAK_PATTERN.matchIndices(d.getPath())); String index = Unique.assertOne(BREAK_PATTERN.matchKeys(d.getPath()));
String output = waitOn(interpreter.executeCapture("info break " + index)); String output = waitOn(interpreter.executeCapture("info break " + index));
assertTrue(output.contains("No breakpoint")); assertTrue(output.contains("No breakpoint"));
} }
@@ -72,7 +72,7 @@ public abstract class AbstractModelForGdbFrameActivationTest
@Override @Override
protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter) protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String index = Unique.assertOne(STACK_PATTERN.matchIndices(obj.getPath())); String index = Unique.assertOne(STACK_PATTERN.matchKeys(obj.getPath()));
waitOn(interpreter.execute("frame " + index)); waitOn(interpreter.execute("frame " + index));
} }
@@ -62,7 +62,7 @@ public abstract class AbstractModelForGdbInferiorActivationTest
@Override @Override
protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter) protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String index = Unique.assertOne(INF_PATTERN.matchIndices(obj.getPath())); String index = Unique.assertOne(INF_PATTERN.matchKeys(obj.getPath()));
waitOn(interpreter.execute("inferior " + index)); waitOn(interpreter.execute("inferior " + index));
} }
@@ -73,7 +73,7 @@ public abstract class AbstractModelForGdbThreadActivationTest
@Override @Override
protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter) protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String index = Unique.assertOne(Set.copyOf(THREAD_PATTERN.matchIndices(obj.getPath()))); String index = Unique.assertOne(Set.copyOf(THREAD_PATTERN.matchKeys(obj.getPath())));
// TODO: This test is imperfect, since inferiors are activated as well // TODO: This test is imperfect, since inferiors are activated as well
waitOn(interpreter.execute("thread " + index + ".1")); waitOn(interpreter.execute("thread " + index + ".1"));
} }
@@ -145,7 +145,7 @@ public abstract class AbstractModelForLldbBreakpointsTest
@Override @Override
protected void disableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter) protected void disableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String bpId = getBreakPattern().matchIndices(t.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(t.getPath()).get(BREAK_ID_POS);
String type = getTypeFromSpec(t); String type = getTypeFromSpec(t);
waitOn(interpreter.execute(getCommand("disable", type, bpId))); waitOn(interpreter.execute(getCommand("disable", type, bpId)));
} }
@@ -153,7 +153,7 @@ public abstract class AbstractModelForLldbBreakpointsTest
@Override @Override
protected void enableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter) protected void enableViaInterpreter(TargetTogglable t, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String bpId = getBreakPattern().matchIndices(t.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(t.getPath()).get(BREAK_ID_POS);
String type = getTypeFromSpec(t); String type = getTypeFromSpec(t);
waitOn(interpreter.execute(getCommand("enable", type, bpId))); waitOn(interpreter.execute(getCommand("enable", type, bpId)));
} }
@@ -161,7 +161,7 @@ public abstract class AbstractModelForLldbBreakpointsTest
@Override @Override
protected void deleteViaInterpreter(TargetDeletable d, TargetInterpreter interpreter) protected void deleteViaInterpreter(TargetDeletable d, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String bpId = getBreakPattern().matchIndices(d.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(d.getPath()).get(BREAK_ID_POS);
String type = getTypeFromSpec(d); String type = getTypeFromSpec(d);
waitOn(interpreter.execute(getCommand("delete", type, bpId))); waitOn(interpreter.execute(getCommand("delete", type, bpId)));
} }
@@ -169,7 +169,7 @@ public abstract class AbstractModelForLldbBreakpointsTest
@Override @Override
protected void assertLocCoversViaInterpreter(AddressRange range, TargetBreakpointKind kind, protected void assertLocCoversViaInterpreter(AddressRange range, TargetBreakpointKind kind,
TargetBreakpointLocation loc, TargetInterpreter interpreter) throws Throwable { TargetBreakpointLocation loc, TargetInterpreter interpreter) throws Throwable {
List<String> matchIndices = getBreakPattern().matchIndices(loc.getSpecification().getPath()); List<String> matchIndices = getBreakPattern().matchKeys(loc.getSpecification().getPath());
String bpId = matchIndices.get(BREAK_ID_POS); String bpId = matchIndices.get(BREAK_ID_POS);
String type = getTypeFromKind(kind); String type = getTypeFromKind(kind);
String line = waitOn(interpreter.executeCapture(getCommand("list", type, bpId))).trim(); String line = waitOn(interpreter.executeCapture(getCommand("list", type, bpId))).trim();
@@ -182,7 +182,7 @@ public abstract class AbstractModelForLldbBreakpointsTest
@Override @Override
protected void assertEnabledViaInterpreter(TargetTogglable t, boolean enabled, protected void assertEnabledViaInterpreter(TargetTogglable t, boolean enabled,
TargetInterpreter interpreter) throws Throwable { TargetInterpreter interpreter) throws Throwable {
String bpId = getBreakPattern().matchIndices(t.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(t.getPath()).get(BREAK_ID_POS);
String type = getTypeFromSpec(t); String type = getTypeFromSpec(t);
String line = waitOn(interpreter.executeCapture(getCommand("list", type, bpId))).trim(); String line = waitOn(interpreter.executeCapture(getCommand("list", type, bpId))).trim();
assertTrue(line.contains(bpId.substring(1)+":")); assertTrue(line.contains(bpId.substring(1)+":"));
@@ -192,7 +192,7 @@ public abstract class AbstractModelForLldbBreakpointsTest
@Override @Override
protected void assertDeletedViaInterpreter(TargetDeletable d, TargetInterpreter interpreter) protected void assertDeletedViaInterpreter(TargetDeletable d, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String bpId = getBreakPattern().matchIndices(d.getPath()).get(BREAK_ID_POS); String bpId = getBreakPattern().matchKeys(d.getPath()).get(BREAK_ID_POS);
String type = getTypeFromSpec(d); String type = getTypeFromSpec(d);
String line = waitOn(interpreter.executeCapture(type + " list ")).trim(); String line = waitOn(interpreter.executeCapture(type + " list ")).trim();
assertFalse(line.contains(bpId+":")); assertFalse(line.contains(bpId+":"));
@@ -57,7 +57,7 @@ public abstract class AbstractModelForLldbFrameActivationTest
protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter) protected void activateViaInterpreter(TargetObject obj, TargetInterpreter interpreter)
throws Throwable { throws Throwable {
String index = getStackPattern().matchIndices(obj.getPath()).get(3); String index = getStackPattern().matchKeys(obj.getPath()).get(3);
waitOn(interpreter.execute("frame select " + index)); waitOn(interpreter.execute("frame select " + index));
} }
@@ -70,7 +70,7 @@ public abstract class AbstractModelForLldbFrameActivationTest
assertFalse(line.contains("\n")); assertFalse(line.contains("\n"));
String id = getIdFromCapture(line); String id = getIdFromCapture(line);
int frameId = Integer.parseInt(id, 10); int frameId = Integer.parseInt(id, 10);
int expId = Integer.decode(getStackPattern().matchIndices(expected.getPath()).get(3)); int expId = Integer.decode(getStackPattern().matchKeys(expected.getPath()).get(3));
assertEquals(expId, frameId); assertEquals(expId, frameId);
} }
@@ -94,7 +94,7 @@ public abstract class AbstractModelForLldbProcessActivationTest
.filter(l -> l.trim().startsWith("*")) .filter(l -> l.trim().startsWith("*"))
.collect(Collectors.toList())).trim(); .collect(Collectors.toList())).trim();
String procId = getIdFromCapture(line); String procId = getIdFromCapture(line);
String expId = getProcessPattern().matchIndices(expected.getPath()).get(1); String expId = getProcessPattern().matchKeys(expected.getPath()).get(1);
assertEquals(Long.parseLong(expId, 16), Long.parseLong(procId)); assertEquals(Long.parseLong(expId, 16), Long.parseLong(procId));
} }
@@ -97,7 +97,7 @@ public abstract class AbstractModelForLldbSessionActivationTest
.filter(l -> l.trim().startsWith("*")) .filter(l -> l.trim().startsWith("*"))
.collect(Collectors.toList())).trim(); .collect(Collectors.toList())).trim();
String procId = getIdFromCapture(line); String procId = getIdFromCapture(line);
String expId = getSessionPattern().matchIndices(expected.getPath()).get(0); String expId = getSessionPattern().matchKeys(expected.getPath()).get(0);
assertEquals(Long.parseLong(expId, 16), Long.parseLong(procId)); assertEquals(Long.parseLong(expId, 16), Long.parseLong(procId));
} }
@@ -80,7 +80,7 @@ public abstract class AbstractModelForLldbThreadActivationTest
.filter(l -> l.trim().startsWith("*")) .filter(l -> l.trim().startsWith("*"))
.collect(Collectors.toList())).trim(); .collect(Collectors.toList())).trim();
String threadId = getIdFromCapture(line); String threadId = getIdFromCapture(line);
String expId = getThreadPattern().matchIndices(expected.getPath()).get(2); String expId = getThreadPattern().matchKeys(expected.getPath()).get(2);
assertEquals(expId, threadId); assertEquals(expId, threadId);
} }
@@ -38,7 +38,8 @@ public class BreakpointLocationRow {
} }
public boolean isEnabled() { public boolean isEnabled() {
return loc.isEnabled(); TraceRecorder recorder = provider.modelService.getRecorder(loc.getTrace());
return recorder != null && loc.isEnabled(recorder.getSnap());
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
@@ -210,8 +210,8 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
} }
@Override @Override
public java.util.List<LogTableColumns> defaultSortOrder() { public List<LogTableColumns> defaultSortOrder() {
return java.util.List.of(LogTableColumns.ACTIONS, LogTableColumns.TIME); return List.of(LogTableColumns.ACTIONS, LogTableColumns.TIME);
} }
} }
@@ -228,7 +228,7 @@ public class DebuggerCopyPlan {
Address dest = intoAddress.add(off); Address dest = intoAddress.add(off);
ProgramBreakpoint pb = ProgramBreakpoint pb =
new ProgramBreakpoint(into, dest, bpt.getLength(), bpt.getKinds()); new ProgramBreakpoint(into, dest, bpt.getLength(), bpt.getKinds());
if (bpt.isEnabled()) { if (bpt.isEnabled(from.getSnap())) {
pb.enable(); pb.enable();
} }
else { else {
@@ -210,6 +210,11 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
public ModuleTableModel() { public ModuleTableModel() {
super("Modules", ModuleTableColumns.class, TraceModule::getObjectKey, ModuleRow::new); super("Modules", ModuleTableColumns.class, TraceModule::getObjectKey, ModuleRow::new);
} }
@Override
public List<ModuleTableColumns> defaultSortOrder() {
return List.of(ModuleTableColumns.BASE);
}
} }
protected static class SectionTableModel protected static class SectionTableModel
@@ -220,6 +225,11 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
super("Sections", SectionTableColumns.class, TraceSection::getObjectKey, super("Sections", SectionTableColumns.class, TraceSection::getObjectKey,
SectionRow::new); SectionRow::new);
} }
@Override
public List<SectionTableColumns> defaultSortOrder() {
return List.of(SectionTableColumns.START);
}
} }
protected static Set<TraceModule> getSelectedModulesFromModuleContext( protected static Set<TraceModule> getSelectedModulesFromModuleContext(
@@ -22,6 +22,7 @@ import ghidra.app.services.TraceRecorder;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
public class ThreadRow { public class ThreadRow {
@@ -109,6 +110,12 @@ public class ThreadRow {
@Override @Override
public String toString() { public String toString() {
return getName(); try {
return getName();
}
catch (Exception e) {
Msg.error(this, "Error rendering as string: " + e);
return "<ERROR>";
}
} }
} }
@@ -288,7 +288,7 @@ public interface LogicalBreakpointInternal extends LogicalBreakpoint {
public TraceEnablement computeEnablement() { public TraceEnablement computeEnablement() {
TraceEnablement en = TraceEnablement.MISSING; TraceEnablement en = TraceEnablement.MISSING;
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
en = en.combine(TraceEnablement.fromBool(bpt.obj.isEnabled())); en = en.combine(TraceEnablement.fromBool(bpt.obj.isEnabled(recorder.getSnap())));
if (en == TraceEnablement.MIXED) { if (en == TraceEnablement.MIXED) {
return en; return en;
} }
@@ -167,7 +167,7 @@ public class DefaultBreakpointRecorder implements ManagedBreakpointRecorder {
traceBpt.setClearedSnap(snap - 1); traceBpt.setClearedSnap(snap - 1);
} }
breakpointManager.placeBreakpoint(path, snap, range, breakpointManager.placeBreakpoint(path, snap, range,
traceBpt.getThreads(), traceBpt.getKinds(), traceBpt.isEnabled(), traceBpt.getThreads(), traceBpt.getKinds(), traceBpt.isEnabled(snap),
traceBpt.getComment()); traceBpt.getComment());
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
@@ -173,7 +173,7 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera
assertEquals(3, allBreakpoints.size()); assertEquals(3, allBreakpoints.size());
}); });
waitForPass(() -> { waitForPass(() -> {
assertFalse(bpt.isEnabled()); assertFalse(bpt.isEnabled(0));
}); });
/** /**
* TODO: Might be necessary to debounce and wait for service callbacks to settle. Sometimes, * TODO: Might be necessary to debounce and wait for service callbacks to settle. Sometimes,
@@ -31,9 +31,9 @@ import ghidra.async.AsyncTestUtils;
import ghidra.test.ToyProgramBuilder; import ghidra.test.ToyProgramBuilder;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.database.time.DBTraceTimeManager; import ghidra.trace.database.time.DBTraceTimeManager;
import ghidra.trace.model.memory.TraceMemoryFlag; import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.util.Swing; import ghidra.util.Swing;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
@@ -105,7 +105,7 @@ public class DebuggerTraceViewDiffPluginScreenShots extends GhidraScreenShotGene
@Test @Test
public void testCaptureDebuggerTimeSelectionDialog() throws Throwable { public void testCaptureDebuggerTimeSelectionDialog() throws Throwable {
DBTraceThread thread; TraceThread thread;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceTimeManager tm = tb.trace.getTimeManager(); DBTraceTimeManager tm = tb.trace.getTimeManager();
thread = tb.getOrAddThread("main", 0); thread = tb.getOrAddThread("main", 0);
@@ -30,7 +30,6 @@ import ghidra.framework.model.DomainFolder;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.test.ToyProgramBuilder; import ghidra.test.ToyProgramBuilder;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.memory.TraceMemoryFlag; import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
@@ -80,7 +79,7 @@ public class DebuggerMemviewPluginScreenShots extends GhidraScreenShotGenerator
private void populateTraceAndPrograms() throws Exception { private void populateTraceAndPrograms() throws Exception {
DomainFolder root = tool.getProject().getProjectData().getRootFolder(); DomainFolder root = tool.getProject().getProjectData().getRootFolder();
DBTraceThread thread1; TraceThread thread1;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
thread1 = tb.trace.getThreadManager().addThread("[0]", Range.openClosed(0L, 40L)); thread1 = tb.trace.getThreadManager().addThread("[0]", Range.openClosed(0L, 40L));
tb.trace.getThreadManager().addThread("[1]", Range.openClosed(3L, 50L)); tb.trace.getThreadManager().addThread("[1]", Range.openClosed(3L, 50L));
@@ -43,8 +43,7 @@ import docking.widgets.tree.GTreeNode;
import generic.Unique; import generic.Unique;
import ghidra.app.plugin.core.debug.gui.action.*; import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.mapping.*; import ghidra.app.plugin.core.debug.mapping.*;
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServiceInternal; import ghidra.app.plugin.core.debug.service.model.*;
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServiceProxyPlugin;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin; import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.app.util.viewer.listingpanel.ListingPanel;
@@ -521,6 +520,8 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
@After @After
public void tearDown() { public void tearDown() {
runSwing(() -> traceManager.setSaveTracesByDefault(false));
if (tb != null) { if (tb != null) {
if (traceManager != null && traceManager.getOpenTraces().contains(tb.trace)) { if (traceManager != null && traceManager.getOpenTraces().contains(tb.trace)) {
traceManager.closeTrace(tb.trace); traceManager.closeTrace(tb.trace);
@@ -531,8 +532,6 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
if (mb != null) { if (mb != null) {
if (mb.testModel != null) { if (mb.testModel != null) {
modelService.removeModel(mb.testModel); modelService.removeModel(mb.testModel);
runSwing(() -> traceManager.setSaveTracesByDefault(false));
for (TraceRecorder recorder : modelService.getTraceRecorders()) { for (TraceRecorder recorder : modelService.getTraceRecorders()) {
recorder.stopRecording(); recorder.stopRecording();
} }
@@ -588,6 +587,17 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
tb = new ToyDBTraceBuilder(trace); tb = new ToyDBTraceBuilder(trace);
} }
protected DebuggerTargetTraceMapper createTargetTraceMapper(TargetObject target)
throws Exception {
return new TestDebuggerTargetTraceMapper(target) {
@Override
public TraceRecorder startRecording(DebuggerModelServicePlugin service, Trace trace) {
useTrace(trace);
return super.startRecording(service, trace);
}
};
}
protected void createAndOpenTrace(String langID) throws IOException { protected void createAndOpenTrace(String langID) throws IOException {
createTrace(langID); createTrace(langID);
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
@@ -137,7 +137,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
createTestModel(); createTestModel();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
createProgramFromTrace(trace); createProgramFromTrace(trace);
intoProject(trace); intoProject(trace);
@@ -0,0 +1,98 @@
/* ###
* 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.breakpoint;
import java.io.IOException;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.trace.model.Trace;
import ghidra.util.database.UndoableTransaction;
public class DebuggerBreakpointsProviderObjectTest extends DebuggerBreakpointsProviderTest {
protected SchemaContext ctx;
@Override
protected void createTrace(String langID) throws IOException {
super.createTrace(langID);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
protected void useTrace(Trace trace) {
super.useTrace(trace);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
public void activateObjectsMode() throws Exception {
// NOTE the use of index='...' allowing object-based managers to ID unique path
// TODO: I guess this'll burn down if the naming scheme changes....
int index = tb.trace.getName().startsWith("[3]") ? 3 : 1;
ctx = XmlSchemaContext.deserialize("" + //
"<context>" + //
" <schema name='Session' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Processes' schema='ProcessContainer' />" + //
" </schema>" + //
" <schema name='ProcessContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element index='" + index + "' schema='Process' />" + // <---- NOTE HERE
" </schema>" + //
" <schema name='Process' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Threads' schema='ThreadContainer' />" + //
" <attribute name='Memory' schema='RegionContainer' />" + //
" <attribute name='Breakpoints' schema='BreakpointContainer' />" + //
" </schema>" + //
" <schema name='ThreadContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Thread' />" + //
" </schema>" + //
" <schema name='Thread' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Thread' />" + //
" </schema>" + //
" <schema name='RegionContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Region' />" + //
" </schema>" + //
" <schema name='Region' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='MemoryRegion' />" + //
" </schema>" + //
" <schema name='BreakpointContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Breakpoint' />" + //
" </schema>" + //
" <schema name='Breakpoint' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='BreakpointSpec' />" + //
" <interface name='BreakpointLocation' />" + //
" </schema>" + //
"</context>");
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getObjectManager().createRootObject(ctx.getSchema(new SchemaName("Session")));
}
}
}
@@ -121,7 +121,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
createTestModel(); createTestModel();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
addLiveMemoryAndBreakpoint(mb.testProcess1, recorder); addLiveMemoryAndBreakpoint(mb.testProcess1, recorder);
@@ -145,7 +145,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
createTestModel(); createTestModel();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
addLiveMemoryAndBreakpoint(mb.testProcess1, recorder); addLiveMemoryAndBreakpoint(mb.testProcess1, recorder);
@@ -210,7 +210,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
createTestModel(); createTestModel();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
createProgramFromTrace(trace); createProgramFromTrace(trace);
intoProject(trace); intoProject(trace);
@@ -471,7 +471,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
createTestModel(); createTestModel();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
createProgramFromTrace(trace); createProgramFromTrace(trace);
intoProject(trace); intoProject(trace);
@@ -512,12 +512,15 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
public void testActionFilters() throws Exception { public void testActionFilters() throws Exception {
createTestModel(); createTestModel();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder1 = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder1 = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace1 = recorder1.getTrace(); Trace trace1 = recorder1.getTrace();
TraceRecorder recorder3 = modelService.recordTarget(mb.testProcess3, TraceRecorder recorder3 = modelService.recordTarget(mb.testProcess3,
new TestDebuggerTargetTraceMapper(mb.testProcess3)); createTargetTraceMapper(mb.testProcess3));
Trace trace3 = recorder3.getTrace(); Trace trace3 = recorder3.getTrace();
createProgramFromTrace(trace1); createProgramFromTrace(trace1);
intoProject(trace1); intoProject(trace1);
intoProject(trace3); intoProject(trace3);
@@ -37,7 +37,6 @@ import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider; import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.services.DebuggerStaticMappingService; import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.TraceRecorder;
import ghidra.dbg.DebuggerModelListener; import ghidra.dbg.DebuggerModelListener;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@@ -457,9 +456,7 @@ public class DebuggerCopyActionsPluginTest extends AbstractGhidraHeadedDebuggerG
mb.testModel.addModelListener(listener); mb.testModel.addModelListener(listener);
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, modelService.recordTarget(mb.testProcess1, createTargetTraceMapper(mb.testProcess1));
new TestDebuggerTargetTraceMapper(mb.testProcess1));
useTrace(recorder.getTrace());
mb.testProcess1.memory.addRegion(".text", mb.rng(0x55550000, 0x5555ffff), "rx"); mb.testProcess1.memory.addRegion(".text", mb.rng(0x55550000, 0x5555ffff), "rx");
mb.testProcess1.memory.setMemory(mb.addr(0x55550000), mb.arr(1, 2, 3, 4, 5, 6, 7, 8)); mb.testProcess1.memory.setMemory(mb.addr(0x55550000), mb.arr(1, 2, 3, 4, 5, 6, 7, 8));
waitForPass(() -> { waitForPass(() -> {
@@ -53,11 +53,11 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.stack.DBTraceStack;
import ghidra.trace.database.stack.DBTraceStackManager; import ghidra.trace.database.stack.DBTraceStackManager;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.memory.*; import ghidra.trace.model.memory.*;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
@@ -632,7 +632,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx"); mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx");
@@ -960,7 +960,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x555500ff), "rx"); mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x555500ff), "rx");
@@ -1256,7 +1256,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
thread = tb.getOrAddThread("Thread 1", 0); thread = tb.getOrAddThread("Thread 1", 0);
DBTraceStackManager sm = tb.trace.getStackManager(); DBTraceStackManager sm = tb.trace.getStackManager();
DBTraceStack stack = sm.getStack(thread, 0, true); TraceStack stack = sm.getStack(thread, 0, true);
stack.getFrame(0, true).setProgramCounter(tb.addr(0x00401234)); stack.getFrame(0, true).setProgramCounter(tb.addr(0x00401234));
stack.getFrame(1, true).setProgramCounter(tb.addr(0x00404321)); stack.getFrame(1, true).setProgramCounter(tb.addr(0x00404321));
} }
@@ -1318,7 +1318,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
regs.setValue(0, new RegisterValue(pc, new BigInteger("00401234", 16))); regs.setValue(0, new RegisterValue(pc, new BigInteger("00401234", 16)));
DBTraceStackManager sm = tb.trace.getStackManager(); DBTraceStackManager sm = tb.trace.getStackManager();
DBTraceStack stack = sm.getStack(thread, 0, true); TraceStack stack = sm.getStack(thread, 0, true);
stack.getFrame(0, true); stack.getFrame(0, true);
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -1348,7 +1348,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
mm.addRegion("exe:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), mm.addRegion("exe:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
thread = tb.getOrAddThread("Thread 1", 0); thread = tb.getOrAddThread("Thread 1", 0);
DBTraceStack stack = sm.getStack(thread, 0, true); TraceStack stack = sm.getStack(thread, 0, true);
stack.getFrame(0, true).setProgramCounter(tb.addr(0x00401234)); stack.getFrame(0, true).setProgramCounter(tb.addr(0x00401234));
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -1358,7 +1358,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress()); assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress());
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceStack stack = sm.getStack(thread, 0, true); TraceStack stack = sm.getStack(thread, 0, true);
stack.getFrame(0, true).setProgramCounter(tb.addr(0x00404321)); stack.getFrame(0, true).setProgramCounter(tb.addr(0x00404321));
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -15,7 +15,7 @@
*/ */
package ghidra.app.plugin.core.debug.gui.memory; package ghidra.app.plugin.core.debug.gui.memory;
import static ghidra.lifecycle.Unfinished.*; import static ghidra.lifecycle.Unfinished.TODO;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.awt.*; import java.awt.*;
@@ -55,11 +55,11 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.memory.DBTraceMemoryManager; import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.stack.DBTraceStack;
import ghidra.trace.database.stack.DBTraceStackManager; import ghidra.trace.database.stack.DBTraceStackManager;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.*; import ghidra.trace.model.memory.*;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.database.UndoableTransaction; import ghidra.util.database.UndoableTransaction;
@@ -468,7 +468,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx"); mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx");
@@ -762,7 +762,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x555500ff), "rx"); mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x555500ff), "rx");
@@ -957,7 +957,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
thread = tb.getOrAddThread("Thread 1", 0); thread = tb.getOrAddThread("Thread 1", 0);
DBTraceStackManager sm = tb.trace.getStackManager(); DBTraceStackManager sm = tb.trace.getStackManager();
DBTraceStack stack = sm.getStack(thread, 0, true); TraceStack stack = sm.getStack(thread, 0, true);
stack.getFrame(0, true).setProgramCounter(tb.addr(0x00401234)); stack.getFrame(0, true).setProgramCounter(tb.addr(0x00401234));
stack.getFrame(1, true).setProgramCounter(tb.addr(0x00404321)); stack.getFrame(1, true).setProgramCounter(tb.addr(0x00404321));
} }
@@ -1019,7 +1019,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
regs.setValue(0, new RegisterValue(pc, new BigInteger("00401234", 16))); regs.setValue(0, new RegisterValue(pc, new BigInteger("00401234", 16)));
DBTraceStackManager sm = tb.trace.getStackManager(); DBTraceStackManager sm = tb.trace.getStackManager();
DBTraceStack stack = sm.getStack(thread, 0, true); TraceStack stack = sm.getStack(thread, 0, true);
stack.getFrame(0, true); stack.getFrame(0, true);
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -1049,7 +1049,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
mm.addRegion("exe:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), mm.addRegion("exe:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
thread = tb.getOrAddThread("Thread 1", 0); thread = tb.getOrAddThread("Thread 1", 0);
DBTraceStack stack = sm.getStack(thread, 0, true); TraceStack stack = sm.getStack(thread, 0, true);
stack.getFrame(0, true).setProgramCounter(tb.addr(0x00401234)); stack.getFrame(0, true).setProgramCounter(tb.addr(0x00401234));
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -1059,7 +1059,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
assertEquals(tb.addr(0x00401234), memBytesProvider.getLocation().getAddress()); assertEquals(tb.addr(0x00401234), memBytesProvider.getLocation().getAddress());
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceStack stack = sm.getStack(thread, 0, true); TraceStack stack = sm.getStack(thread, 0, true);
stack.getFrame(0, true).setProgramCounter(tb.addr(0x00404321)); stack.getFrame(0, true).setProgramCounter(tb.addr(0x00404321));
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -1074,7 +1074,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx"); mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx");
@@ -1106,7 +1106,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx"); mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx");
@@ -1150,7 +1150,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx"); mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx");
@@ -0,0 +1,59 @@
/* ###
* 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.memory;
import java.io.IOException;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.util.database.UndoableTransaction;
public class DebuggerRegionsProviderObjectTest extends DebuggerRegionsProviderTest {
protected SchemaContext ctx;
@Override
protected void createTrace(String langID) throws IOException {
super.createTrace(langID);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
public void activateObjectsMode() throws Exception {
ctx = XmlSchemaContext.deserialize("" + //
"<context>" + //
" <schema name='Session' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Memory' schema='RegionContainer' />" + //
" </schema>" + //
" <schema name='RegionContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Region' />" + //
" </schema>" + //
" <schema name='Region' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='MemoryRegion' />" + //
" </schema>" + //
"</context>");
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getObjectManager().createRootObject(ctx.getSchema(new SchemaName("Session")));
}
}
}
@@ -62,13 +62,13 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
protected void addRegions() throws Exception { protected void addRegions() throws Exception {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
regionExeText = mm.createRegion("Regions[/bin/echo 0x55550000]", 0, regionExeText = mm.createRegion("Memory[/bin/echo 0x55550000]", 0,
tb.range(0x55550000, 0x555500ff), TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); tb.range(0x55550000, 0x555500ff), TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
regionExeData = mm.createRegion("Regions[/bin/echo 0x55750000]", 0, regionExeData = mm.createRegion("Memory[/bin/echo 0x55750000]", 0,
tb.range(0x55750000, 0x5575007f), TraceMemoryFlag.READ, TraceMemoryFlag.WRITE); tb.range(0x55750000, 0x5575007f), TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
regionLibText = mm.createRegion("Regions[/lib/libc.so 0x7f000000]", 0, regionLibText = mm.createRegion("Memory[/lib/libc.so 0x7f000000]", 0,
tb.range(0x7f000000, 0x7f0003ff), TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); tb.range(0x7f000000, 0x7f0003ff), TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
regionLibData = mm.createRegion("Regions[/lib/libc.so 0x7f100000]", 0, regionLibData = mm.createRegion("Memory[/lib/libc.so 0x7f100000]", 0,
tb.range(0x7f100000, 0x7f10003f), TraceMemoryFlag.READ, TraceMemoryFlag.WRITE); tb.range(0x7f100000, 0x7f10003f), TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
} }
} }
@@ -104,7 +104,8 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceMemoryRegion region; TraceMemoryRegion region;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
region = mm.addRegion("bin:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), region = mm.addRegion("Memory[bin:.text]", Range.atLeast(0L),
tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
} }
@@ -114,7 +115,7 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
RegionRow row = Unique.assertOne(provider.regionTableModel.getModelData()); RegionRow row = Unique.assertOne(provider.regionTableModel.getModelData());
assertEquals(region, row.getRegion()); assertEquals(region, row.getRegion());
assertEquals("bin:.text", row.getName()); assertEquals("Memory[bin:.text]", row.getName());
assertEquals(tb.addr(0x00400000), row.getMinAddress()); assertEquals(tb.addr(0x00400000), row.getMinAddress());
assertEquals(tb.addr(0x0040ffff), row.getMaxAddress()); assertEquals(tb.addr(0x0040ffff), row.getMaxAddress());
assertEquals(tb.range(0x00400000, 0x0040ffff), row.getRange()); assertEquals(tb.range(0x00400000, 0x0040ffff), row.getRange());
@@ -132,7 +133,8 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceMemoryRegion region; TraceMemoryRegion region;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
region = mm.addRegion("bin:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), region = mm.addRegion("Memory[bin:.text]", Range.atLeast(0L),
tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
} }
@@ -149,7 +151,8 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceMemoryRegion region; TraceMemoryRegion region;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
region = mm.addRegion("bin:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), region = mm.addRegion("Memory[bin:.text]", Range.atLeast(0L),
tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
} }
@@ -174,7 +177,7 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
mm.addRegion("bin:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), mm.addRegion("Memory[bin:.text]", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
} }
@@ -197,7 +200,7 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
mm.addRegion("bin:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), mm.addRegion("Memory[bin:.text]", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -218,7 +221,8 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceMemoryRegion region; TraceMemoryRegion region;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
region = mm.addRegion("bin:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), region = mm.addRegion("Memory[bin:.text]", Range.atLeast(0L),
tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
} }
@@ -333,7 +337,8 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceMemoryRegion region; TraceMemoryRegion region;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
region = mm.addRegion("bin:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), region = mm.addRegion("Memory[bin:.text]", Range.atLeast(0L),
tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
} }
@@ -365,7 +370,8 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceMemoryRegion region; TraceMemoryRegion region;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
TraceMemoryManager mm = tb.trace.getMemoryManager(); TraceMemoryManager mm = tb.trace.getMemoryManager();
region = mm.addRegion("bin:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff), region = mm.addRegion("Memory[bin:.text]", Range.atLeast(0L),
tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
} }
@@ -0,0 +1,95 @@
/* ###
* 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.modules;
import java.io.IOException;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.trace.model.Trace;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.util.database.UndoableTransaction;
public class DebuggerModulesProviderObjectTest extends DebuggerModulesProviderTest {
protected SchemaContext ctx;
@Override
protected void createTrace(String langID) throws IOException {
super.createTrace(langID);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
protected void useTrace(Trace trace) {
super.useTrace(trace);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
public void activateObjectsMode() throws Exception {
// NOTE the use of index='1' allowing object-based managers to ID unique path
ctx = XmlSchemaContext.deserialize("" + //
"<context>" + //
" <schema name='Session' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Processes' schema='ProcessContainer' />" + //
" </schema>" + //
" <schema name='ProcessContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element index='1' schema='Process' />" + // <---- NOTE HERE
" </schema>" + //
" <schema name='Process' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Modules' schema='ModuleContainer' />" + //
" <attribute name='Memory' schema='RegionContainer' />" + //
" </schema>" + //
" <schema name='RegionContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Region' />" + //
" </schema>" + //
" <schema name='Region' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='MemoryRegion' />" + //
" </schema>" + //
" <schema name='ModuleContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Module' />" + //
" </schema>" + //
" <schema name='Module' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Module' />" + //
" <attribute name='Sections' schema='SectionContainer' />" + //
" </schema>" + //
" <schema name='SectionContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Section' />" + //
" </schema>" + //
" <schema name='Section' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Section' />" + //
" </schema>" + //
"</context>");
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getObjectManager().createRootObject(ctx.getSchema(new SchemaName("Session")));
}
}
}
@@ -100,7 +100,8 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
else { else {
throw new AssertionError(); throw new AssertionError();
} }
manager.addRegion(module.getName() + ":" + section.getName(), manager.addRegion(
"Processes[1].Memory[" + module.getName() + ":" + section.getName() + "]",
module.getLifespan(), section.getRange(), flags); module.getLifespan(), section.getRange(), flags);
} }
} }
@@ -110,19 +111,19 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
protected void addModules() throws Exception { protected void addModules() throws Exception {
TraceModuleManager manager = tb.trace.getModuleManager(); TraceModuleManager manager = tb.trace.getModuleManager();
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
modExe = manager.addLoadedModule("first_proc", "first_proc", modExe = manager.addLoadedModule("Processes[1].Modules[first_proc]", "first_proc",
tb.range(0x55550000, 0x5575007f), 0); tb.range(0x55550000, 0x5575007f), 0);
secExeText = secExeText = modExe.addSection("Processes[1].Modules[first_proc].Sections[.text]",
modExe.addSection("first_proc[.text]", ".text", tb.range(0x55550000, 0x555500ff)); ".text", tb.range(0x55550000, 0x555500ff));
secExeData = secExeData = modExe.addSection("Processes[1].Modules[first_proc].Sections[.data]",
modExe.addSection("first_proc[.data]", ".data", tb.range(0x55750000, 0x5575007f)); ".data", tb.range(0x55750000, 0x5575007f));
modLib = manager.addLoadedModule("some_lib", "some_lib", modLib = manager.addLoadedModule("Processes[1].Modules[some_lib]", "some_lib",
tb.range(0x7f000000, 0x7f10003f), 0); tb.range(0x7f000000, 0x7f10003f), 0);
secLibText = secLibText = modLib.addSection("Processes[1].Modules[some_lib].Sections[.text]",
modLib.addSection("some_lib[.text]", ".text", tb.range(0x7f000000, 0x7f0003ff)); ".text", tb.range(0x7f000000, 0x7f0003ff));
secLibData = secLibData = modLib.addSection("Processes[1].Modules[some_lib].Sections[.data]",
modLib.addSection("some_lib[.data]", ".data", tb.range(0x7f100000, 0x7f10003f)); ".data", tb.range(0x7f100000, 0x7f10003f));
} }
} }
@@ -144,8 +145,10 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
} }
protected void assertProviderPopulated() { protected void assertProviderPopulated() {
List<ModuleRow> modulesDisplayed = modulesProvider.moduleTableModel.getModelData(); List<ModuleRow> modulesDisplayed =
// I should be able to assume this is sorted by base address new ArrayList<>(modulesProvider.moduleTableModel.getModelData());
modulesDisplayed.sort(Comparator.comparing(r -> r.getBase()));
// I should be able to assume this is sorted by base address. It's the default sort column.
assertEquals(2, modulesDisplayed.size()); assertEquals(2, modulesDisplayed.size());
ModuleRow execRow = modulesDisplayed.get(0); ModuleRow execRow = modulesDisplayed.get(0);
@@ -156,7 +159,9 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
ModuleRow libRow = modulesDisplayed.get(1); ModuleRow libRow = modulesDisplayed.get(1);
assertEquals(tb.addr(0x7f000000), libRow.getBase()); assertEquals(tb.addr(0x7f000000), libRow.getBase());
List<SectionRow> sectionsDisplayed = modulesProvider.sectionTableModel.getModelData(); List<SectionRow> sectionsDisplayed =
new ArrayList<>(modulesProvider.sectionTableModel.getModelData());
sectionsDisplayed.sort(Comparator.comparing(r -> r.getStart()));
assertEquals(4, sectionsDisplayed.size()); assertEquals(4, sectionsDisplayed.size());
SectionRow execTextRow = sectionsDisplayed.get(0); SectionRow execTextRow = sectionsDisplayed.get(0);
@@ -261,13 +266,17 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
List<ModuleRow> modulesDisplayed = modulesProvider.moduleTableModel.getModelData(); List<ModuleRow> modulesDisplayed =
new ArrayList<>(modulesProvider.moduleTableModel.getModelData());
modulesDisplayed.sort(Comparator.comparing(r -> r.getBase()));
assertEquals(1, modulesDisplayed.size()); assertEquals(1, modulesDisplayed.size());
ModuleRow libRow = modulesDisplayed.get(0); ModuleRow libRow = modulesDisplayed.get(0);
assertEquals("some_lib", libRow.getName()); assertEquals("some_lib", libRow.getName());
List<SectionRow> sectionsDisplayed = modulesProvider.sectionTableModel.getModelData(); List<SectionRow> sectionsDisplayed =
new ArrayList<>(modulesProvider.sectionTableModel.getModelData());
sectionsDisplayed.sort(Comparator.comparing(r -> r.getStart()));
assertEquals(2, sectionsDisplayed.size()); assertEquals(2, sectionsDisplayed.size());
SectionRow libTextRow = sectionsDisplayed.get(0); SectionRow libTextRow = sectionsDisplayed.get(0);
@@ -543,13 +552,15 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
// TODO: A region should not be required first. Just to get a memMapper? // TODO: A region should not be required first. Just to get a memMapper?
mb.testProcess1.addRegion("first_proc:.text", mb.rng(0x55550000, 0x555500ff), "rx"); mb.testProcess1.addRegion("Memory[first_proc:.text]", mb.rng(0x55550000, 0x555500ff),
"rx");
TestTargetModule module = TestTargetModule module =
mb.testProcess1.modules.addModule("first_proc", mb.rng(0x55550000, 0x555500ff)); mb.testProcess1.modules.addModule("Modules[first_proc]",
mb.rng(0x55550000, 0x555500ff));
// NOTE: A section should not be required at this point. // NOTE: A section should not be required at this point.
TestTargetTypedefDataType typedef = module.types.addTypedefDataType("myInt", TestTargetTypedefDataType typedef = module.types.addTypedefDataType("myInt",
new DefaultTargetPrimitiveDataType(PrimitiveKind.SINT, 4)); new DefaultTargetPrimitiveDataType(PrimitiveKind.SINT, 4));
@@ -577,7 +588,7 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
conv.convertTargetDataType(typedef).get(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS); conv.convertTargetDataType(typedef).get(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS);
// TODO: Some heuristic or convention to extract the module name, if applicable // TODO: Some heuristic or convention to extract the module name, if applicable
waitForPass(() -> { waitForPass(() -> {
DataType actType = dtm.getDataType("/Processes[1].Modules[first_proc].Types/myInt"); DataType actType = dtm.getDataType("/Modules[first_proc].Types/myInt");
assertTypeEquals(expType, actType); assertTypeEquals(expType, actType);
}); });
@@ -596,11 +607,12 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerGUI
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
// TODO: A region should not be required first. Just to get a memMapper? // TODO: A region should not be required first. Just to get a memMapper?
mb.testProcess1.addRegion("first_proc:.text", mb.rng(0x55550000, 0x555500ff), "rx"); mb.testProcess1.addRegion("first_proc:.text", mb.rng(0x55550000, 0x555500ff),
"rx");
TestTargetModule module = TestTargetModule module =
mb.testProcess1.modules.addModule("first_proc", mb.rng(0x55550000, 0x555500ff)); mb.testProcess1.modules.addModule("first_proc", mb.rng(0x55550000, 0x555500ff));
// NOTE: A section should not be required at this point. // NOTE: A section should not be required at this point.
@@ -146,7 +146,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
Register::isBaseRegister); Register::isBaseRegister);
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
waitFor(() -> { waitFor(() -> {
TraceThread thread = recorder.getTraceThread(mb.testThread1); TraceThread thread = recorder.getTraceThread(mb.testThread1);
@@ -0,0 +1,96 @@
/* ###
* 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.stack;
import java.io.IOException;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.trace.model.Trace;
import ghidra.util.database.UndoableTransaction;
public class DebuggerStackProviderObjectTest extends DebuggerStackProviderTest {
protected SchemaContext ctx;
@Override
protected void createTrace(String langID) throws IOException {
super.createTrace(langID);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
protected void useTrace(Trace trace) {
super.useTrace(trace);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
public void activateObjectsMode() throws Exception {
// NOTE the use of index='1' allowing object-based managers to ID unique path
ctx = XmlSchemaContext.deserialize("" + //
"<context>" + //
" <schema name='Session' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Processes' schema='ProcessContainer' />" + //
" </schema>" + //
" <schema name='ProcessContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element index='1' schema='Process' />" + // <---- NOTE HERE
" </schema>" + //
" <schema name='Process' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Threads' schema='ThreadContainer' />" + //
" <attribute name='Memory' schema='RegionContainer' />" + //
" </schema>" + //
" <schema name='ThreadContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Thread' />" + //
" </schema>" + //
" <schema name='Thread' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Thread' />" + //
" <attribute name='Stack' schema='Stack' />" + //
" </schema>" + //
" <schema name='Stack' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <interface name='Stack' />" + //
" <element schema='Frame' />" + //
" </schema>" + //
" <schema name='Frame' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='StackFrame' />" + //
" </schema>" + //
" <schema name='RegionContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Region' />" + //
" </schema>" + //
" <schema name='Region' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='MemoryRegion' />" + //
" </schema>" + //
"</context>");
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getObjectManager().createRootObject(ctx.getSchema(new SchemaName("Session")));
}
}
}
@@ -155,7 +155,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testActivateThreadNoStackNoRegsEmpty() throws Exception { public void testActivateThreadNoStackNoRegsEmpty() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
traceManager.activateThread(thread); traceManager.activateThread(thread);
@@ -168,7 +168,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testActivateThreadNoStackRegsSynthetic() throws Exception { public void testActivateThreadNoStackRegsSynthetic() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
addRegVals(thread); addRegVals(thread);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -182,7 +182,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testActivateThreadRegsThenAddEmptyStackEmpty() throws Exception { public void testActivateThreadRegsThenAddEmptyStackEmpty() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
addRegVals(thread); addRegVals(thread);
addStack(thread); addStack(thread);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -197,7 +197,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testActivateThreadThenAddStackPopulatesProvider() throws Exception { public void testActivateThreadThenAddStackPopulatesProvider() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
traceManager.activateThread(thread); traceManager.activateThread(thread);
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
@@ -210,7 +210,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testAddStackThenActivateThreadPopulatesProvider() throws Exception { public void testAddStackThenActivateThreadPopulatesProvider() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -225,7 +225,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testAppendStackUpdatesProvider() throws Exception { public void testAppendStackUpdatesProvider() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -250,7 +250,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testPushStackUpdatesProvider() throws Exception { public void testPushStackUpdatesProvider() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -275,7 +275,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testTruncateStackUpdatesProvider() throws Exception { public void testTruncateStackUpdatesProvider() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -298,7 +298,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testPopStackUpdatesProvider() throws Exception { public void testPopStackUpdatesProvider() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -321,7 +321,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testDeleteStackUpdatesProvider() throws Exception { public void testDeleteStackUpdatesProvider() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -343,8 +343,8 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testActivateOtherThread() throws Exception { public void testActivateOtherThread() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread1 = addThread("Thread 1"); TraceThread thread1 = addThread("Processes[1].Threads[1]");
TraceThread thread2 = addThread("Thread 2"); TraceThread thread2 = addThread("Processes[1].Threads[2]");
TraceStack stack = addStack(thread1); TraceStack stack = addStack(thread1);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -364,7 +364,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testActivateSnap() throws Exception { public void testActivateSnap() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -389,7 +389,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testCloseCurrentTraceEmpty() throws Exception { public void testCloseCurrentTraceEmpty() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -410,7 +410,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testSelectRowActivatesFrame() throws Exception { public void testSelectRowActivatesFrame() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -435,7 +435,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
public void testActivateFrameSelectsRow() throws Exception { public void testActivateFrameSelectsRow() throws Exception {
createAndOpenTrace(); createAndOpenTrace();
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -467,7 +467,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
programManager.openProgram(program); programManager.openProgram(program);
TraceThread thread = addThread("Thread 1"); TraceThread thread = addThread("Processes[1].Threads[1]");
TraceStack stack = addStack(thread); TraceStack stack = addStack(thread);
addStackFrames(stack); addStackFrames(stack);
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
@@ -491,7 +491,8 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getMemoryManager() tb.trace.getMemoryManager()
.addRegion("bin:.text", Range.atLeast(0L), tb.drng(0x00400000, 0x00400fff), .addRegion("Processes[1].Memory[bin:.text]", Range.atLeast(0L),
tb.drng(0x00400000, 0x00400fff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE); TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
TraceLocation dloc = TraceLocation dloc =
@@ -0,0 +1,79 @@
/* ###
* 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.thread;
import java.io.IOException;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.trace.model.Trace;
import ghidra.util.database.UndoableTransaction;
public class DebuggerThreadsProviderObjectTest extends DebuggerThreadsProviderTest {
protected SchemaContext ctx;
@Override
protected void createTrace(String langID) throws IOException {
super.createTrace(langID);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
protected void useTrace(Trace trace) {
super.useTrace(trace);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
public void activateObjectsMode() throws Exception {
// NOTE the use of index='1' allowing object-based managers to ID unique path
ctx = XmlSchemaContext.deserialize("" + //
"<context>" + //
" <schema name='Session' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Processes' schema='ProcessContainer' />" + //
" </schema>" + //
" <schema name='ProcessContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element index='1' schema='Process' />" + // <---- NOTE HERE
" </schema>" + //
" <schema name='Process' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Threads' schema='ThreadContainer' />" + //
" </schema>" + //
" <schema name='ThreadContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Thread' />" + //
" </schema>" + //
" <schema name='Thread' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Thread' />" + //
" </schema>" + //
"</context>");
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getObjectManager().createRootObject(ctx.getSchema(new SchemaName("Session")));
}
}
}
@@ -55,9 +55,9 @@ public class DebuggerThreadsProviderTest extends AbstractGhidraHeadedDebuggerGUI
protected void addThreads() throws Exception { protected void addThreads() throws Exception {
TraceThreadManager manager = tb.trace.getThreadManager(); TraceThreadManager manager = tb.trace.getThreadManager();
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
thread1 = manager.addThread("Thread 1", Range.atLeast(0L)); thread1 = manager.addThread("Processes[1].Threads[1]", Range.atLeast(0L));
thread1.setComment("A comment"); thread1.setComment("A comment");
thread2 = manager.addThread("Thread 2", Range.closed(5L, 10L)); thread2 = manager.addThread("Processes[1].Threads[2]", Range.closed(5L, 10L));
thread2.setComment("Another comment"); thread2.setComment("Another comment");
} }
} }
@@ -99,7 +99,7 @@ public class DebuggerThreadsProviderTest extends AbstractGhidraHeadedDebuggerGUI
ThreadRow thread1Record = threadsDisplayed.get(0); ThreadRow thread1Record = threadsDisplayed.get(0);
assertEquals(thread1, thread1Record.getThread()); assertEquals(thread1, thread1Record.getThread());
assertEquals("Thread 1", thread1Record.getName()); assertEquals("Processes[1].Threads[1]", thread1Record.getName());
assertEquals(Range.atLeast(0L), thread1Record.getLifespan()); assertEquals(Range.atLeast(0L), thread1Record.getLifespan());
assertEquals(0, thread1Record.getCreationSnap()); assertEquals(0, thread1Record.getCreationSnap());
assertEquals("", thread1Record.getDestructionSnap()); assertEquals("", thread1Record.getDestructionSnap());
@@ -475,13 +475,15 @@ public class DebuggerThreadsProviderTest extends AbstractGhidraHeadedDebuggerGUI
// Not live, so no seek // Not live, so no seek
assertEquals(0, traceManager.getCurrentSnap()); assertEquals(0, traceManager.getCurrentSnap());
tb.close();
createTestModel(); createTestModel();
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
// Threads needs registers to be recognized by the recorder // Threads needs registers to be recognized by the recorder
mb.createTestThreadRegisterBanks(); mb.createTestThreadRegisterBanks();
TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1, TraceRecorder recorder = modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
// Wait till two threads are observed in the database // Wait till two threads are observed in the database
@@ -208,7 +208,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
mb.testProcess1.memory.writeMemory(mb.addr(0x00400000), tb.arr(1, 2, 3, 4)); mb.testProcess1.memory.writeMemory(mb.addr(0x00400000), tb.arr(1, 2, 3, 4));
recorder = modelService.recordTarget(mb.testProcess1, recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
TraceThread thread = waitForValue(() -> recorder.getTraceThread(mb.testThread1)); TraceThread thread = waitForValue(() -> recorder.getTraceThread(mb.testThread1));
@@ -348,7 +348,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
mb.testProcess1.addRegion(".text", mb.rng(0x00400000, 0x00401000), "rx"); mb.testProcess1.addRegion(".text", mb.rng(0x00400000, 0x00401000), "rx");
recorder = modelService.recordTarget(mb.testProcess1, recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
TraceThread thread = waitForValue(() -> recorder.getTraceThread(mb.testThread1)); TraceThread thread = waitForValue(() -> recorder.getTraceThread(mb.testThread1));
@@ -0,0 +1,99 @@
/* ###
* 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.breakpoint;
import java.io.IOException;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.trace.model.Trace;
import ghidra.util.database.UndoableTransaction;
public class DebuggerLogicalBreakpointServiceObjectTest
extends DebuggerLogicalBreakpointServiceTest {
protected SchemaContext ctx;
@Override
protected void createTrace(String langID) throws IOException {
super.createTrace(langID);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
protected void useTrace(Trace trace) {
super.useTrace(trace);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
public void activateObjectsMode() throws Exception {
// NOTE the use of index='...' allowing object-based managers to ID unique path
// TODO: I guess this'll burn down if the naming scheme changes....
int index = tb.trace.getName().startsWith("[3]") ? 3 : 1;
ctx = XmlSchemaContext.deserialize("" + //
"<context>" + //
" <schema name='Session' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Processes' schema='ProcessContainer' />" + //
" </schema>" + //
" <schema name='ProcessContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element index='" + index + "' schema='Process' />" + // <---- NOTE HERE
" </schema>" + //
" <schema name='Process' elementResync='NEVER' attributeResync='ONCE'>" + //
" <attribute name='Threads' schema='ThreadContainer' />" + //
" <attribute name='Memory' schema='RegionContainer' />" + //
" <attribute name='Breakpoints' schema='BreakpointContainer' />" + //
" </schema>" + //
" <schema name='ThreadContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Thread' />" + //
" </schema>" + //
" <schema name='Thread' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='Thread' />" + //
" </schema>" + //
" <schema name='RegionContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Region' />" + //
" </schema>" + //
" <schema name='Region' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='MemoryRegion' />" + //
" </schema>" + //
" <schema name='BreakpointContainer' canonical='yes' elementResync='NEVER' " + //
" attributeResync='ONCE'>" + //
" <element schema='Breakpoint' />" + //
" </schema>" + //
" <schema name='Breakpoint' elementResync='NEVER' attributeResync='NEVER'>" + //
" <interface name='BreakpointSpec' />" + //
" <interface name='BreakpointLocation' />" + //
" </schema>" + //
"</context>");
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getObjectManager().createRootObject(ctx.getSchema(new SchemaName("Session")));
}
}
}
@@ -74,7 +74,8 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
@Override @Override
public synchronized void breakpointUpdated(LogicalBreakpoint lb) { public synchronized void breakpointUpdated(LogicalBreakpoint lb) {
Msg.debug(this, "LogicalBreakpoint updated: " + lb); Msg.debug(this,
"LogicalBreakpoint updated: (" + System.identityHashCode(lb) + ")" + lb);
assertTrue(current.contains(lb)); assertTrue(current.contains(lb));
} }
@@ -165,12 +166,12 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
public void startRecorder1() throws Throwable { public void startRecorder1() throws Throwable {
recorder1 = modelService.recordTarget(mb.testProcess1, recorder1 = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
} }
public void startRecorder3() throws Throwable { public void startRecorder3() throws Throwable {
recorder3 = modelService.recordTarget(mb.testProcess3, recorder3 = modelService.recordTarget(mb.testProcess3,
new TestDebuggerTargetTraceMapper(mb.testProcess3)); createTargetTraceMapper(mb.testProcess3));
} }
@After @After
@@ -213,7 +213,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
assertEquals(Set.of(), Set.copyOf(modelService.getTraceRecorders())); assertEquals(Set.of(), Set.copyOf(modelService.getTraceRecorders()));
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
assertEquals(Set.of(recorder), Set.copyOf(modelService.getTraceRecorders())); assertEquals(Set.of(recorder), Set.copyOf(modelService.getTraceRecorders()));
} }
@@ -228,7 +228,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
new CollectionChangeDelegateWrapper<>(recorderChangeListener); new CollectionChangeDelegateWrapper<>(recorderChangeListener);
modelService.addTraceRecordersChangedListener(wrapper); modelService.addTraceRecordersChangedListener(wrapper);
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
new VerificationsInOrder() { new VerificationsInOrder() {
{ {
@@ -243,7 +243,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
// Strong ref // Strong ref
CollectionChangeDelegateWrapper<TraceRecorder> wrapper = CollectionChangeDelegateWrapper<TraceRecorder> wrapper =
new CollectionChangeDelegateWrapper<>(recorderChangeListener); new CollectionChangeDelegateWrapper<>(recorderChangeListener);
@@ -265,7 +265,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
assertNotNull(recorder); assertNotNull(recorder);
waitOn(recorder.init()); // Already initializing, just wait for it to complete waitOn(recorder.init()); // Already initializing, just wait for it to complete
@@ -281,7 +281,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
modelService.recordTargetAndActivateTrace(mb.testProcess1, modelService.recordTargetAndActivateTrace(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
waitForSwing(); waitForSwing();
Trace trace = traceManager.getCurrentTrace(); Trace trace = traceManager.getCurrentTrace();
@@ -290,7 +290,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
traceManager.closeTrace(trace); traceManager.closeTrace(trace);
waitOn(mb.testModel.close()); waitOn(mb.testModel.close());
waitForPass(() -> { waitForPass(() -> {
assertEquals(List.of(), trace.getConsumerList()); assertEquals(List.of(tb), trace.getConsumerList());
}); });
} }
@@ -300,7 +300,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
assertEquals(recorder, modelService.getRecorder(mb.testProcess1)); assertEquals(recorder, modelService.getRecorder(mb.testProcess1));
} }
@@ -311,7 +311,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
assertEquals(recorder, modelService.getRecorder(recorder.getTrace())); assertEquals(recorder, modelService.getRecorder(recorder.getTrace()));
} }
@@ -322,7 +322,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
assertEquals(mb.testProcess1, modelService.getTarget(recorder.getTrace())); assertEquals(mb.testProcess1, modelService.getTarget(recorder.getTrace()));
} }
@@ -333,7 +333,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
assertEquals(recorder.getTrace(), modelService.getTrace(mb.testProcess1)); assertEquals(recorder.getTrace(), modelService.getTrace(mb.testProcess1));
} }
@@ -344,7 +344,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
// The most complicated case, lest I want another dimension in a cross product // The most complicated case, lest I want another dimension in a cross product
mb.createTestThreadStacksAndFramesHaveRegisterBanks(); mb.createTestThreadStacksAndFramesHaveRegisterBanks();
@@ -373,7 +373,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
preRec.run(); preRec.run();
modelService.recordTarget(mb.testProcess1, modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
postRec.run(); postRec.run();
@@ -420,7 +420,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
modelService.recordTarget(mb.testProcess1, modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
// The most complicated case, lest I want another dimension in a cross product // The most complicated case, lest I want another dimension in a cross product
mb.createTestThreadStacksAndFramesHaveRegisterBanks(); mb.createTestThreadStacksAndFramesHaveRegisterBanks();
@@ -439,9 +439,9 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
// NOTE: getTargetFocus assumes the target is being recorded // NOTE: getTargetFocus assumes the target is being recorded
modelService.recordTarget(mb.testProcess1, modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
modelService.recordTarget(mb.testProcess3, modelService.recordTarget(mb.testProcess3,
new TestDebuggerTargetTraceMapper(mb.testProcess3)); createTargetTraceMapper(mb.testProcess3));
assertNull(modelService.getTargetFocus(mb.testProcess1)); assertNull(modelService.getTargetFocus(mb.testProcess1));
assertNull(modelService.getTargetFocus(mb.testProcess3)); assertNull(modelService.getTargetFocus(mb.testProcess3));
@@ -48,7 +48,7 @@ public class DefaultTraceRecorderTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
waitForPass(() -> { waitForPass(() -> {
assertNotNull(recorder.getTraceThread(mb.testThread1)); assertNotNull(recorder.getTraceThread(mb.testThread1));
assertNotNull(recorder.getTraceThread(mb.testThread2)); assertNotNull(recorder.getTraceThread(mb.testThread2));
@@ -61,7 +61,7 @@ public class DefaultTraceRecorderTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
TestTargetMemoryRegion targetRegion = TestTargetMemoryRegion targetRegion =
@@ -97,7 +97,7 @@ public class DefaultTraceRecorderTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
Language lang = trace.getBaseLanguage(); Language lang = trace.getBaseLanguage();
Register r0 = lang.getRegister("r0"); Register r0 = lang.getRegister("r0");
@@ -131,7 +131,7 @@ public class DefaultTraceRecorderTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
Language lang = trace.getBaseLanguage(); Language lang = trace.getBaseLanguage();
Register pc = lang.getRegister("pc"); Register pc = lang.getRegister("pc");
@@ -180,7 +180,7 @@ public class DefaultTraceRecorderTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
Language lang = trace.getBaseLanguage(); Language lang = trace.getBaseLanguage();
Register pc = lang.getRegister("pc"); Register pc = lang.getRegister("pc");
@@ -229,7 +229,7 @@ public class DefaultTraceRecorderTest extends AbstractGhidraHeadedDebuggerGUITes
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
Language lang = trace.getBaseLanguage(); Language lang = trace.getBaseLanguage();
Register pc = lang.getRegister("pc"); Register pc = lang.getRegister("pc");
@@ -31,7 +31,6 @@ import ghidra.dbg.model.TestTargetStack;
import ghidra.dbg.model.TestTargetStackFrameHasRegisterBank; import ghidra.dbg.model.TestTargetStackFrameHasRegisterBank;
import ghidra.dbg.testutil.DebuggerModelTestUtils; import ghidra.dbg.testutil.DebuggerModelTestUtils;
import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFile;
import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.stack.TraceStack; import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
@@ -116,7 +115,7 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
assertNull(traceManager.getCurrentThread()); assertNull(traceManager.getCurrentThread());
DBTraceThread thread; TraceThread thread;
try (UndoableTransaction tid = tb.startTransaction()) { try (UndoableTransaction tid = tb.startTransaction()) {
thread = tb.getOrAddThread("Thread 1", 0); thread = tb.getOrAddThread("Thread 1", 0);
} }
@@ -297,7 +296,7 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
traceManager.openTrace(trace); traceManager.openTrace(trace);
@@ -338,7 +337,7 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
waitForValue(() -> modelService.getTarget(trace)); waitForValue(() -> modelService.getTarget(trace));
@@ -400,7 +399,7 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
mb.createTestProcessesAndThreads(); mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
traceManager.openTrace(trace); traceManager.openTrace(trace);
@@ -50,7 +50,7 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
"r1", new byte[] { 6 }))); "r1", new byte[] { 6 })));
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
TraceThread thread = waitForValue(() -> recorder.getTraceThread(mb.testThread1)); TraceThread thread = waitForValue(() -> recorder.getTraceThread(mb.testThread1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
@@ -93,7 +93,7 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
"r1", new byte[] { 6 }))); "r1", new byte[] { 6 })));
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1, TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1)); createTargetTraceMapper(mb.testProcess1));
TraceThread thread = waitForValue(() -> recorder.getTraceThread(mb.testThread1)); TraceThread thread = waitForValue(() -> recorder.getTraceThread(mb.testThread1));
Trace trace = recorder.getTrace(); Trace trace = recorder.getTrace();
@@ -718,6 +718,17 @@ public interface TargetObjectSchema {
return null; return null;
} }
/**
* Find the nearest ancestor implementing the given interface along the given path
*
* <p>
* If the given path implements the interface, it is returned, i.e., it is not strictly an
* ancestor.
*
* @param type the interface to search for
* @param path the seed path
* @return the found path, or {@code null} if no ancestor implements the interface
*/
default List<String> searchForAncestor(Class<? extends TargetObject> type, List<String> path) { default List<String> searchForAncestor(Class<? extends TargetObject> type, List<String> path) {
for (; path != null; path = PathUtils.parent(path)) { for (; path != null; path = PathUtils.parent(path)) {
TargetObjectSchema schema = getSuccessorSchema(path); TargetObjectSchema schema = getSuccessorSchema(path);
@@ -38,6 +38,23 @@ public class PathMatcher implements PathPredicates {
return String.format("<PathMatcher\n %s\n>", StringUtils.join(patterns, "\n ")); return String.format("<PathMatcher\n %s\n>", StringUtils.join(patterns, "\n "));
} }
@Override
public PathPredicates or(PathPredicates that) {
PathMatcher result = new PathMatcher();
result.patterns.addAll(this.patterns);
if (that instanceof PathMatcher) {
PathMatcher matcher = (PathMatcher) that;
result.patterns.addAll(matcher.patterns);
}
else if (that instanceof PathPattern) {
result.patterns.add((PathPattern) that);
}
else {
throw new AssertionError();
}
return result;
}
/** /**
* TODO: We could probably do a lot better, esp. for many patterns, by using a trie. * TODO: We could probably do a lot better, esp. for many patterns, by using a trie.
*/ */
@@ -81,6 +98,23 @@ public class PathMatcher implements PathPredicates {
return patterns.iterator().next(); return patterns.iterator().next();
} }
@Override
public Set<String> getNextKeys(List<String> path) {
Set<String> result = new HashSet<>();
for (PathPattern pattern : patterns) {
result.addAll(pattern.getNextKeys(path));
}
if (result.contains("")) {
result.removeIf(PathUtils::isName);
result.add("");
}
if (result.contains("[]")) {
result.removeIf(PathUtils::isIndex);
result.add("[]");
}
return result;
}
@Override @Override
public Set<String> getNextNames(List<String> path) { public Set<String> getNextNames(List<String> path) {
Set<String> result = new HashSet<>(); Set<String> result = new HashSet<>();
@@ -111,10 +145,10 @@ public class PathMatcher implements PathPredicates {
} }
@Override @Override
public PathMatcher applyIndices(List<String> indices) { public PathMatcher applyKeys(List<String> indices) {
PathMatcher result = new PathMatcher(); PathMatcher result = new PathMatcher();
for (PathPattern pat : patterns) { for (PathPattern pat : patterns) {
result.addPattern(pat.applyIndices(indices)); result.addPattern(pat.applyKeys(indices));
} }
return result; return result;
} }
@@ -62,26 +62,33 @@ public class PathPattern implements PathPredicates {
return pattern.hashCode(); return pattern.hashCode();
} }
@Override
public PathPredicates or(PathPredicates that) {
if (this.equals(that)) {
return this;
}
PathMatcher result = new PathMatcher();
result.addPattern(this);
if (that instanceof PathPattern) {
result.addPattern(this);
}
else if (that instanceof PathMatcher) {
PathMatcher matcher = (PathMatcher) that;
result.patterns.addAll(matcher.patterns);
}
else {
throw new AssertionError();
}
return result;
}
public static boolean isWildcard(String pat) { public static boolean isWildcard(String pat) {
return "[]".equals(pat) || "".equals(pat); return "[]".equals(pat) || "".equals(pat);
} }
public static boolean keyMatches(String pat, String key) {
if (key.equals(pat)) {
return true;
}
if ("[]".equals(pat) && PathUtils.isIndex(key)) {
return true;
}
if ("".equals(pat) && PathUtils.isName(key)) {
return true;
}
return false;
}
protected boolean matchesUpTo(List<String> path, int length) { protected boolean matchesUpTo(List<String> path, int length) {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
if (!keyMatches(pattern.get(i), path.get(i))) { if (!PathPredicates.keyMatches(pattern.get(i), path.get(i))) {
return false; return false;
} }
} }
@@ -144,11 +151,25 @@ public class PathPattern implements PathPredicates {
return this; return this;
} }
@Override
public Set<String> getNextKeys(List<String> path) {
if (path.size() >= pattern.size()) {
return Set.of();
}
if (!matchesUpTo(path, path.size())) {
return Set.of();
}
return Set.of(pattern.get(path.size()));
}
@Override @Override
public Set<String> getNextNames(List<String> path) { public Set<String> getNextNames(List<String> path) {
if (path.size() >= pattern.size()) { if (path.size() >= pattern.size()) {
return Set.of(); return Set.of();
} }
if (!matchesUpTo(path, path.size())) {
return Set.of();
}
String pat = pattern.get(path.size()); String pat = pattern.get(path.size());
if (PathUtils.isName(pat)) { if (PathUtils.isName(pat)) {
return Set.of(pat); return Set.of(pat);
@@ -161,6 +182,9 @@ public class PathPattern implements PathPredicates {
if (path.size() >= pattern.size()) { if (path.size() >= pattern.size()) {
return Set.of(); return Set.of();
} }
if (!matchesUpTo(path, path.size())) {
return Set.of();
}
String pat = pattern.get(path.size()); String pat = pattern.get(path.size());
if (PathUtils.isIndex(pat)) { if (PathUtils.isIndex(pat)) {
return Set.of(PathUtils.parseIndex(pat)); return Set.of(PathUtils.parseIndex(pat));
@@ -174,7 +198,7 @@ public class PathPattern implements PathPredicates {
} }
@Override @Override
public PathPattern applyIndices(List<String> indices) { public PathPattern applyKeys(List<String> indices) {
List<String> result = new ArrayList<>(pattern.size()); List<String> result = new ArrayList<>(pattern.size());
Iterator<String> it = indices.iterator(); Iterator<String> it = indices.iterator();
for (String pat : pattern) { for (String pat : pattern) {
@@ -196,18 +220,18 @@ public class PathPattern implements PathPredicates {
} }
/** /**
* If the given path matches, extract indices where matched by wildcards * If the given path matches, extract keys where matched by wildcards
* *
* <p> * <p>
* This is essentially the inverse of {@link #applyIndices(List)}, but can only be asked of one * This is essentially the inverse of {@link #applyKeys(List)}, but can only be asked of one
* pattern. The keys are returned from left to right, in the order matched by the pattern. Only * pattern. The keys are returned from left to right, in the order matched by the pattern. Only
* those keys matched by a wildcard are included in the result. Indices are extracted with the * those keys matched by a wildcard are included in the result. Indices are extracted with the
* brackets {@code []} removed. * brackets {@code []} removed.
* *
* @param path the path to match * @param path the path to match
* @return the list of matched indices or {@code null} if not matched * @return the list of matched keys or {@code null} if not matched
*/ */
public List<String> matchIndices(List<String> path) { public List<String> matchKeys(List<String> path) {
int length = pattern.size(); int length = pattern.size();
if (length != path.size()) { if (length != path.size()) {
return null; return null;
@@ -216,7 +240,7 @@ public class PathPattern implements PathPredicates {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
String pat = pattern.get(i); String pat = pattern.get(i);
String key = path.get(i); String key = path.get(i);
if (!keyMatches(pat, key)) { if (!PathPredicates.keyMatches(pat, key)) {
return null; return null;
} }
if (isWildcard(pat)) { if (isWildcard(pat)) {
@@ -23,6 +23,38 @@ import ghidra.dbg.target.TargetObject;
import ghidra.dbg.util.PathUtils.PathComparator; import ghidra.dbg.util.PathUtils.PathComparator;
public interface PathPredicates { public interface PathPredicates {
static boolean keyMatches(String pat, String key) {
if (key.equals(pat)) {
return true;
}
if ("[]".equals(pat)) {
return PathUtils.isIndex(key);
}
if ("".equals(pat)) {
return PathUtils.isName(key);
}
return false;
}
static boolean anyMatches(Set<String> pats, String key) {
return pats.stream().anyMatch(p -> keyMatches(p, key));
}
static PathPredicates pattern(String... keyPatterns) {
return new PathPattern(List.of(keyPatterns));
}
static PathPredicates pattern(List<String> keyPatterns) {
return new PathPattern(keyPatterns);
}
static PathPredicates parse(String pattern) {
return new PathPattern(PathUtils.parse(pattern));
}
PathPredicates or(PathPredicates that);
/** /**
* Check if the entire path passes * Check if the entire path passes
* *
@@ -59,10 +91,22 @@ public interface PathPredicates {
boolean ancestorMatches(List<String> path, boolean strict); boolean ancestorMatches(List<String> path, boolean strict);
/** /**
* Assuming a successor of path could match, get the patterns for the next possible key * Get the patterns for the next possible key
* *
* <p> * <p>
* If the pattern could accept a name next, get all patterns describing those names * If a successor of the given path cannot match this pattern, the empty set is returned.
*
* @param path the ancestor path
* @return a set of patterns where indices are enclosed in brackets ({@code [])
*/
Set<String> getNextKeys(List<String> path);
/**
* Get the patterns for the next possible name
*
* <p>
* If a successor of the given path cannot match this pattern, the empty set is returned. If the
* pattern could accept a name next, get all patterns describing those names
* *
* @param path the ancestor path * @param path the ancestor path
* @return a set of patterns * @return a set of patterns
@@ -73,10 +117,11 @@ public interface PathPredicates {
* Assuming a successor of path could match, get the patterns for the next possible index * Assuming a successor of path could match, get the patterns for the next possible index
* *
* <p> * <p>
* If the pattern could accept an index next, get all patterns describing those indices * If a successor of the given path cannot match this pattern, the empty set is returned. If the
* pattern could accept an index next, get all patterns describing those indices
* *
* @param path the ancestor path * @param path the ancestor path
* @return a set of patterns, without brack@Override ets ({@code []) * @return a set of patterns, without brackets ({@code [])
*/ */
Set<String> getNextIndices(List<String> path); Set<String> getNextIndices(List<String> path);
@@ -94,18 +139,6 @@ public interface PathPredicates {
*/ */
PathPattern getSingletonPattern(); PathPattern getSingletonPattern();
static boolean anyMatches(Set<String> pats, String key) {
for (String pat : pats) {
if ("".equals(pat)) {
return true;
}
if (key.equals(pat)) {
return true;
}
}
return false;
}
default NavigableMap<List<String>, ?> getCachedValues(TargetObject seed) { default NavigableMap<List<String>, ?> getCachedValues(TargetObject seed) {
return getCachedValues(List.of(), seed); return getCachedValues(List.of(), seed);
} }
@@ -259,10 +292,10 @@ public interface PathPredicates {
* @param indices the indices to substitute * @param indices the indices to substitute
* @return the pattern or matcher with the applied substitutions * @return the pattern or matcher with the applied substitutions
*/ */
PathPredicates applyIndices(List<String> indices); PathPredicates applyKeys(List<String> indices);
default PathPredicates applyIndices(String... indices) { default PathPredicates applyIndices(String... indices) {
return applyIndices(List.of(indices)); return applyKeys(List.of(indices));
} }
/** /**

Some files were not shown because too many files have changed in this diff Show More