mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge remote-tracking branch 'origin/GP-326_d-millar_how_to_add_a_debugger--SQUASHED'
This commit is contained in:
commit
52a1550eff
28 changed files with 4816 additions and 1 deletions
|
@ -0,0 +1,379 @@
|
|||
/* ###
|
||||
* 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 agent.drgn.rmi;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.*;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.junit.Before;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
|
||||
import ghidra.app.plugin.core.debug.service.tracermi.TraceRmiPlugin;
|
||||
import ghidra.app.plugin.core.debug.utils.ManagedDomainObject;
|
||||
import ghidra.app.services.TraceRmiService;
|
||||
import ghidra.debug.api.tracermi.*;
|
||||
import ghidra.framework.*;
|
||||
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginsConfiguration;
|
||||
import ghidra.framework.plugintool.util.*;
|
||||
import ghidra.pty.testutil.DummyProc;
|
||||
import ghidra.util.Msg;
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
public abstract class AbstractDrgnTraceRmiTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
|
||||
protected static String CORE = "core.12137";
|
||||
protected static String MDO = "/New Traces/" + CORE;
|
||||
public static String PREAMBLE = """
|
||||
import os
|
||||
import drgn
|
||||
import drgn.cli
|
||||
os.environ['OPT_TARGET_KIND'] = 'coredump'
|
||||
os.environ['OPT_TARGET_IMG'] = '$CORE'
|
||||
from ghidradrgn.commands import *
|
||||
""";
|
||||
|
||||
// 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 TIMEOUT_SECONDS = 30000;
|
||||
protected static final int QUIT_TIMEOUT_MS = 1000;
|
||||
|
||||
protected static boolean didSetupPython = false;
|
||||
|
||||
protected TraceRmiService traceRmi;
|
||||
private Path pythonPath;
|
||||
private Path outFile;
|
||||
private Path errFile;
|
||||
|
||||
@Before
|
||||
public void assertOS() {
|
||||
assumeTrue(OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.LINUX);
|
||||
}
|
||||
|
||||
//@BeforeClass
|
||||
public static void setupPython() throws Throwable {
|
||||
if (didSetupPython) {
|
||||
// Only do this once when running the full suite.
|
||||
return;
|
||||
}
|
||||
String gradle = DummyProc.which("gradle");
|
||||
new ProcessBuilder(gradle, "Debugger-agent-drgn:assemblePyPackage")
|
||||
.directory(TestApplicationUtils.getInstallationDirectory())
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor();
|
||||
didSetupPython = true;
|
||||
}
|
||||
|
||||
protected void setPythonPath(ProcessBuilder pb) throws IOException {
|
||||
String sep =
|
||||
OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.LINUX ? ";" : ":";
|
||||
String rmiPyPkg = Application.getModuleSubDirectory("Debugger-rmi-trace",
|
||||
"build/pypkg/src").getAbsolutePath();
|
||||
String drgnPyPkg = Application.getModuleSubDirectory("Debugger-agent-drgn",
|
||||
"build/pypkg/src").getAbsolutePath();
|
||||
String add = rmiPyPkg + sep + drgnPyPkg;
|
||||
pb.environment().compute("PYTHONPATH", (k, v) -> v == null ? add : (v + sep + add));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setupTraceRmi() throws Throwable {
|
||||
traceRmi = addPlugin(tool, TraceRmiPlugin.class);
|
||||
|
||||
try {
|
||||
pythonPath = Paths.get(DummyProc.which("drgn"));
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
Msg.error(this, e);
|
||||
}
|
||||
outFile = Files.createTempFile("drgnout", null);
|
||||
errFile = Files.createTempFile("drgnerr", null);
|
||||
}
|
||||
|
||||
protected void addAllDebuggerPlugins() throws PluginException {
|
||||
PluginsConfiguration plugConf = new PluginsConfiguration() {
|
||||
@Override
|
||||
protected boolean accepts(Class<? extends Plugin> pluginClass) {
|
||||
return !ApplicationLevelOnlyPlugin.class.isAssignableFrom(pluginClass);
|
||||
}
|
||||
};
|
||||
|
||||
for (PluginDescription pd : plugConf
|
||||
.getPluginDescriptions(PluginPackage.getPluginPackage("Debugger"))) {
|
||||
addPlugin(tool, pd.getPluginClass());
|
||||
}
|
||||
}
|
||||
|
||||
protected static String addrToStringForPython(InetAddress address) {
|
||||
if (address.isAnyLocalAddress()) {
|
||||
return "127.0.0.1"; // Can't connect to 0.0.0.0 as such. Choose localhost.
|
||||
}
|
||||
return address.getHostAddress();
|
||||
}
|
||||
|
||||
protected static String sockToStringForPython(SocketAddress address) {
|
||||
if (address instanceof InetSocketAddress tcp) {
|
||||
return addrToStringForPython(tcp.getAddress()) + ":" + tcp.getPort();
|
||||
}
|
||||
throw new AssertionError("Unhandled address type " + address);
|
||||
}
|
||||
|
||||
protected record PythonResult(boolean timedOut, int exitCode, String stdout, String stderr) {
|
||||
protected String handle() {
|
||||
if (stderr.contains("RuntimeError") || stderr.contains(" Error") || (0 != exitCode && 1 != exitCode && 143 != exitCode)) {
|
||||
throw new PythonError(exitCode, stdout, stderr);
|
||||
}
|
||||
System.out.println("--stdout--");
|
||||
System.out.println(stdout);
|
||||
System.out.println("--stderr--");
|
||||
System.out.println(stderr);
|
||||
return stdout;
|
||||
}
|
||||
}
|
||||
|
||||
protected record ExecInDrgn(Process python, CompletableFuture<PythonResult> future) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource") // Do not close stdin
|
||||
protected ExecInDrgn execInDrgn(String script) throws IOException {
|
||||
ResourceFile rf = Application.getModuleDataFile("TestResources", CORE);
|
||||
script = script.replace("$CORE", rf.getAbsolutePath());
|
||||
Path fp = Files.createTempFile("test", ".py");
|
||||
FileWriter fw = new FileWriter(fp.toFile());
|
||||
fw.write(script);
|
||||
fw.close();
|
||||
ProcessBuilder pb = new ProcessBuilder(pythonPath.toString(), "-c",
|
||||
rf.getAbsolutePath(), fp.toFile().getAbsolutePath());
|
||||
setPythonPath(pb);
|
||||
|
||||
// If commands come from file, Python will quit after EOF.
|
||||
Msg.info(this, "outFile: " + outFile);
|
||||
Msg.info(this, "errFile: " + errFile);
|
||||
|
||||
//pb.inheritIO();
|
||||
pb.redirectInput(ProcessBuilder.Redirect.PIPE);
|
||||
pb.redirectOutput(outFile.toFile());
|
||||
pb.redirectError(errFile.toFile());
|
||||
Process pyproc = pb.start();
|
||||
return new ExecInDrgn(pyproc, CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
if (!pyproc.waitFor(TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
|
||||
Msg.error(this, "Timed out waiting for Python");
|
||||
pyproc.destroyForcibly();
|
||||
pyproc.waitFor(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
return new PythonResult(true, -1, Files.readString(outFile),
|
||||
Files.readString(errFile));
|
||||
}
|
||||
Msg.info(this, "Python exited with code " + pyproc.exitValue());
|
||||
return new PythonResult(false, pyproc.exitValue(), Files.readString(outFile),
|
||||
Files.readString(errFile));
|
||||
}
|
||||
catch (Exception e) {
|
||||
return ExceptionUtils.rethrow(e);
|
||||
}
|
||||
finally {
|
||||
pyproc.destroyForcibly();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static class PythonError extends RuntimeException {
|
||||
public final int exitCode;
|
||||
public final String stdout;
|
||||
public final String stderr;
|
||||
|
||||
public PythonError(int exitCode, String stdout, String stderr) {
|
||||
super("""
|
||||
exitCode=%d:
|
||||
----stdout----
|
||||
%s
|
||||
----stderr----
|
||||
%s
|
||||
""".formatted(exitCode, stdout, stderr));
|
||||
this.exitCode = exitCode;
|
||||
this.stdout = stdout;
|
||||
this.stderr = stderr;
|
||||
}
|
||||
}
|
||||
|
||||
protected String runThrowError(String script) throws Exception {
|
||||
CompletableFuture<PythonResult> result = execInDrgn(script).future;
|
||||
return result.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).handle();
|
||||
}
|
||||
|
||||
protected record PythonAndConnection(ExecInDrgn exec, TraceRmiConnection connection)
|
||||
implements AutoCloseable {
|
||||
protected RemoteMethod getMethod(String name) {
|
||||
return Objects.requireNonNull(connection.getMethods().get(name));
|
||||
}
|
||||
|
||||
public void execute(String cmd) {
|
||||
RemoteMethod execute = getMethod("execute");
|
||||
execute.invoke(Map.of("cmd", cmd));
|
||||
}
|
||||
|
||||
public RemoteAsyncResult executeAsync(String cmd) {
|
||||
RemoteMethod execute = getMethod("execute");
|
||||
return execute.invokeAsync(Map.of("cmd", cmd));
|
||||
}
|
||||
|
||||
public String executeCapture(String cmd) {
|
||||
RemoteMethod execute = getMethod("execute");
|
||||
return (String) execute.invoke(Map.of("cmd", cmd, "to_string", true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
Msg.info(this, "Cleaning up python");
|
||||
exec.python().destroy();
|
||||
try {
|
||||
PythonResult r = exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
r.handle();
|
||||
waitForPass(() -> assertTrue(connection.isClosed()));
|
||||
}
|
||||
finally {
|
||||
exec.python.destroyForcibly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected PythonAndConnection startAndConnectDrgn(Function<String, String> scriptSupplier)
|
||||
throws Exception {
|
||||
TraceRmiAcceptor acceptor = traceRmi.acceptOne(null);
|
||||
ExecInDrgn exec =
|
||||
execInDrgn(scriptSupplier.apply(sockToStringForPython(acceptor.getAddress())));
|
||||
acceptor.setTimeout(CONNECT_TIMEOUT_MS);
|
||||
try {
|
||||
TraceRmiConnection connection = acceptor.accept();
|
||||
return new PythonAndConnection(exec, connection);
|
||||
}
|
||||
catch (SocketTimeoutException e) {
|
||||
exec.python.destroyForcibly();
|
||||
exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).handle();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected PythonAndConnection startAndConnectDrgn() throws Exception {
|
||||
return startAndConnectDrgn(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
drgn.cli.run_interactive(prog)
|
||||
""".formatted(PREAMBLE, addr));
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
protected String runThrowError(Function<String, String> scriptSupplier)
|
||||
throws Exception {
|
||||
PythonAndConnection conn = startAndConnectDrgn(scriptSupplier);
|
||||
PythonResult r = conn.exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
String stdout = r.handle();
|
||||
//waitForPass(() -> assertTrue(conn.connection.isClosed()));
|
||||
return stdout;
|
||||
}
|
||||
|
||||
protected String extractOutSection(String out, String head) {
|
||||
String[] split = out.split("\n");
|
||||
String xout = "";
|
||||
for (String s : split) {
|
||||
if (!s.startsWith("(python)") && !s.equals("")) {
|
||||
xout += s + "\n";
|
||||
}
|
||||
}
|
||||
return xout.split(head)[1].split("---")[0].replace("(python)", "").trim();
|
||||
}
|
||||
|
||||
protected ManagedDomainObject openDomainObject(String path) throws Exception {
|
||||
DomainFile df = env.getProject().getProjectData().getFile(path);
|
||||
assertNotNull(df);
|
||||
return new ManagedDomainObject(df, false, false, monitor);
|
||||
}
|
||||
|
||||
protected ManagedDomainObject waitDomainObject(String path) throws Exception {
|
||||
DomainFile df;
|
||||
long start = System.currentTimeMillis();
|
||||
while (true) {
|
||||
df = env.getProject().getProjectData().getFile(path);
|
||||
if (df != null) {
|
||||
return new ManagedDomainObject(df, false, false, monitor);
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
if (System.currentTimeMillis() - start > 30000) {
|
||||
throw new TimeoutException("30 seconds expired waiting for domain file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected long getMaxSnap() {
|
||||
Long maxSnap = tb.trace.getTimeManager().getMaxSnap();
|
||||
return maxSnap == null ? 0 : maxSnap;
|
||||
}
|
||||
|
||||
protected void waitTxDone() {
|
||||
waitFor(() -> tb.trace.getCurrentTransactionInfo() == null);
|
||||
}
|
||||
|
||||
public static void waitForPass(Runnable runnable) {
|
||||
AtomicReference<AssertionError> lastError = new AtomicReference<>();
|
||||
waitForCondition(() -> {
|
||||
try {
|
||||
runnable.run();
|
||||
return true;
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
lastError.set(e);
|
||||
return false;
|
||||
}
|
||||
}, () -> lastError.get().getMessage());
|
||||
}
|
||||
|
||||
public static void waitForCondition(BooleanSupplier condition,
|
||||
Supplier<String> failureMessageSupplier) throws AssertionFailedError {
|
||||
|
||||
int totalTime = 0;
|
||||
while (totalTime <= DEFAULT_WAIT_TIMEOUT * 10) {
|
||||
|
||||
if (condition.getAsBoolean()) {
|
||||
return; // success
|
||||
}
|
||||
|
||||
totalTime += sleep(DEFAULT_WAIT_DELAY * 10);
|
||||
}
|
||||
|
||||
String failureMessage = "Timed-out waiting for condition";
|
||||
if (failureMessageSupplier != null) {
|
||||
failureMessage = failureMessageSupplier.get();
|
||||
}
|
||||
|
||||
throw new AssertionFailedError(failureMessage);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,909 @@
|
|||
/* ###
|
||||
* 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 agent.drgn.rmi;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import db.Transaction;
|
||||
import generic.Unique;
|
||||
import ghidra.app.plugin.core.debug.utils.ManagedDomainObject;
|
||||
import ghidra.debug.api.tracermi.TraceRmiAcceptor;
|
||||
import ghidra.debug.api.tracermi.TraceRmiConnection;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.Float10DataType;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.listing.TraceCodeSpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class DrgnCommandsTest extends AbstractDrgnTraceRmiTest {
|
||||
|
||||
//@Test
|
||||
public void testManual() throws Exception {
|
||||
TraceRmiAcceptor acceptor = traceRmi.acceptOne(null);
|
||||
Msg.info(this,
|
||||
"Use: ghidra_trace_connect(" + sockToStringForPython(acceptor.getAddress()) + ")");
|
||||
TraceRmiConnection connection = acceptor.accept();
|
||||
Msg.info(this, "Connected: " + sockToStringForPython(connection.getRemoteAddress()));
|
||||
connection.waitClosed();
|
||||
Msg.info(this, "Closed");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectErrorNoArg() throws Exception {
|
||||
try {
|
||||
runThrowError("""
|
||||
from ghidradrgn.commands import *
|
||||
ghidra_trace_connect()
|
||||
quit()
|
||||
""");
|
||||
fail();
|
||||
}
|
||||
catch (PythonError e) {
|
||||
assertThat(e.stderr, containsString("'ghidra_trace_connect'"));
|
||||
assertThat(e.stderr, containsString("'address'"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnect() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisconnect() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_disconnect()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartTraceDefaults() throws Exception {
|
||||
// Default name and lcsp
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
assertEquals("x86:LE:64:default",
|
||||
tb.trace.getBaseLanguage().getLanguageID().getIdAsString());
|
||||
assertEquals("gcc",
|
||||
tb.trace.getBaseCompilerSpec().getCompilerSpecID().getIdAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartTraceDefaultNoFile() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_start()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) {
|
||||
assertThat(mdo.get(), instanceOf(Trace.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartTraceCustomize() throws Exception {
|
||||
runThrowError(
|
||||
addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create(start_trace=False)
|
||||
util.set_convenience_variable('ghidra-language','Toy:BE:64:default')
|
||||
util.set_convenience_variable('ghidra-compiler','default')
|
||||
ghidra_trace_start('myToy')
|
||||
quit()
|
||||
"""
|
||||
.formatted(PREAMBLE, addr));
|
||||
DomainFile df = env.getProject().getProjectData().getFile("/New Traces/myToy");
|
||||
assertNotNull(df);
|
||||
try (ManagedDomainObject mdo = new ManagedDomainObject(df, false, false, monitor)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
assertEquals("Toy:BE:64:default",
|
||||
tb.trace.getBaseLanguage().getLanguageID().getIdAsString());
|
||||
assertEquals("default",
|
||||
tb.trace.getBaseCompilerSpec().getCompilerSpecID().getIdAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopTrace() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_stop()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
DomainFile df =
|
||||
env.getProject().getProjectData().getFile(MDO);
|
||||
assertNotNull(df);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInfo() throws Exception {
|
||||
AtomicReference<String> refAddr = new AtomicReference<>();
|
||||
String out = runThrowError(addr -> {
|
||||
refAddr.set(addr);
|
||||
return """
|
||||
%s
|
||||
print('---Import---')
|
||||
ghidra_trace_info()
|
||||
print('---BeforeConnect---')
|
||||
ghidra_trace_connect('%s')
|
||||
print('---Connect---')
|
||||
ghidra_trace_info()
|
||||
print('---Create---')
|
||||
ghidra_trace_create()
|
||||
print('---Start---')
|
||||
ghidra_trace_info()
|
||||
ghidra_trace_stop()
|
||||
print('---Stop---')
|
||||
ghidra_trace_info()
|
||||
ghidra_trace_disconnect()
|
||||
print('---Disconnect---')
|
||||
ghidra_trace_info()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr);
|
||||
});
|
||||
|
||||
assertEquals("""
|
||||
Not connected to Ghidra""",
|
||||
extractOutSection(out, "---Import---"));
|
||||
assertEquals("""
|
||||
Connected to %s %s at %s
|
||||
No trace""".formatted(
|
||||
Application.getName(), Application.getApplicationVersion(), refAddr.get()),
|
||||
extractOutSection(out, "---Connect---").replaceAll("\r", ""));
|
||||
assertEquals("""
|
||||
Connected to %s %s at %s
|
||||
Trace active""".formatted(
|
||||
Application.getName(), Application.getApplicationVersion(), refAddr.get()),
|
||||
extractOutSection(out, "---Start---").replaceAll("\r", ""));
|
||||
assertEquals("""
|
||||
Connected to %s %s at %s
|
||||
No trace""".formatted(
|
||||
Application.getName(), Application.getApplicationVersion(), refAddr.get()),
|
||||
extractOutSection(out, "---Stop---").replaceAll("\r", ""));
|
||||
assertEquals("""
|
||||
Not connected to Ghidra""",
|
||||
extractOutSection(out, "---Disconnect---"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLcsp() throws Exception {
|
||||
String out = runThrowError(addr ->
|
||||
"""
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
print('---Import---')
|
||||
ghidra_trace_info_lcsp()
|
||||
print('---Create---')
|
||||
ghidra_trace_create()
|
||||
print('---File---')
|
||||
ghidra_trace_info_lcsp()
|
||||
util.set_convenience_variable('ghidra-language','DATA:BE:64:default')
|
||||
print('---Language---')
|
||||
ghidra_trace_info_lcsp()
|
||||
util.set_convenience_variable('ghidra-compiler','posStack')
|
||||
print('---Compiler---')
|
||||
ghidra_trace_info_lcsp()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
|
||||
assertEquals("""
|
||||
Selected Ghidra language: x86:LE:64:default
|
||||
Selected Ghidra compiler: gcc""",
|
||||
extractOutSection(out, "---File---").replaceAll("\r", ""));
|
||||
assertEquals("""
|
||||
Using the DATA64 compiler map
|
||||
Selected Ghidra language: DATA:BE:64:default
|
||||
Selected Ghidra compiler: pointer64""",
|
||||
extractOutSection(out, "---Language---").replaceAll("\r", ""));
|
||||
assertEquals("""
|
||||
Selected Ghidra language: DATA:BE:64:default
|
||||
Selected Ghidra compiler: posStack""",
|
||||
extractOutSection(out, "---Compiler---").replaceAll("\r", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshot() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Create snapshot')
|
||||
ghidra_trace_new_snap('Scripted snapshot')
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceSnapshot snapshot = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots());
|
||||
assertEquals(0, snapshot.getKey());
|
||||
assertEquals("Scripted snapshot", snapshot.getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutreg() throws Exception {
|
||||
String count = IntStream.iterate(0, i -> i < 32, i -> i + 1)
|
||||
.mapToObj(Integer::toString)
|
||||
.collect(Collectors.joining(",", "{", "}"));
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Create snapshot')
|
||||
ghidra_trace_new_snap('Scripted snapshot')
|
||||
ghidra_trace_putreg()
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr, count));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey();
|
||||
List<TraceObjectValue> regVals = tb.trace.getObjectManager()
|
||||
.getValuePaths(Lifespan.at(0),
|
||||
PathFilter.parse("Processes[].Threads[].Stack[].Registers"))
|
||||
.map(p -> p.getLastEntry())
|
||||
.toList();
|
||||
TraceObjectValue tobj = regVals.get(0);
|
||||
AddressSpace t1f0 = tb.trace.getBaseAddressFactory()
|
||||
.getAddressSpace(tobj.getCanonicalPath().toString());
|
||||
TraceMemorySpace regs = tb.trace.getMemoryManager().getMemorySpace(t1f0, false);
|
||||
|
||||
RegisterValue rip = regs.getValue(snap, tb.reg("rip"));
|
||||
assertEquals("3a40cdf7ff7f0000", rip.getUnsignedValue().toString(16));
|
||||
|
||||
try (Transaction tx = tb.trace.openTransaction("Float80 unit")) {
|
||||
TraceCodeSpace code = tb.trace.getCodeManager().getCodeSpace(t1f0, true);
|
||||
code.definedData()
|
||||
.create(Lifespan.nowOn(0), tb.reg("st0"), Float10DataType.dataType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelreg() throws Exception {
|
||||
String count = IntStream.iterate(0, i -> i < 32, i -> i + 1)
|
||||
.mapToObj(Integer::toString)
|
||||
.collect(Collectors.joining(",", "{", "}"));
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Create snapshot')
|
||||
ghidra_trace_new_snap('Scripted snapshot')
|
||||
ghidra_trace_putreg()
|
||||
ghidra_trace_delreg()
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr, count));
|
||||
// The spaces will be left over, but the values should be zeroed
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
long snap = Unique.assertOne(tb.trace.getTimeManager().getAllSnapshots()).getKey();
|
||||
List<TraceObjectValue> regVals = tb.trace.getObjectManager()
|
||||
.getValuePaths(Lifespan.at(0),
|
||||
PathFilter.parse("Processes[].Threads[].Stack[].Registers"))
|
||||
.map(p -> p.getLastEntry())
|
||||
.toList();
|
||||
TraceObjectValue tobj = regVals.get(0);
|
||||
AddressSpace t1f0 = tb.trace.getBaseAddressFactory()
|
||||
.getAddressSpace(tobj.getCanonicalPath().toString());
|
||||
TraceMemorySpace regs = tb.trace.getMemoryManager().getMemorySpace(t1f0, false);
|
||||
|
||||
RegisterValue rax = regs.getValue(snap, tb.reg("rax"));
|
||||
assertEquals("0", rax.getUnsignedValue().toString(16));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateObj() throws Exception {
|
||||
String out = runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_start()
|
||||
ghidra_trace_txstart('Create Object')
|
||||
print('---Id---')
|
||||
ghidra_trace_create_obj('Test.Objects[1]')
|
||||
print('---')
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject object = tb.trace.getObjectManager()
|
||||
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
|
||||
assertNotNull(object);
|
||||
String created = extractOutSection(out, "---Id---");
|
||||
long id = Long.parseLong(created.split("id=")[1].split(",")[0]);
|
||||
assertEquals(object.getKey(), id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertObj() throws Exception {
|
||||
String out = runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_start()
|
||||
ghidra_trace_txstart('Create Object')
|
||||
ghidra_trace_create_obj('Test.Objects[1]')
|
||||
print('---Lifespan---')
|
||||
ghidra_trace_insert_obj('Test.Objects[1]')
|
||||
print('---')
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject object = tb.trace.getObjectManager()
|
||||
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
|
||||
assertNotNull(object);
|
||||
Lifespan life = Unique.assertOne(object.getLife().spans());
|
||||
assertEquals(Lifespan.nowOn(0), life);
|
||||
assertEquals("Inserted object: lifespan=[0,+inf)",
|
||||
extractOutSection(out, "---Lifespan---"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveObj() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Create Object')
|
||||
ghidra_trace_create_obj('Test.Objects[1]')
|
||||
ghidra_trace_insert_obj('Test.Objects[1]')
|
||||
ghidra_trace_set_snap(1)
|
||||
ghidra_trace_remove_obj('Test.Objects[1]')
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject object = tb.trace.getObjectManager()
|
||||
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
|
||||
assertNotNull(object);
|
||||
Lifespan life = Unique.assertOne(object.getLife().spans());
|
||||
assertEquals(Lifespan.at(0), life);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T runTestSetValue(String extra, String drgnExpr, String gtype)
|
||||
throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Create Object')
|
||||
ghidra_trace_create_obj('Test.Objects[1]')
|
||||
ghidra_trace_insert_obj('Test.Objects[1]')
|
||||
%s
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'test', %s, '%s')
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr, extra, drgnExpr, gtype));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject object = tb.trace.getObjectManager()
|
||||
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
|
||||
assertNotNull(object);
|
||||
TraceObjectValue value = object.getValue(0, "test");
|
||||
return value == null ? null : (T) value.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueNull() throws Exception {
|
||||
assertNull(runTestSetValue("", "None", "VOID"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueBool() throws Exception {
|
||||
assertEquals(Boolean.TRUE, runTestSetValue("", "True", "BOOL"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueByte() throws Exception {
|
||||
assertEquals(Byte.valueOf((byte) 1), runTestSetValue("", "'1'", "BYTE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueChar() throws Exception {
|
||||
assertEquals(Character.valueOf('A'), runTestSetValue("", "'A'", "CHAR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueShort() throws Exception {
|
||||
assertEquals(Short.valueOf((short) 1), runTestSetValue("", "'1'", "SHORT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueInt() throws Exception {
|
||||
assertEquals(Integer.valueOf(1), runTestSetValue("", "'1'", "INT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueLong() throws Exception {
|
||||
assertEquals(Long.valueOf(1), runTestSetValue("", "'1'", "LONG"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueString() throws Exception {
|
||||
assertEquals("HelloWorld!", runTestSetValue("", "\'HelloWorld!\'", "STRING"));
|
||||
}
|
||||
|
||||
@Test //- how do we input long strings in python
|
||||
public void testSetValueStringWide() throws Exception {
|
||||
assertEquals("HelloWorld!", runTestSetValue("", "u\'HelloWorld!\'", "STRING"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueBoolArr() throws Exception {
|
||||
assertArrayEquals(new boolean[] { true, false },
|
||||
runTestSetValue("", "[True,False]", "BOOL_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueByteArrUsingString() throws Exception {
|
||||
assertArrayEquals(new byte[] { 'H', 1, 'W' },
|
||||
runTestSetValue("", "'H\\1W'", "BYTE_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueByteArrUsingArray() throws Exception {
|
||||
assertArrayEquals(new byte[] { 'H', 0, 'W' },
|
||||
runTestSetValue("", "['H',0,'W']", "BYTE_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueCharArrUsingString() throws Exception {
|
||||
assertArrayEquals(new char[] { 'H', 1, 'W' },
|
||||
runTestSetValue("", "'H\\1W'", "CHAR_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueCharArrUsingArray() throws Exception {
|
||||
assertArrayEquals(new char[] { 'H', 0, 'W' },
|
||||
runTestSetValue("", "['H',0,'W']", "CHAR_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueShortArrUsingString() throws Exception {
|
||||
assertArrayEquals(new short[] { 'H', 1, 'W' },
|
||||
runTestSetValue("", "'H\\1W'", "SHORT_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueShortArrUsingArray() throws Exception {
|
||||
assertArrayEquals(new short[] { 'H', 0, 'W' },
|
||||
runTestSetValue("", "['H',0,'W']", "SHORT_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueIntArrayUsingMixedArray() throws Exception {
|
||||
// Because explicit array type is chosen, we get null terminator
|
||||
assertArrayEquals(new int[] { 'H', 0, 'W' },
|
||||
runTestSetValue("", "['H',0,'W']", "INT_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueIntArrUsingArray() throws Exception {
|
||||
assertArrayEquals(new int[] { 1, 2, 3, 4 },
|
||||
runTestSetValue("", "[1,2,3,4]", "INT_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueLongArr() throws Exception {
|
||||
assertArrayEquals(new long[] { 1, 2, 3, 4 },
|
||||
runTestSetValue("", "[1,2,3,4]", "LONG_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueStringArr() throws Exception {
|
||||
assertArrayEquals(new String[] { "1", "A", "dead", "beef" },
|
||||
runTestSetValue("", "['1','A','dead','beef']", "STRING_ARR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueAddress() throws Exception {
|
||||
Address address = runTestSetValue("", "0xdeadbeef", "ADDRESS");
|
||||
// Don't have the address factory to create expected address
|
||||
assertEquals(0xdeadbeefL, address.getOffset());
|
||||
assertEquals("ram", address.getAddressSpace().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueObject() throws Exception {
|
||||
TraceObject object = runTestSetValue("", "'Test.Objects[1]'", "OBJECT");
|
||||
assertEquals("Test.Objects[1]", object.getCanonicalPath().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetainValues() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Create Object')
|
||||
ghidra_trace_create_obj('Test.Objects[1]')
|
||||
ghidra_trace_insert_obj('Test.Objects[1]')
|
||||
ghidra_trace_set_value('Test.Objects[1]', '[1]', '"A"', 'STRING')
|
||||
ghidra_trace_set_value('Test.Objects[1]', '[2]', '"B"', 'STRING')
|
||||
ghidra_trace_set_value('Test.Objects[1]', '[3]', '"C"', 'STRING')
|
||||
ghidra_trace_set_snap(10)
|
||||
ghidra_trace_retain_values('Test.Objects[1]', '[1] [3]')
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject object = tb.trace.getObjectManager()
|
||||
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
|
||||
assertNotNull(object);
|
||||
assertEquals(Map.ofEntries(
|
||||
Map.entry("[1]", Lifespan.nowOn(0)),
|
||||
Map.entry("[2]", Lifespan.span(0, 9)),
|
||||
Map.entry("[3]", Lifespan.nowOn(0))),
|
||||
object.getValues(Lifespan.ALL)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(v -> v.getEntryKey(), v -> v.getLifespan())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetObj() throws Exception {
|
||||
String out = runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_start()
|
||||
ghidra_trace_txstart('Create Object')
|
||||
print('---Id---')
|
||||
ghidra_trace_create_obj('Test.Objects[1]')
|
||||
print('---')
|
||||
ghidra_trace_txcommit()
|
||||
print('---GetObject---')
|
||||
ghidra_trace_get_obj('Test.Objects[1]')
|
||||
print('---')
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject object = tb.trace.getObjectManager()
|
||||
.getObjectByCanonicalPath(KeyPath.parse("Test.Objects[1]"));
|
||||
assertNotNull(object);
|
||||
assertEquals("1\tTest.Objects[1]", extractOutSection(out, "---GetObject---"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetValues() throws Exception {
|
||||
String out = runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Create Object')
|
||||
ghidra_trace_create_obj('Test.Objects[1]')
|
||||
ghidra_trace_insert_obj('Test.Objects[1]')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vnull', None, 'VOID')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vbool', True, 'BOOL')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vbyte', '1', 'BYTE')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vchar', 'A', 'CHAR')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vshort', '2', 'SHORT')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vint', '3', 'INT')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vlong', '4', 'LONG')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vstring', 'Hello', 'STRING')
|
||||
vboolarr = [True, False]
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vboolarr', vboolarr, 'BOOL_ARR')
|
||||
vbytearr = [1, 2, 3]
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vbytearr', vbytearr, 'BYTE_ARR')
|
||||
vchararr = 'Hello'
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vchararr', vchararr, 'CHAR_ARR')
|
||||
vshortarr = [1, 2, 3]
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vshortarr', vshortarr, 'SHORT_ARR')
|
||||
vintarr = [1, 2, 3]
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vintarr', vintarr, 'INT_ARR')
|
||||
vlongarr = [1, 2, 3]
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vlongarr', vlongarr, 'LONG_ARR')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vaddr', 0xdeadbeef, 'ADDRESS')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vobj', 'Test.Objects[1]', 'OBJECT')
|
||||
ghidra_trace_txcommit()
|
||||
print('---GetValues---')
|
||||
ghidra_trace_get_values('Test.Objects[1].')
|
||||
print('---')
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
assertEquals("""
|
||||
Parent Key Span Value Type
|
||||
Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS
|
||||
Test.Objects[1] vbool [0,+inf) True BOOL
|
||||
Test.Objects[1] vboolarr [0,+inf) [True, False] BOOL_ARR
|
||||
Test.Objects[1] vbyte [0,+inf) 1 BYTE
|
||||
Test.Objects[1] vbytearr [0,+inf) b'\\x01\\x02\\x03' BYTE_ARR
|
||||
Test.Objects[1] vchar [0,+inf) 'A' CHAR
|
||||
Test.Objects[1] vchararr [0,+inf) 'Hello' CHAR_ARR
|
||||
Test.Objects[1] vint [0,+inf) 3 INT
|
||||
Test.Objects[1] vintarr [0,+inf) [1, 2, 3] INT_ARR
|
||||
Test.Objects[1] vlong [0,+inf) 4 LONG
|
||||
Test.Objects[1] vlongarr [0,+inf) [1, 2, 3] LONG_ARR
|
||||
Test.Objects[1] vobj [0,+inf) Test.Objects[1] OBJECT
|
||||
Test.Objects[1] vshort [0,+inf) 2 SHORT
|
||||
Test.Objects[1] vshortarr [0,+inf) [1, 2, 3] SHORT_ARR
|
||||
Test.Objects[1] vstring [0,+inf) 'Hello' STRING""",
|
||||
extractOutSection(out, "---GetValues---").replaceAll("\r", ""));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetValuesRng() throws Exception {
|
||||
String out = runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Create Object')
|
||||
ghidra_trace_create_obj('Test.Objects[1]')
|
||||
ghidra_trace_insert_obj('Test.Objects[1]')
|
||||
ghidra_trace_set_value('Test.Objects[1]', 'vaddr', 0xdeadbeef, 'ADDRESS')
|
||||
ghidra_trace_txcommit()
|
||||
print('---GetValues---')
|
||||
ghidra_trace_get_values_rng(0xdeadbeef, 10)
|
||||
print('---')
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
assertEquals("""
|
||||
Parent Key Span Value Type
|
||||
Test.Objects[1] vaddr [0,+inf) ram:deadbeef ADDRESS""",
|
||||
extractOutSection(out, "---GetValues---").replaceAll("\r", ""));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivateObject() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
#set language c++
|
||||
ghidra_trace_txstart('Create Object')
|
||||
ghidra_trace_create_obj('Test.Objects[1]')
|
||||
ghidra_trace_insert_obj('Test.Objects[1]')
|
||||
ghidra_trace_txcommit()
|
||||
ghidra_trace_activate('Test.Objects[1]')
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
assertSame(mdo.get(), traceManager.getCurrentTrace());
|
||||
assertEquals("Test.Objects[1]",
|
||||
traceManager.getCurrentObject().getCanonicalPath().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisassemble() throws Exception {
|
||||
String out = runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Tx')
|
||||
pc = get_pc()
|
||||
ghidra_trace_putmem(pc, 16)
|
||||
print('---Disassemble---')
|
||||
ghidra_trace_disassemble(pc)
|
||||
print('---')
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
// Not concerned about specifics, so long as disassembly occurs
|
||||
long total = 0;
|
||||
for (CodeUnit cu : tb.trace.getCodeManager().definedUnits().get(0, true)) {
|
||||
total += cu.getLength();
|
||||
}
|
||||
String extract = extractOutSection(out, "---Disassemble---");
|
||||
String[] split = extract.split("\r\n");
|
||||
// NB: core.12137 has no memory
|
||||
//assertEquals("Disassembled %d bytes".formatted(total),
|
||||
// split[0]);
|
||||
assertEquals(0, total);
|
||||
assertEquals("", split[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutProcesses() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_start()
|
||||
ghidra_trace_txstart('Tx')
|
||||
ghidra_trace_put_processes()
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/drgn/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
// Would be nice to control / validate the specifics
|
||||
Collection<TraceObject> processes = tb.trace.getObjectManager()
|
||||
.getValuePaths(Lifespan.at(0), PathFilter.parse("Processes[]"))
|
||||
.map(p -> p.getDestination(null))
|
||||
.toList();
|
||||
assertEquals(0, processes.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutEnvironment() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Tx')
|
||||
ghidra_trace_put_environment()
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
// Assumes LLDB on Linux amd64
|
||||
TraceObject envobj =
|
||||
Objects.requireNonNull(tb.objAny("Processes[].Environment", Lifespan.at(0)));
|
||||
assertEquals("drgn", envobj.getValue(0, "_debugger").getValue());
|
||||
assertEquals("X86_64", envobj.getValue(0, "_arch").getValue());
|
||||
assertEquals("Language.C", envobj.getValue(0, "_os").getValue());
|
||||
assertEquals("little", envobj.getValue(0, "_endian").getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutRegions() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Tx')
|
||||
ghidra_trace_put_regions()
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
// Would be nice to control / validate the specifics
|
||||
Collection<? extends TraceMemoryRegion> all =
|
||||
tb.trace.getMemoryManager().getAllRegions();
|
||||
assertThat(all.size(), greaterThan(2));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutModules() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Tx')
|
||||
ghidra_trace_put_modules()
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
// Would be nice to control / validate the specifics
|
||||
Collection<? extends TraceModule> all = tb.trace.getModuleManager().getAllModules();
|
||||
TraceModule modBash =
|
||||
Unique.assertOne(all.stream().filter(m -> m.getName().contains("helloWorld")));
|
||||
assertNotEquals(tb.addr(0), Objects.requireNonNull(modBash.getBase()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutThreads() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Tx')
|
||||
ghidra_trace_put_threads()
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
// Would be nice to control / validate the specifics
|
||||
Collection<? extends TraceThread> threads = tb.trace.getThreadManager().getAllThreads();
|
||||
assertEquals(1, threads.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutFrames() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
ghidra_trace_create()
|
||||
ghidra_trace_txstart('Tx')
|
||||
ghidra_trace_put_frames()
|
||||
ghidra_trace_txcommit()
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
// Would be nice to control / validate the specifics
|
||||
List<TraceObject> stack = tb.trace.getObjectManager()
|
||||
.getValuePaths(Lifespan.at(0),
|
||||
PathFilter.parse("Processes[0].Threads[].Stack[]"))
|
||||
.map(p -> p.getDestination(null))
|
||||
.toList();
|
||||
assertEquals(7, stack.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinimal() throws Exception {
|
||||
runThrowError(addr -> """
|
||||
%s
|
||||
ghidra_trace_connect('%s')
|
||||
print('FINISHED')
|
||||
quit()
|
||||
""".formatted(PREAMBLE, addr));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
/* ###
|
||||
* 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 agent.drgn.rmi;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.Unique;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.debug.utils.ManagedDomainObject;
|
||||
import ghidra.debug.api.tracermi.RemoteMethod;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.target.path.PathPattern;
|
||||
|
||||
public class DrgnMethodsTest extends AbstractDrgnTraceRmiTest {
|
||||
|
||||
@Test
|
||||
public void testExecuteCapture() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
RemoteMethod execute = conn.getMethod("execute");
|
||||
assertEquals(false, execute.parameters().get("to_string").getDefaultValue());
|
||||
assertEquals("11\n",
|
||||
execute.invoke(Map.of(
|
||||
"cmd", "print(3+4*2)",
|
||||
"to_string", true)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
start(conn, null);
|
||||
}
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
// Just confirm it's present
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshProcesses() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
start(conn, null);
|
||||
txCreate(conn, "Processes");
|
||||
|
||||
RemoteMethod attachCore = conn.getMethod("attach_core");
|
||||
RemoteMethod refreshProcesses = conn.getMethod("refresh_processes");
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject processes = Objects.requireNonNull(tb.objAny0("Processes"));
|
||||
|
||||
refreshProcesses.invoke(Map.of("node", processes));
|
||||
|
||||
List<TraceObject> list = tb.trace.getObjectManager()
|
||||
.getValuePaths(Lifespan.at(getMaxSnap()), PathFilter.parse("Processes[]"))
|
||||
.map(p -> p.getDestination(null))
|
||||
.toList();
|
||||
assertEquals(1, list.size());
|
||||
|
||||
ResourceFile rf = Application.getModuleDataFile("TestResources", CORE);
|
||||
attachCore.invoke(Map.of("processes", processes, "core", rf.getAbsolutePath()));
|
||||
refreshProcesses.invoke(Map.of("node", processes));
|
||||
|
||||
list = tb.trace.getObjectManager()
|
||||
.getValuePaths(Lifespan.at(getMaxSnap()), PathFilter.parse("Processes[]"))
|
||||
.map(p -> p.getDestination(null))
|
||||
.toList();
|
||||
assertEquals(2, list.size());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshEnvironment() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
String path = "Processes[].Environment";
|
||||
start(conn, null);
|
||||
txPut(conn, "all");
|
||||
|
||||
RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment");
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject envobj = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshEnvironment.invoke(Map.of("node", envobj));
|
||||
|
||||
assertEquals("drgn", envobj.getValue(0, "_debugger").getValue());
|
||||
assertEquals("X86_64", envobj.getValue(0, "_arch").getValue());
|
||||
assertEquals("Language.C", envobj.getValue(0, "_os").getValue());
|
||||
assertEquals("little", envobj.getValue(0, "_endian").getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshThreads() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
String path = "Processes[].Threads";
|
||||
start(conn, null);
|
||||
txCreate(conn, path);
|
||||
|
||||
RemoteMethod refreshThreads = conn.getMethod("refresh_threads");
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject threads = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshThreads.invoke(Map.of("node", threads));
|
||||
|
||||
int listSize = tb.trace.getThreadManager().getAllThreads().size();
|
||||
assertEquals(1, listSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshStack() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
String path = "Processes[].Threads[].Stack";
|
||||
start(conn, null);
|
||||
txPut(conn, "processes");
|
||||
|
||||
RemoteMethod refreshStack = conn.getMethod("refresh_stack");
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
txPut(conn, "frames");
|
||||
TraceObject stack = Objects.requireNonNull(tb.objAny0(path));
|
||||
refreshStack.invoke(Map.of("node", stack));
|
||||
|
||||
// Would be nice to control / validate the specifics
|
||||
List<TraceObject> list = tb.trace.getObjectManager()
|
||||
.getValuePaths(Lifespan.at(0),
|
||||
PathFilter.parse("Processes[].Threads[].Stack[]"))
|
||||
.map(p -> p.getDestination(null))
|
||||
.toList();
|
||||
assertEquals(7, list.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshRegisters() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
String path = "Processes[].Threads[].Stack[].Registers";
|
||||
start(conn, null);
|
||||
conn.execute("ghidra_trace_txstart('Tx')");
|
||||
conn.execute("ghidra_trace_putreg()");
|
||||
conn.execute("ghidra_trace_delreg()");
|
||||
conn.execute("ghidra_trace_txcommit()");
|
||||
|
||||
RemoteMethod refreshRegisters = conn.getMethod("refresh_registers");
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject registers = Objects.requireNonNull(tb.objAny(path, Lifespan.at(0)));
|
||||
refreshRegisters.invoke(Map.of("node", registers));
|
||||
|
||||
long snap = 0;
|
||||
AddressSpace t1f0 = tb.trace.getBaseAddressFactory()
|
||||
.getAddressSpace(registers.getCanonicalPath().toString());
|
||||
TraceMemorySpace regs = tb.trace.getMemoryManager().getMemorySpace(t1f0, false);
|
||||
RegisterValue rip = regs.getValue(snap, tb.reg("rip"));
|
||||
assertEquals("3a40cdf7ff7f0000", rip.getUnsignedValue().toString(16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshMappings() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
String path = "Processes[].Memory";
|
||||
start(conn, null);
|
||||
txCreate(conn, path);
|
||||
|
||||
RemoteMethod refreshMappings = conn.getMethod("refresh_mappings");
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject memory = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshMappings.invoke(Map.of("node", memory));
|
||||
|
||||
// Would be nice to control / validate the specifics
|
||||
Collection<? extends TraceMemoryRegion> all =
|
||||
tb.trace.getMemoryManager().getAllRegions();
|
||||
assertThat(all.size(), greaterThan(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshModules() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
String path = "Processes[].Modules";
|
||||
start(conn, null);
|
||||
txCreate(conn, path);
|
||||
|
||||
RemoteMethod refreshModules = conn.getMethod("refresh_modules");
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject modules = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshModules.invoke(Map.of("node", modules));
|
||||
|
||||
// Would be nice to control / validate the specifics
|
||||
Collection<? extends TraceModule> all = tb.trace.getModuleManager().getAllModules();
|
||||
TraceModule mod =
|
||||
Unique.assertOne(all.stream().filter(m -> m.getName().contains("helloWorld")));
|
||||
assertNotEquals(tb.addr(0), Objects.requireNonNull(mod.getBase()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivateThread() throws Exception {
|
||||
try (PythonAndConnection conn = startAndConnectDrgn()) {
|
||||
start(conn, null);
|
||||
txPut(conn, "processes");
|
||||
|
||||
RemoteMethod activateThread = conn.getMethod("activate_thread");
|
||||
try (ManagedDomainObject mdo = openDomainObject(MDO)) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
txPut(conn, "threads");
|
||||
|
||||
PathPattern pattern =
|
||||
PathFilter.parse("Processes[].Threads[]").getSingletonPattern();
|
||||
List<TraceObject> list = tb.trace.getObjectManager()
|
||||
.getValuePaths(Lifespan.at(0), pattern)
|
||||
.map(p -> p.getDestination(null))
|
||||
.toList();
|
||||
assertEquals(1, list.size());
|
||||
|
||||
for (TraceObject t : list) {
|
||||
activateThread.invoke(Map.of("thread", t));
|
||||
String out = conn.executeCapture("print(util.selected_thread())").strip();
|
||||
List<String> indices = pattern.matchKeys(t.getCanonicalPath(), true);
|
||||
assertEquals("%s".formatted(indices.get(1)), out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void start(PythonAndConnection conn, String obj) {
|
||||
conn.execute("from ghidradrgn.commands import *");
|
||||
conn.execute("ghidra_trace_create()");
|
||||
}
|
||||
|
||||
private void txPut(PythonAndConnection conn, String obj) {
|
||||
conn.execute("ghidra_trace_txstart('Tx')");
|
||||
conn.execute("ghidra_trace_put_" + obj + "()");
|
||||
conn.execute("ghidra_trace_txcommit()");
|
||||
}
|
||||
|
||||
private void txCreate(PythonAndConnection conn, String path) {
|
||||
conn.execute("ghidra_trace_txstart('Fake')");
|
||||
conn.execute("ghidra_trace_create_obj('%s')".formatted(path));
|
||||
conn.execute("ghidra_trace_txcommit()");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue