Merge remote-tracking branch 'origin/GP-0_d-millar_lldb_tests_PATCH' into patch

This commit is contained in:
Ryan Kurtz
2026-01-24 04:46:09 -05:00
5 changed files with 213 additions and 111 deletions
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -118,7 +118,7 @@ public class AnsiBufferedInputStream extends InputStream {
return -1; return -1;
} }
byte c = (byte) ci; byte c = (byte) ci;
// printDebugChar(c); //printDebugChar(c);
switch (mode) { switch (mode) {
case CHARS: case CHARS:
processChars(c); processChars(c);
@@ -250,6 +250,10 @@ public class AnsiBufferedInputStream extends InputStream {
execCursorBackward(); execCursorBackward();
mode = Mode.CHARS; mode = Mode.CHARS;
break; break;
case 'G':
execCursorCharAbsolute();
mode = Mode.CHARS;
break;
case 'H': case 'H':
execCursorPosition(); execCursorPosition();
mode = Mode.CHARS; mode = Mode.CHARS;
@@ -415,6 +419,11 @@ public class AnsiBufferedInputStream extends InputStream {
lineBuf.position(lineBuf.position() - delta); lineBuf.position(lineBuf.position() - delta);
} }
protected void execCursorCharAbsolute() {
int abs = parseNumericBuffer();
lineBuf.position(abs - 1);
}
protected void execCursorPosition() { protected void execCursorPosition() {
int[] yx = parseNumericListBuffer(); int[] yx = parseNumericListBuffer();
if (yx.length == 0) { if (yx.length == 0) {
@@ -32,8 +32,7 @@ import java.util.stream.Collectors;
import org.apache.commons.io.output.TeeOutputStream; import org.apache.commons.io.output.TeeOutputStream;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.Before; import org.junit.*;
import org.junit.BeforeClass;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
import ghidra.app.plugin.core.debug.service.tracermi.TraceRmiPlugin; import ghidra.app.plugin.core.debug.service.tracermi.TraceRmiPlugin;
@@ -50,6 +49,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRangeImpl; import ghidra.program.model.address.AddressRangeImpl;
import ghidra.pty.*; import ghidra.pty.*;
import ghidra.pty.testutil.DummyProc; import ghidra.pty.testutil.DummyProc;
import ghidra.pty.windows.AnsiBufferedInputStream;
import ghidra.trace.model.Lifespan; import ghidra.trace.model.Lifespan;
import ghidra.trace.model.breakpoint.TraceBreakpointKind; import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet; import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet;
@@ -59,12 +59,16 @@ import ghidra.util.*;
public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebuggerTest { public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebuggerTest {
public static final boolean IS_WINDOWS =
OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS;
record PlatDep(String name, String endian, String lang, String cSpec, String callMne, record PlatDep(String name, String endian, String lang, String cSpec, String callMne,
String intReg, String floatReg) { String intReg, String floatReg) {
static final PlatDep ARM64 = static final PlatDep ARM64 =
new PlatDep("arm64", "little", "AARCH64:LE:64:v8A", "default", "bl", "x0", "s0"); new PlatDep("arm64", "little", "AARCH64:LE:64:v8A", "default", "bl", "x0", "s0");
static final PlatDep X8664 = // Note AT&T callq static final PlatDep X8664 = // Note AT&T callq
new PlatDep("x86_64", "little", "x86:LE:64:default", "gcc", "callq", "rax", "st0"); new PlatDep("x86_64", "little", "x86:LE:64:default", IS_WINDOWS ? "windows" : "gcc",
"callq", "rax", "st0");
} }
public static final PlatDep PLAT = computePlat(); public static final PlatDep PLAT = computePlat();
@@ -81,16 +85,27 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
}; };
} }
static String getSpecimenClone() { static String getSpecimenNewThreadAndExit() {
return DummyProc.which("expCloneExit"); return IS_WINDOWS ? which("expCreateThreadExit") : which("expCloneExit");
}
static int getSleepThreadCount() {
// The targets above use different sleep methods:
// Linux spawns clock_nanosleep
// Windows spawns ZwDelayExecution and multiple ZwWaitForWork threads
return IS_WINDOWS ? 3 : 1;
} }
static String getSpecimenPrint() { static String getSpecimenPrint() {
return DummyProc.which("expPrint"); return which("expPrint");
}
static String getSpecimenSpin() {
return which("expSpin");
} }
static String getSpecimenRead() { static String getSpecimenRead() {
return DummyProc.which("expRead"); return which("expRead");
} }
/** /**
@@ -106,7 +121,7 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
"""; """;
// Connecting should be the first thing the script does, so use a tight timeout. // Connecting should be the first thing the script does, so use a tight timeout.
protected static final int CONNECT_TIMEOUT_MS = 3000; protected static final int CONNECT_TIMEOUT_MS = 3000;
protected static final int TIMEOUT_SECONDS = 300; protected static final int TIMEOUT_SECONDS = SystemUtilities.isInTestingBatchMode() ? 10 : 30;
protected static final int QUIT_TIMEOUT_MS = 1000; protected static final int QUIT_TIMEOUT_MS = 1000;
/** Some snapshot likely to exceed the latest */ /** Some snapshot likely to exceed the latest */
@@ -125,7 +140,11 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
// Don't run gradle in gradle. It already did this task. // Don't run gradle in gradle. It already did this task.
return; return;
} }
new ProcessBuilder("gradle", "assemblePyPackage") String gradleCmd =
OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS ? "gradle.bat"
: "gradle";
String gradle = DummyProc.which(gradleCmd);
new ProcessBuilder(gradle, "assemblePyPackage")
.directory(TestApplicationUtils.getInstallationDirectory()) .directory(TestApplicationUtils.getInstallationDirectory())
.inheritIO() .inheritIO()
.start() .start()
@@ -156,6 +175,13 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
} }
} }
@After
public void tearDownTraceRmi() throws IOException {
for (TraceRmiConnection cx : traceRmi.getAllConnections()) {
cx.close();
}
}
protected void addAllDebuggerPlugins() throws PluginException { protected void addAllDebuggerPlugins() throws PluginException {
PluginsConfiguration plugConf = new PluginsConfiguration() { PluginsConfiguration plugConf = new PluginsConfiguration() {
@Override @Override
@@ -198,6 +224,7 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
@SuppressWarnings("resource") // Do not close stdin @SuppressWarnings("resource") // Do not close stdin
protected ExecInLldb execInLldb(String script) throws IOException { protected ExecInLldb execInLldb(String script) throws IOException {
script = lfIfWindows(script);
Pty pty = PtyFactory.local().openpty(); Pty pty = PtyFactory.local().openpty();
Map<String, String> env = new HashMap<>(System.getenv()); Map<String, String> env = new HashMap<>(System.getenv());
setPythonPath(env); setPythonPath(env);
@@ -210,8 +237,12 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
else { else {
out = new TeeOutputStream(System.out, capture); out = new TeeOutputStream(System.out, capture);
} }
Thread pumper = new StreamPumper(pty.getParent().getInputStream(), out); InputStream inputStream = pty.getParent().getInputStream();
inputStream = new AnsiBufferedInputStream(inputStream);
Thread pumper = new StreamPumper(inputStream, out);
pumper.start(); pumper.start();
// NB: you have to do this because the AnsiBufferedInputStream requires a single line
pty.getChild().setWindowSize((short) 400, (short) 1);
PtySession lldbSession = pty.getChild().session(new String[] { lldbPath.toString() }, env); PtySession lldbSession = pty.getChild().session(new String[] { lldbPath.toString() }, env);
OutputStream stdin = pty.getParent().getOutputStream(); OutputStream stdin = pty.getParent().getOutputStream();
@@ -281,21 +312,25 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
} }
public void execute(String cmd) { public void execute(String cmd) {
cmd = lfIfWindows(cmd);
RemoteMethod execute = getMethod("execute"); RemoteMethod execute = getMethod("execute");
execute.invoke(Map.of("cmd", cmd)); execute.invoke(Map.of("cmd", cmd));
} }
public RemoteAsyncResult executeAsync(String cmd) { public RemoteAsyncResult executeAsync(String cmd) {
cmd = lfIfWindows(cmd);
RemoteMethod execute = getMethod("execute"); RemoteMethod execute = getMethod("execute");
return execute.invokeAsync(Map.of("cmd", cmd)); return execute.invokeAsync(Map.of("cmd", cmd));
} }
public String executeCapture(String cmd) { public String executeCapture(String cmd) {
cmd = lfIfWindows(cmd);
RemoteMethod execute = getMethod("execute"); RemoteMethod execute = getMethod("execute");
return (String) execute.invoke(Map.of("cmd", cmd, "to_string", true)); return (String) execute.invoke(Map.of("cmd", cmd, "to_string", true));
} }
public Object evaluate(String expr) { public Object evaluate(String expr) {
expr = lfIfWindows(expr);
RemoteMethod evaluate = getMethod("evaluate"); RemoteMethod evaluate = getMethod("evaluate");
return evaluate.invoke(Map.of("expr", expr)); return evaluate.invoke(Map.of("expr", expr));
} }
@@ -313,13 +348,22 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
public void close() throws Exception { public void close() throws Exception {
Msg.info(this, "Cleaning up lldb"); Msg.info(this, "Cleaning up lldb");
execute("settings set auto-confirm true"); execute("settings set auto-confirm true");
exec.pty.getParent().getOutputStream().write(""" String cmd = """
quit quit
""".getBytes()); """;
cmd = lfIfWindows(cmd);
exec.pty.getParent().getOutputStream().write(cmd.getBytes());
Exception finalExc = null;
try { try {
LldbResult r = exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); try {
r.handle(); LldbResult r = exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
waitForPass(() -> assertTrue(connection.isClosed())); r.handle();
}
catch (Exception e) {
finalExc = e;
}
waitForPass(this, () -> assertTrue(connection.isClosed()), TIMEOUT_SECONDS,
TimeUnit.SECONDS);
} }
finally { finally {
if (!success) { if (!success) {
@@ -329,6 +373,9 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
exec.pty.close(); exec.pty.close();
exec.lldb.destroyForcibly(); exec.lldb.destroyForcibly();
exec.pumper.interrupt(); exec.pumper.interrupt();
if (finalExc != null) {
throw finalExc;
}
} }
} }
} }
@@ -468,6 +515,7 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
protected void assertLocalOs(String actual) { protected void assertLocalOs(String actual) {
assertThat(actual, Matchers.startsWith(switch (OperatingSystem.CURRENT_OPERATING_SYSTEM) { assertThat(actual, Matchers.startsWith(switch (OperatingSystem.CURRENT_OPERATING_SYSTEM) {
case LINUX -> "linux"; case LINUX -> "linux";
case WINDOWS -> "windows";
case MAC_OS_X -> "macos"; case MAC_OS_X -> "macos";
default -> throw new AssertionError("What OS?"); default -> throw new AssertionError("What OS?");
})); }));
@@ -611,4 +659,20 @@ public abstract class AbstractLldbTraceRmiTest extends AbstractGhidraHeadedDebug
} }
throw lastError; throw lastError;
} }
public static String which(String cmd) {
return DummyProc.which(cmd).replace('\\', '/');
}
public static String projectName(String cmd) {
String ext = IS_WINDOWS ? ".exe" : "";
return "/New Traces/lldb/" + cmd + ext;
}
private String lfIfWindows(String cmd) {
if (IS_WINDOWS) {
cmd = cmd.replace("\n", "\r\n");
}
return cmd;
}
} }
@@ -17,6 +17,7 @@ package agent.lldb.rmi;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.junit.Assume.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
@@ -111,7 +112,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
ghidra trace start ghidra trace start
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
assertEquals(PLAT.lang(), assertEquals(PLAT.lang(),
tb.trace.getBaseLanguage().getLanguageID().getIdAsString()); tb.trace.getBaseLanguage().getLanguageID().getIdAsString());
@@ -168,7 +169,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
// NOTE: Given the 'quit' command, I'm not sure this assertion is checking anything. // NOTE: Given the 'quit' command, I'm not sure this assertion is checking anything.
waitDomainObjectClosed("/New Traces/lldb/expPrint"); waitDomainObjectClosed(projectName("expPrint"));
} }
@Test @Test
@@ -177,8 +178,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
String out = runThrowError(addr -> { String out = runThrowError(addr -> {
refAddr.set(addr); refAddr.set(addr);
return """ return """
file %s
%s %s
file %s
script print("---Import---") script print("---Import---")
ghidra trace info ghidra trace info
ghidra trace connect %s ghidra trace connect %s
@@ -194,7 +195,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
script print("---Disconnect---") script print("---Disconnect---")
ghidra trace info ghidra trace info
quit quit
""".formatted(getSpecimenPrint(), PREAMBLE, addr); """.formatted(PREAMBLE, getSpecimenPrint(), addr);
}); });
assertEquals(""" assertEquals("""
@@ -221,10 +222,11 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
public void testLcsp() throws Exception { public void testLcsp() throws Exception {
String out = runThrowError( String out = runThrowError(
""" """
%s
script import ghidralldb script import ghidralldb
script print("---Import---") script print("---Import---")
ghidra trace info-lcsp ghidra trace info-lcsp
script print("--- script print("---")
file %s file %s
script print("---File---") script print("---File---")
ghidra trace info-lcsp ghidra trace info-lcsp
@@ -236,7 +238,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
ghidra trace info-lcsp ghidra trace info-lcsp
quit quit
""" """
.formatted(getSpecimenPrint())); .formatted(PREAMBLE, getSpecimenPrint()));
assertEquals(""" assertEquals("""
Selected Ghidra language: %s Selected Ghidra language: %s
@@ -253,8 +255,10 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
extractOutSection(out, "---Compiler---")); extractOutSection(out, "---Compiler---"));
} }
// TODO: Fails for Windows because the Project cannot be closed
@Test @Test
public void testSave() throws Exception { public void testSave() throws Exception {
assumeFalse(IS_WINDOWS);
traceManager.setSaveTracesByDefault(false); traceManager.setSaveTracesByDefault(false);
// For sanity check, verify failing to save drops data // For sanity check, verify failing to save drops data
@@ -304,7 +308,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
ghidra trace tx-commit ghidra trace tx-commit
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceSnapshot snapshot = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()); TraceSnapshot snapshot = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots());
assertEquals(0, snapshot.getKey()); assertEquals(0, snapshot.getKey());
@@ -330,7 +334,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey();
@@ -363,7 +367,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey();
@@ -400,7 +404,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey();
@@ -431,7 +435,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint(), PLAT.intReg(), PLAT.floatReg())); """.formatted(PREAMBLE, addr, getSpecimenPrint(), PLAT.intReg(), PLAT.floatReg()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey();
List<TraceObjectValue> regVals = tb.trace.getObjectManager() List<TraceObjectValue> regVals = tb.trace.getObjectManager()
@@ -491,7 +495,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint(), PLAT.intReg(), PLAT.floatReg())); """.formatted(PREAMBLE, addr, getSpecimenPrint(), PLAT.intReg(), PLAT.floatReg()));
// The spaces will be left over, but the values should be zeroed // The spaces will be left over, but the values should be zeroed
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey(); long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey();
List<TraceObjectValue> regVals = tb.trace.getObjectManager() List<TraceObjectValue> regVals = tb.trace.getObjectManager()
@@ -593,7 +597,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject object = tb.trace.getObjectManager() TraceObject object = tb.trace.getObjectManager()
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
@@ -621,7 +625,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint(), extra, lldbExpr, gtype)); """.formatted(PREAMBLE, addr, getSpecimenPrint(), extra, lldbExpr, gtype));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject object = tb.trace.getObjectManager() TraceObject object = tb.trace.getObjectManager()
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
@@ -811,7 +815,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject object = tb.trace.getObjectManager() TraceObject object = tb.trace.getObjectManager()
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
@@ -848,12 +852,15 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]")); .getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
assertNotNull(object); assertNotNull(object);
String getObject = extractOutSection(out, "---GetObject---"); String getObject = extractOutSection(out, "---GetObject---");
assertEquals("%d\tTest.Objects[1]".formatted(object.getKey()), getObject); assertTrue(getObject.contains("%d".formatted(object.getKey())));
assertTrue(getObject.contains("Test.Objects[1]"));
} }
} }
@Test @Test
public void testGetValues() throws Exception { public void testGetValues() throws Exception {
// NB: For reasons no one currently understands, using 0xdeadbeef below
// causes the process output to short-circuit and the test to fail.
String out = runThrowError(addr -> """ String out = runThrowError(addr -> """
%s %s
ghidra trace connect %s ghidra trace connect %s
@@ -869,7 +876,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
ghidra trace set-value Test.Objects[1] vshort (short)2 ghidra trace set-value Test.Objects[1] vshort (short)2
ghidra trace set-value Test.Objects[1] vint 3 ghidra trace set-value Test.Objects[1] vint 3
ghidra trace set-value Test.Objects[1] vlong 4LL ghidra trace set-value Test.Objects[1] vlong 4LL
ghidra trace set-value Test.Objects[1] vaddr (void*)0xdeadbeef ghidra trace set-value Test.Objects[1] vaddr (void*)0xdead
ghidra trace set-value Test.Objects[1] vobj Test.Objects[1] OBJECT ghidra trace set-value Test.Objects[1] vobj Test.Objects[1] OBJECT
ghidra trace tx-commit ghidra trace tx-commit
script print("---GetValues---") script print("---GetValues---")
@@ -878,12 +885,12 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
assertEquals( assertEquals(
""" """
Parent Key Span Value Type Parent Key Span Value Type
Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS Test.Objects[1] vaddr [0,+inf) ram:0000dead ADDRESS
Test.Objects[1] vbool [0,+inf) True BOOL Test.Objects[1] vbool [0,+inf) True BOOL
Test.Objects[1] vbyte [0,+inf) 1 BYTE Test.Objects[1] vbyte [0,+inf) 1 BYTE
Test.Objects[1] vchar [0,+inf) 'A' CHAR Test.Objects[1] vchar [0,+inf) 'A' CHAR
@@ -898,6 +905,8 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
@Test @Test
public void testGetValuesRng() throws Exception { public void testGetValuesRng() throws Exception {
// NB: For reasons no one currently understands, using 0xdeadbeef below causes
// the process output to short-circuit and the test to fail, as above.
String out = runThrowError(addr -> """ String out = runThrowError(addr -> """
%s %s
ghidra trace connect %s ghidra trace connect %s
@@ -907,19 +916,19 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
ghidra trace tx-start "Create Object" ghidra trace tx-start "Create Object"
ghidra trace create-obj Test.Objects[1] ghidra trace create-obj Test.Objects[1]
ghidra trace insert-obj Test.Objects[1] ghidra trace insert-obj Test.Objects[1]
ghidra trace set-value Test.Objects[1] vaddr (void*)0xdeadbeef ghidra trace set-value Test.Objects[1] vaddr (void*)0xdead
ghidra trace tx-commit ghidra trace tx-commit
script print("---GetValues---") script print("---GetValues---")
ghidra trace get-values-rng (void*)0xdeadbeef 10 ghidra trace get-values-rng (void*)0xdead 10
script print("---") script print("---")
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
assertEquals(""" assertEquals("""
Parent Key Span Value Type Parent Key Span Value Type
Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS\ Test.Objects[1] vaddr [0,+inf) ram:0000dead ADDRESS\
""", """,
extractOutSection(out, "---GetValues---")); extractOutSection(out, "---GetValues---"));
} }
@@ -941,7 +950,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
assertSame(mdo.get(), traceManager.getCurrentTrace()); assertSame(mdo.get(), traceManager.getCurrentTrace());
assertEquals("Test.Objects[1]", assertEquals("Test.Objects[1]",
traceManager.getCurrentObject().getCanonicalPath().toString()); traceManager.getCurrentObject().getCanonicalPath().toString());
@@ -965,7 +974,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
// Not concerned about specifics, so long as disassembly occurs // Not concerned about specifics, so long as disassembly occurs
long total = 0; long total = 0;
@@ -1037,7 +1046,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
List<TraceObjectValue> procBreakLocVals = tb.trace.getObjectManager() List<TraceObjectValue> procBreakLocVals = tb.trace.getObjectManager()
.getValuePaths(Lifespan.at(0), .getValuePaths(Lifespan.at(0),
@@ -1076,7 +1085,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
List<TraceObjectValue> procWatchLocVals = tb.trace.getObjectManager() List<TraceObjectValue> procWatchLocVals = tb.trace.getObjectManager()
.getValuePaths(Lifespan.at(0), .getValuePaths(Lifespan.at(0),
@@ -1118,7 +1127,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
// Assumes LLDB on Linux amd64 // Assumes LLDB on Linux amd64
TraceObject env = TraceObject env =
@@ -1144,7 +1153,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
// Would be nice to control / validate the specifics // Would be nice to control / validate the specifics
Collection<? extends TraceMemoryRegion> all = Collection<? extends TraceMemoryRegion> all =
@@ -1167,7 +1176,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
// Would be nice to control / validate the specifics // Would be nice to control / validate the specifics
Collection<? extends TraceModule> all = tb.trace.getModuleManager().getAllModules(); Collection<? extends TraceModule> all = tb.trace.getModuleManager().getAllModules();
@@ -1191,7 +1200,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
kill kill
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
// Would be nice to control / validate the specifics // Would be nice to control / validate the specifics
Unique.assertOne(tb.trace.getThreadManager().getAllThreads()); Unique.assertOne(tb.trace.getThreadManager().getAllThreads());
@@ -1218,7 +1227,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
quit quit
""".formatted(PREAMBLE, addr, getSpecimenPrint())); """.formatted(PREAMBLE, addr, getSpecimenPrint()));
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
// Would be nice to control / validate the specifics // Would be nice to control / validate the specifics
List<TraceObject> stack = tb.trace.getObjectManager() List<TraceObject> stack = tb.trace.getObjectManager()
@@ -1226,12 +1235,13 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
PathFilter.parse("Processes[].Threads[].Stack[]")) PathFilter.parse("Processes[].Threads[].Stack[]"))
.map(p -> p.getDestination(null)) .map(p -> p.getDestination(null))
.toList(); .toList();
assertThat(stack.size(), greaterThan(2)); assertThat(stack.size(), IS_WINDOWS ? equalTo(1) : greaterThan(2));
} }
} }
@Test @Test
public void testMinimal() throws Exception { public void testMinimal() throws Exception {
assumeFalse(IS_WINDOWS);
Function<String, String> scriptSupplier = addr -> """ Function<String, String> scriptSupplier = addr -> """
%s %s
ghidra trace connect %s ghidra trace connect %s
@@ -16,8 +16,7 @@
package agent.lldb.rmi; package agent.lldb.rmi;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -28,9 +27,9 @@ import org.junit.Test;
import org.junit.experimental.categories.Category; import org.junit.experimental.categories.Category;
import generic.test.category.NightlyCategory; import generic.test.category.NightlyCategory;
import generic.test.rule.Repeated;
import ghidra.app.plugin.core.debug.utils.ManagedDomainObject; import ghidra.app.plugin.core.debug.utils.ManagedDomainObject;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.pty.testutil.DummyProc;
import ghidra.trace.database.ToyDBTraceBuilder; import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.model.Lifespan; import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
@@ -59,8 +58,22 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
@Override @Override
public void close() throws Exception { public void close() throws Exception {
conn.close(); Exception toThrow = null;
mdo.close(); try {
conn.close();
}
catch (Exception e) {
toThrow = e;
}
try {
mdo.close();
}
catch (Exception e) {
toThrow = e;
}
if (toThrow != null) {
throw toThrow;
}
} }
} }
@@ -86,10 +99,10 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
// TODO: This passes if you single-step through it but fails on some transactional stuff if run // TODO: This passes if you single-step through it but fails on some transactional stuff if run
//@Test //@Test
public void testOnNewThread() throws Exception { public void testOnNewThread() throws Exception {
String cloneExit = DummyProc.which("expCloneExit"); String specimen = getSpecimenNewThreadAndExit();
try (LldbAndTrace conn = startAndSyncLldb()) { try (LldbAndTrace conn = startAndSyncLldb()) {
start(conn, "%s".formatted(cloneExit)); start(conn, "%s".formatted(specimen));
conn.execute("break set -n work"); conn.execute("break set -n work");
waitForPass(() -> { waitForPass(() -> {
TraceObject proc = tb.objAny0("Processes[]"); TraceObject proc = tb.objAny0("Processes[]");
@@ -115,11 +128,11 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
// TODO: This passes if you single-step through it but fails on some transactional stuff if run // TODO: This passes if you single-step through it but fails on some transactional stuff if run
//@Test //@Test
public void testOnThreadSelected() throws Exception { public void testOnThreadSelected() throws Exception {
String cloneExit = DummyProc.which("expCloneExit"); String specimen = getSpecimenNewThreadAndExit();
try (LldbAndTrace conn = startAndSyncLldb()) { try (LldbAndTrace conn = startAndSyncLldb()) {
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
start(conn, "%s".formatted(cloneExit)); start(conn, "%s".formatted(specimen));
conn.execute("break set -n work"); conn.execute("break set -n work");
waitForPass(() -> { waitForPass(() -> {
@@ -211,7 +224,7 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
start(conn, getSpecimenPrint()); start(conn, getSpecimenPrint());
conn.execute("breakpoint set -n puts"); conn.execute("breakpoint set -n wrapputs");
conn.execute("cont"); conn.execute("cont");
waitStopped(conn.conn); waitStopped(conn.conn);
@@ -284,7 +297,7 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
@Test @Test
public void testOnCont() throws Exception { public void testOnCont() throws Exception {
try (LldbAndTrace conn = startAndSyncLldb()) { try (LldbAndTrace conn = startAndSyncLldb()) {
start(conn, getSpecimenRead()); start(conn, getSpecimenSpin());
conn.execute("cont"); conn.execute("cont");
waitRunning(conn.conn); waitRunning(conn.conn);
@@ -18,7 +18,7 @@ package agent.lldb.rmi;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue; import static org.junit.Assume.*;
import java.util.*; import java.util.*;
@@ -54,8 +54,9 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
try (LldbAndConnection conn = startAndConnectLldb()) { try (LldbAndConnection conn = startAndConnectLldb()) {
RemoteMethod execute = conn.getMethod("execute"); RemoteMethod execute = conn.getMethod("execute");
assertEquals(false, execute.parameters().get("to_string").getDefaultValue()); assertEquals(false, execute.parameters().get("to_string").getDefaultValue());
assertEquals("test\n", String result =
execute.invoke(Map.of("cmd", "script print('test')", "to_string", true))); (String) execute.invoke(Map.of("cmd", "script print('test')", "to_string", true));
assertEquals("test\n", result.replace("\r\n", "\n"));
conn.success(); conn.success();
} }
} }
@@ -67,7 +68,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
conn.execute("kill"); conn.execute("kill");
conn.success(); conn.success();
} }
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
// Just confirm it's present // Just confirm it's present
} }
} }
@@ -104,7 +105,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "breakpoints"); txPut(conn, "breakpoints");
RemoteMethod refreshProcBreakpoints = conn.getMethod("refresh_proc_breakpoints"); RemoteMethod refreshProcBreakpoints = conn.getMethod("refresh_proc_breakpoints");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -143,7 +144,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "all"); txPut(conn, "all");
RemoteMethod refreshProcWatchpoints = conn.getMethod("refresh_proc_watchpoints"); RemoteMethod refreshProcWatchpoints = conn.getMethod("refresh_proc_watchpoints");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -219,7 +220,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "all"); txPut(conn, "all");
RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment"); RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject env = Objects.requireNonNull(tb.objAny0(path)); TraceObject env = Objects.requireNonNull(tb.objAny0(path));
@@ -242,7 +243,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txCreate(conn, path); txCreate(conn, path);
RemoteMethod refreshThreads = conn.getMethod("refresh_threads"); RemoteMethod refreshThreads = conn.getMethod("refresh_threads");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject threads = Objects.requireNonNull(tb.objAny0(path)); TraceObject threads = Objects.requireNonNull(tb.objAny0(path));
@@ -262,10 +263,10 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
conn.execute("file " + getSpecimenPrint()); conn.execute("file " + getSpecimenPrint());
conn.execute("ghidra trace start"); conn.execute("ghidra trace start");
txPut(conn, "processes"); txPut(conn, "processes");
breakAt(conn, "puts"); breakAt(conn, "wrapputs");
RemoteMethod refreshStack = conn.getMethod("refresh_stack"); RemoteMethod refreshStack = conn.getMethod("refresh_stack");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
waitTxDone(); waitTxDone();
@@ -296,7 +297,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
conn.execute("ghidra trace tx-commit"); conn.execute("ghidra trace tx-commit");
RemoteMethod refreshRegisters = conn.getMethod("refresh_registers"); RemoteMethod refreshRegisters = conn.getMethod("refresh_registers");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
conn.execute("expr $%s = 0xdeadbeef".formatted(PLAT.intReg())); conn.execute("expr $%s = 0xdeadbeef".formatted(PLAT.intReg()));
@@ -324,7 +325,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txCreate(conn, path); txCreate(conn, path);
RemoteMethod refreshMappings = conn.getMethod("refresh_mappings"); RemoteMethod refreshMappings = conn.getMethod("refresh_mappings");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject memory = Objects.requireNonNull(tb.objAny0(path)); TraceObject memory = Objects.requireNonNull(tb.objAny0(path));
@@ -347,7 +348,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txCreate(conn, path); txCreate(conn, path);
RemoteMethod refreshModules = conn.getMethod("refresh_modules"); RemoteMethod refreshModules = conn.getMethod("refresh_modules");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject modules = Objects.requireNonNull(tb.objAny0(path)); TraceObject modules = Objects.requireNonNull(tb.objAny0(path));
@@ -366,17 +367,18 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
@Test @Test
public void testActivateThread() throws Exception { public void testActivateThread() throws Exception {
// This test crashes lldb-1500.0.404.7 on macOS arm64 // This test crashes lldb-1500.0.404.7 on macOS arm64
assumeTrue(OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.LINUX); OperatingSystem os = OperatingSystem.CURRENT_OPERATING_SYSTEM;
assumeTrue(os == OperatingSystem.LINUX || os == OperatingSystem.WINDOWS);
try (LldbAndConnection conn = startAndConnectLldb()) { try (LldbAndConnection conn = startAndConnectLldb()) {
// TODO: need to find this file (same issue in LldbHookTests // TODO: need to find this file (same issue in LldbHookTests
String dproc = DummyProc.which("expCloneExit"); conn.execute("file " + getSpecimenNewThreadAndExit());
conn.execute("file " + dproc);
conn.execute("ghidra trace start"); conn.execute("ghidra trace start");
txPut(conn, "processes"); txPut(conn, "processes");
breakAt(conn, "work"); breakAt(conn, "work");
RemoteMethod activateThread = conn.getMethod("activate_thread"); RemoteMethod activateThread = conn.getMethod("activate_thread");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expCloneExit")) { try (ManagedDomainObject mdo = openDomainObject(
IS_WINDOWS ? projectName("expCreateThreadExit") : projectName("expCloneExit"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
waitTxDone(); waitTxDone();
@@ -389,7 +391,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
.getValuePaths(Lifespan.at(0), pattern) .getValuePaths(Lifespan.at(0), pattern)
.map(p -> p.getDestination(null)) .map(p -> p.getDestination(null))
.toList(); .toList();
assertEquals(2, list.size()); assertTrue(list.size() > getSleepThreadCount());
for (TraceObject t : list) { for (TraceObject t : list) {
activateThread.invoke(Map.of("thread", t)); activateThread.invoke(Map.of("thread", t));
@@ -398,7 +400,8 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
long index = Long.decode(indices.get(1)); long index = Long.decode(indices.get(1));
assertThat(out, Matchers assertThat(out, Matchers
.either(containsString("tid = %s".formatted(index))) .either(containsString("tid = %s".formatted(index)))
.or(containsString("tid = 0x%x".formatted(index)))); .or(containsString("tid = 0x%x".formatted(index)))
.or(containsString("tid = 0x0%x".formatted(index))));
} }
} }
conn.success(); conn.success();
@@ -411,10 +414,10 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
conn.execute("file " + getSpecimenPrint()); conn.execute("file " + getSpecimenPrint());
conn.execute("ghidra trace start"); conn.execute("ghidra trace start");
txPut(conn, "processes"); txPut(conn, "processes");
breakAt(conn, "puts"); breakAt(conn, "wrapputs");
RemoteMethod activateFrame = conn.getMethod("activate_frame"); RemoteMethod activateFrame = conn.getMethod("activate_frame");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
waitTxDone(); waitTxDone();
@@ -446,7 +449,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod removeProcess = conn.getMethod("remove_process"); RemoteMethod removeProcess = conn.getMethod("remove_process");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject proc2 = Objects.requireNonNull(tb.objAny0("Processes[]")); TraceObject proc2 = Objects.requireNonNull(tb.objAny0("Processes[]"));
@@ -520,7 +523,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
//conn.execute("process attach -p %d".formatted(dproc.pid)); //conn.execute("process attach -p %d".formatted(dproc.pid));
RemoteMethod detach = conn.getMethod("detach"); RemoteMethod detach = conn.getMethod("detach");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
@@ -551,7 +554,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
waitStopped(conn); waitStopped(conn);
String out = conn.executeCapture("target list"); String out = conn.executeCapture("target list");
assertThat(out, containsString(getSpecimenPrint())); assertThat(out, containsString(DummyProc.which("expPrint")));
} }
conn.success(); conn.success();
} }
@@ -596,7 +599,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod kill = conn.getMethod("kill"); RemoteMethod kill = conn.getMethod("kill");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -651,7 +654,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod step_into = conn.getMethod("step_into"); RemoteMethod step_into = conn.getMethod("step_into");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
waitTxDone(); waitTxDone();
@@ -681,7 +684,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod step_over = conn.getMethod("step_over"); RemoteMethod step_over = conn.getMethod("step_over");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
txPut(conn, "threads"); txPut(conn, "threads");
@@ -711,7 +714,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod step_advance = conn.getMethod("step_advance"); RemoteMethod step_advance = conn.getMethod("step_advance");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
txPut(conn, "threads"); txPut(conn, "threads");
@@ -737,14 +740,16 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
@Test @Test
public void testFinish() throws Exception { public void testFinish() throws Exception {
try (LldbAndConnection conn = startAndConnectLldb()) { try (LldbAndConnection conn = startAndConnectLldb()) {
conn.execute("file " + getSpecimenPrint()); // NB: These examples have shorter per-platform "step out"'s
conn.execute("file " + (IS_WINDOWS ? getSpecimenRead() : getSpecimenPrint()));
conn.execute("ghidra trace start"); conn.execute("ghidra trace start");
txPut(conn, "processes"); txPut(conn, "processes");
breakAt(conn, "puts"); breakAt(conn, IS_WINDOWS ? "wrapread" : "wrapputs");
RemoteMethod activate = conn.getMethod("activate_thread"); RemoteMethod activate = conn.getMethod("activate_thread");
RemoteMethod step_out = conn.getMethod("step_out"); RemoteMethod step_out = conn.getMethod("step_out");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo =
openDomainObject(projectName(IS_WINDOWS ? "expRead" : "expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
waitTxDone(); waitTxDone();
@@ -757,6 +762,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
int initDepth = getDepth(conn); int initDepth = getDepth(conn);
step_out.invoke(Map.of("thread", thread)); step_out.invoke(Map.of("thread", thread));
waitStopped(conn);
int finalDepth = getDepth(conn); int finalDepth = getDepth(conn);
assertEquals(initDepth - 1, finalDepth); assertEquals(initDepth - 1, finalDepth);
@@ -771,11 +777,11 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
conn.execute("file " + getSpecimenPrint()); conn.execute("file " + getSpecimenPrint());
conn.execute("ghidra trace start"); conn.execute("ghidra trace start");
txPut(conn, "processes"); txPut(conn, "processes");
breakAt(conn, "puts"); breakAt(conn, "wrapputs");
RemoteMethod activate = conn.getMethod("activate_thread"); RemoteMethod activate = conn.getMethod("activate_thread");
RemoteMethod ret = conn.getMethod("step_return"); RemoteMethod ret = conn.getMethod("step_return");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
waitTxDone(); waitTxDone();
@@ -803,7 +809,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakAddress = conn.getMethod("break_address"); RemoteMethod breakAddress = conn.getMethod("break_address");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]")); TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
@@ -825,7 +831,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakExpression = conn.getMethod("break_expression"); RemoteMethod breakExpression = conn.getMethod("break_expression");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -846,7 +852,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakAddress = conn.getMethod("break_hw_address"); RemoteMethod breakAddress = conn.getMethod("break_hw_address");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -868,7 +874,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakExpression = conn.getMethod("break_hw_expression"); RemoteMethod breakExpression = conn.getMethod("break_hw_expression");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -890,7 +896,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakRange = conn.getMethod("break_read_range"); RemoteMethod breakRange = conn.getMethod("break_read_range");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -917,7 +923,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakExpression = conn.getMethod("break_read_expression"); RemoteMethod breakExpression = conn.getMethod("break_read_expression");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
breakExpression.invoke(Map.of( breakExpression.invoke(Map.of(
@@ -940,7 +946,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakRange = conn.getMethod("break_write_range"); RemoteMethod breakRange = conn.getMethod("break_write_range");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -969,7 +975,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakExpression = conn.getMethod("break_write_expression"); RemoteMethod breakExpression = conn.getMethod("break_write_expression");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
breakExpression.invoke(Map.of( breakExpression.invoke(Map.of(
@@ -994,7 +1000,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakRange = conn.getMethod("break_access_range"); RemoteMethod breakRange = conn.getMethod("break_access_range");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -1021,7 +1027,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakExpression = conn.getMethod("break_access_expression"); RemoteMethod breakExpression = conn.getMethod("break_access_expression");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
breakExpression.invoke(Map.of( breakExpression.invoke(Map.of(
@@ -1045,7 +1051,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod breakExc = conn.getMethod("break_exception"); RemoteMethod breakExc = conn.getMethod("break_exception");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
breakExc.invoke(Map.of("lang", "C++")); breakExc.invoke(Map.of("lang", "C++"));
@@ -1065,7 +1071,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod toggleBreakpoint = conn.getMethod("toggle_breakpoint"); RemoteMethod toggleBreakpoint = conn.getMethod("toggle_breakpoint");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -1089,7 +1095,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod toggleBreakpointLocation = conn.getMethod("toggle_breakpoint_location"); RemoteMethod toggleBreakpointLocation = conn.getMethod("toggle_breakpoint_location");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -1114,7 +1120,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
txPut(conn, "processes"); txPut(conn, "processes");
RemoteMethod deleteBreakpoint = conn.getMethod("delete_breakpoint"); RemoteMethod deleteBreakpoint = conn.getMethod("delete_breakpoint");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
waitStopped(conn); waitStopped(conn);
@@ -1139,7 +1145,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
RemoteMethod breakExpression = conn.getMethod("break_read_expression"); RemoteMethod breakExpression = conn.getMethod("break_read_expression");
RemoteMethod deleteWatchpoint = conn.getMethod("delete_watchpoint"); RemoteMethod deleteWatchpoint = conn.getMethod("delete_watchpoint");
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) { try (ManagedDomainObject mdo = openDomainObject(projectName("expPrint"))) {
tb = new ToyDBTraceBuilder((Trace) mdo.get()); tb = new ToyDBTraceBuilder((Trace) mdo.get());
breakExpression.invoke(Map.of( breakExpression.invoke(Map.of(