mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-4894: Improve and better test Java debug connector.
This commit is contained in:
parent
24a5928c3c
commit
cce33f772e
44 changed files with 5159 additions and 1345 deletions
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -95,7 +95,7 @@ public class DbgEngHooksTest extends AbstractDbgEngTraceRmiTest {
|
|||
txPut(conn, "processes");
|
||||
|
||||
waitForPass(() -> {
|
||||
TraceObject proc = tb.objAny("Processes[]");
|
||||
TraceObject proc = tb.objAny0("Processes[]");
|
||||
assertNotNull(proc);
|
||||
assertEquals("STOPPED", tb.objValue(proc, lastSnap(conn), "_state"));
|
||||
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||
|
@ -107,7 +107,7 @@ public class DbgEngHooksTest extends AbstractDbgEngTraceRmiTest {
|
|||
|
||||
// Via method, go is asynchronous
|
||||
RemoteMethod go = conn.conn.getMethod("go");
|
||||
TraceObject proc = tb.objAny("Processes[]");
|
||||
TraceObject proc = tb.objAny0("Processes[]");
|
||||
go.invoke(Map.of("process", proc));
|
||||
|
||||
waitForPass(
|
||||
|
@ -273,7 +273,7 @@ public class DbgEngHooksTest extends AbstractDbgEngTraceRmiTest {
|
|||
""");
|
||||
waitRunning("Missed running after go");
|
||||
|
||||
TraceObject proc = waitForValue(() -> tb.objAny("Processes[]"));
|
||||
TraceObject proc = waitForValue(() -> tb.objAny0("Processes[]"));
|
||||
waitForPass(() -> {
|
||||
assertEquals("RUNNING", tb.objValue(proc, lastSnap(conn), "_state"));
|
||||
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||
|
@ -285,7 +285,7 @@ public class DbgEngHooksTest extends AbstractDbgEngTraceRmiTest {
|
|||
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||
txPut(conn, "processes");
|
||||
|
||||
TraceObject proc = waitForValue(() -> tb.objAny("Processes[]"));
|
||||
TraceObject proc = waitForValue(() -> tb.objAny0("Processes[]"));
|
||||
waitForPass(() -> {
|
||||
assertEquals("STOPPED", tb.objValue(proc, lastSnap(conn), "_state"));
|
||||
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||
|
@ -307,7 +307,7 @@ public class DbgEngHooksTest extends AbstractDbgEngTraceRmiTest {
|
|||
assertNotNull(snapshot);
|
||||
assertEquals("Exited with code 0", snapshot.getDescription());
|
||||
|
||||
TraceObject proc = tb.objAny("Processes[]");
|
||||
TraceObject proc = tb.objAny0("Processes[]");
|
||||
assertNotNull(proc);
|
||||
Object val = tb.objValue(proc, lastSnap(conn), "_exit_code");
|
||||
assertThat(val, instanceOf(Number.class));
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -82,7 +82,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
RemoteMethod refreshAvailable = conn.getMethod("refresh_available");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject available = Objects.requireNonNull(tb.objAny("Available"));
|
||||
TraceObject available = Objects.requireNonNull(tb.objAny0("Available"));
|
||||
|
||||
refreshAvailable.invoke(Map.of("node", available));
|
||||
|
||||
|
@ -111,7 +111,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
conn.execute("util.dbg.ba(expr=pc+4)");
|
||||
txPut(conn, "breakpoints");
|
||||
TraceObject breakpoints =
|
||||
Objects.requireNonNull(tb.objAny("Processes[].Breakpoints"));
|
||||
Objects.requireNonNull(tb.objAny0("Processes[].Breakpoints"));
|
||||
refreshBreakpoints.invoke(Map.of("node", breakpoints));
|
||||
|
||||
List<TraceObjectValue> procBreakLocVals = tb.trace.getObjectManager()
|
||||
|
@ -150,7 +150,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
conn.execute("util.dbg.ba(expr=pc+4, access=DbgEng.DEBUG_BREAK_READ)");
|
||||
conn.execute("util.dbg.ba(expr=pc+8, access=DbgEng.DEBUG_BREAK_WRITE)");
|
||||
TraceObject locations =
|
||||
Objects.requireNonNull(tb.objAny("Processes[].Breakpoints"));
|
||||
Objects.requireNonNull(tb.objAny0("Processes[].Breakpoints"));
|
||||
refreshProcWatchpoints.invoke(Map.of("node", locations));
|
||||
|
||||
List<TraceObjectValue> procBreakVals = tb.trace.getObjectManager()
|
||||
|
@ -193,7 +193,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
RemoteMethod refreshProcesses = conn.getMethod("refresh_processes");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject processes = Objects.requireNonNull(tb.objAny("Processes"));
|
||||
TraceObject processes = Objects.requireNonNull(tb.objAny0("Processes"));
|
||||
|
||||
refreshProcesses.invoke(Map.of("node", processes));
|
||||
|
||||
|
@ -217,7 +217,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject env = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject env = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshEnvironment.invoke(Map.of("node", env));
|
||||
|
||||
|
@ -240,7 +240,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
RemoteMethod refreshThreads = conn.getMethod("refresh_threads");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject threads = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject threads = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshThreads.invoke(Map.of("node", threads));
|
||||
|
||||
|
@ -263,7 +263,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
txPut(conn, "frames");
|
||||
TraceObject stack = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject stack = Objects.requireNonNull(tb.objAny0(path));
|
||||
refreshStack.invoke(Map.of("node", stack));
|
||||
|
||||
// Would be nice to control / validate the specifics
|
||||
|
@ -315,7 +315,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
RemoteMethod refreshMappings = conn.getMethod("refresh_mappings");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject memory = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject memory = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshMappings.invoke(Map.of("node", memory));
|
||||
|
||||
|
@ -337,7 +337,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
RemoteMethod refreshModules = conn.getMethod("refresh_modules");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject modules = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject modules = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshModules.invoke(Map.of("node", modules));
|
||||
|
||||
|
@ -390,7 +390,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/netstat.exe")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject proc2 = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc2 = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
removeProcess.invoke(Map.of("process", proc2));
|
||||
|
||||
String out = conn.executeCapture("print(list(util.process_list()))");
|
||||
|
@ -451,7 +451,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/netstat.exe")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
detach.invoke(Map.of("process", proc));
|
||||
|
||||
String out = conn.executeCapture("print(list(util.process_list()))");
|
||||
|
@ -512,7 +512,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped("Missed initial stop");
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
kill.invoke(Map.of("process", proc));
|
||||
|
||||
String out = conn.executeCapture("print(list(util.process_list()))");
|
||||
|
@ -535,7 +535,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped("Missed initial stop");
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
go.invoke(Map.of("process", proc));
|
||||
|
@ -561,7 +561,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
waitStopped("Missed initial stop");
|
||||
txPut(conn, "threads");
|
||||
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny("Processes[].Threads[]"));
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny0("Processes[].Threads[]"));
|
||||
|
||||
while (!getInst(conn).contains("call")) {
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
|
@ -595,7 +595,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
waitStopped("Missed initial stop");
|
||||
txPut(conn, "threads");
|
||||
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny("Processes[].Threads[]"));
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny0("Processes[].Threads[]"));
|
||||
|
||||
while (!getInst(conn).contains("call")) {
|
||||
stepOver.invoke(Map.of("thread", thread));
|
||||
|
@ -623,7 +623,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "threads");
|
||||
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny("Processes[].Threads[]"));
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny0("Processes[].Threads[]"));
|
||||
while (!getInst(conn).contains("call")) {
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
}
|
||||
|
@ -656,7 +656,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
waitStopped("Missed initial stop");
|
||||
txPut(conn, "threads");
|
||||
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny("Processes[].Threads[]"));
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny0("Processes[].Threads[]"));
|
||||
|
||||
while (!getInst(conn).contains("call")) {
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
|
@ -683,7 +683,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
|
||||
long address = getAddressAtOffset(conn, 0);
|
||||
breakAddress.invoke(Map.of("process", proc, "address", tb.addr(address)));
|
||||
|
@ -723,7 +723,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/pydbg/notepad.exe")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
|
||||
long address = getAddressAtOffset(conn, 0);
|
||||
breakAddress.invoke(Map.of("process", proc, "address", tb.addr(address)));
|
||||
|
@ -764,7 +764,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped("Missed initial stop");
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
long address = getAddressAtOffset(conn, 0);
|
||||
AddressRange range = tb.range(address, address + 3); // length 4
|
||||
breakRange.invoke(Map.of("process", proc, "range", range));
|
||||
|
@ -809,7 +809,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped("Missed initial stop");
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
long address = getAddressAtOffset(conn, 0);
|
||||
AddressRange range = tb.range(address, address + 3); // length 4
|
||||
breakRange.invoke(Map.of("process", proc, "range", range));
|
||||
|
@ -854,7 +854,7 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped("Missed initial stop");
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
long address = getAddressAtOffset(conn, 0);
|
||||
AddressRange range = tb.range(address, address + 3); // length 4
|
||||
breakRange.invoke(Map.of("process", proc, "range", range));
|
||||
|
@ -900,11 +900,11 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
long address = getAddressAtOffset(conn, 0);
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
breakAddress.invoke(Map.of("process", proc, "address", tb.addr(address)));
|
||||
|
||||
txPut(conn, "breakpoints");
|
||||
TraceObject bpt = Objects.requireNonNull(tb.objAny("Processes[].Breakpoints[]"));
|
||||
TraceObject bpt = Objects.requireNonNull(tb.objAny0("Processes[].Breakpoints[]"));
|
||||
|
||||
toggleBreakpoint.invoke(Map.of("breakpoint", bpt, "enabled", false));
|
||||
|
||||
|
@ -926,11 +926,11 @@ public class DbgEngMethodsTest extends AbstractDbgEngTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
long address = getAddressAtOffset(conn, 0);
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
breakAddress.invoke(Map.of("process", proc, "address", tb.addr(address)));
|
||||
|
||||
txPut(conn, "breakpoints");
|
||||
TraceObject bpt = Objects.requireNonNull(tb.objAny("Processes[].Breakpoints[]"));
|
||||
TraceObject bpt = Objects.requireNonNull(tb.objAny0("Processes[].Breakpoints[]"));
|
||||
|
||||
deleteBreakpoint.invoke(Map.of("breakpoint", bpt));
|
||||
|
||||
|
|
|
@ -0,0 +1,581 @@
|
|||
/* ###
|
||||
* 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.java.rmi;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.junit.Before;
|
||||
|
||||
import generic.Unique;
|
||||
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.plugin.core.misc.RecoverySnapshotMgrPlugin;
|
||||
import ghidra.app.services.TraceRmiService;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.debug.api.tracermi.*;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginsConfiguration;
|
||||
import ghidra.framework.plugintool.util.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.util.Msg;
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
public abstract class AbstractJavaTraceRmiTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
|
||||
/**
|
||||
* TODO: It would be nice if we didn't have to initialize a "Ghidra application" in order to use
|
||||
* the RmiClient; however, I'm not sure that's worth it.
|
||||
*/
|
||||
public static final String PREAMBLE = """
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import com.sun.jdi.*;
|
||||
import com.sun.jdi.request.*;
|
||||
import ghidra.dbg.jdi.rmi.jpda.*;
|
||||
import ghidra.dbg.jdi.manager.impl.*;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.GhidraApplicationConfiguration;
|
||||
import ghidra.GhidraApplicationLayout;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.app.plugin.core.debug.client.tracermi.*;
|
||||
import ghidra.rmi.trace.TraceRmi.MemoryState;
|
||||
|
||||
GhidraApplicationLayout layout = new GhidraApplicationLayout();
|
||||
GhidraApplicationConfiguration config = new GhidraApplicationConfiguration();
|
||||
config.setShowSplashScreen(false);
|
||||
Application.initializeApplication(layout, config);
|
||||
|
||||
JdiManagerImpl manager = new JdiManagerImpl();
|
||||
JdiManager jdiManager = new JdiManager(manager);
|
||||
JdiCommands cmds = jdiManager.getCommands();
|
||||
JdiMethods meths = jdiManager.getMethods();
|
||||
JdiHooks hooks = jdiManager.getHooks();
|
||||
hooks.installHooks();
|
||||
""";
|
||||
// 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 = 300;
|
||||
protected static final int QUIT_TIMEOUT_MS = 1000;
|
||||
protected static final long EXT_TIMEOUT_MS = 5000;
|
||||
protected static final long EXT_RETRY_MS = 500;
|
||||
|
||||
protected TraceRmiService traceRmi;
|
||||
private Path jshellPath;
|
||||
private Path outFile;
|
||||
private Path errFile;
|
||||
|
||||
@Before
|
||||
public void setupTraceRmi() throws Throwable {
|
||||
traceRmi = addPlugin(tool, TraceRmiPlugin.class);
|
||||
|
||||
traceManager.setSaveTracesByDefault(false);
|
||||
|
||||
jshellPath = Paths.get(System.getProperty("java.home")).resolve("bin/jshell");
|
||||
outFile = Files.createTempFile("jshout", null);
|
||||
errFile = Files.createTempFile("jsherr", null);
|
||||
|
||||
FrontEndTool frontEndTool = env.getFrontEndTool();
|
||||
Plugin recoveryPlugin = frontEndTool.getManagedPlugins()
|
||||
.stream()
|
||||
.filter(p -> p instanceof RecoverySnapshotMgrPlugin)
|
||||
.findAny()
|
||||
.get();
|
||||
frontEndTool.removePlugins(List.of(recoveryPlugin));
|
||||
}
|
||||
|
||||
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 addrToStringForJshell(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 sockToStringForJshell(SocketAddress address) {
|
||||
if (address instanceof InetSocketAddress tcp) {
|
||||
return addrToStringForJshell(tcp.getAddress()) + ":" + tcp.getPort();
|
||||
}
|
||||
throw new AssertionError("Unhandled address type " + address);
|
||||
}
|
||||
|
||||
protected record JshellResult(boolean timedOut, int exitCode, String stdout, String stderr) {
|
||||
protected String handle() {
|
||||
if (stderr.contains("Error:") || (0 != exitCode)) {
|
||||
throw new JshellError(exitCode, stdout, stderr);
|
||||
}
|
||||
System.out.println("--stdout--");
|
||||
System.out.println(stdout);
|
||||
System.out.println("--stderr--");
|
||||
System.out.println(stderr);
|
||||
return stdout;
|
||||
}
|
||||
}
|
||||
|
||||
protected record ExecInJshell(Process jshell, AbstractJavaTraceRmiTest test,
|
||||
CompletableFuture<JshellResult> future) {}
|
||||
|
||||
private String parseClassPath() {
|
||||
String classPath = System.getProperty("java.class.path");
|
||||
String[] split = classPath.split(":");
|
||||
String newClassPath = "";
|
||||
for (String p : split) {
|
||||
File file = new File(p);
|
||||
if (file.exists()) {
|
||||
newClassPath += p + ":";
|
||||
}
|
||||
}
|
||||
return newClassPath;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource") // Do not close stdin
|
||||
protected ExecInJshell execInJshell(String script) throws IOException {
|
||||
String classPath = parseClassPath();
|
||||
ProcessBuilder pb = new ProcessBuilder(jshellPath.toString(), "--class-path=" + classPath);
|
||||
|
||||
// If commands come from file, jshell will quit after EOF.
|
||||
Msg.info(this, "outFile: " + outFile);
|
||||
Msg.info(this, "errFile: " + errFile);
|
||||
|
||||
ResourceFile rf = Application.getModuleDataFile("TestResources", "HelloWorld.class");
|
||||
pb.environment().put("OPT_TARGET_CLASSPATH", rf.getParentFile().getAbsolutePath());
|
||||
pb.environment().put("OPT_TARGET_CLASS", "HelloWorld");
|
||||
pb.environment().put("OPT_SUSPEND", "true");
|
||||
pb.environment().put("OPT_INCLUDE", "n");
|
||||
//pb.inheritIO();
|
||||
pb.redirectInput(ProcessBuilder.Redirect.PIPE);
|
||||
pb.redirectOutput(outFile.toFile());
|
||||
pb.redirectError(errFile.toFile());
|
||||
Process proc = pb.start();
|
||||
OutputStream stdin = proc.getOutputStream();
|
||||
stdin.write(script.getBytes());
|
||||
stdin.flush();
|
||||
//stdin.close();
|
||||
return new ExecInJshell(proc, this, CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
if (!proc.waitFor(TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
|
||||
Msg.error(this, "Timed out waiting for jshell");
|
||||
proc.destroyForcibly();
|
||||
proc.waitFor(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
return new JshellResult(true, -1, Files.readString(outFile),
|
||||
Files.readString(errFile));
|
||||
}
|
||||
Msg.info(this, "jshell exited with code " + proc.exitValue());
|
||||
return new JshellResult(false, proc.exitValue(), Files.readString(outFile),
|
||||
Files.readString(errFile));
|
||||
}
|
||||
catch (Exception e) {
|
||||
return ExceptionUtils.rethrow(e);
|
||||
}
|
||||
finally {
|
||||
proc.destroyForcibly();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static class JshellError extends RuntimeException {
|
||||
public final int exitCode;
|
||||
public final String stdout;
|
||||
public final String stderr;
|
||||
|
||||
public JshellError(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<JshellResult> result = execInJshell(script).future;
|
||||
return result.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).handle();
|
||||
}
|
||||
|
||||
protected record JshellAndConnection(ExecInJshell exec, TraceRmiConnection connection)
|
||||
implements AutoCloseable {
|
||||
private static BufferedReader reader = null;
|
||||
|
||||
protected RemoteMethod getMethod(String name) {
|
||||
return Objects.requireNonNull(connection.getMethods().get(name));
|
||||
}
|
||||
|
||||
public void execute(String cmd) {
|
||||
try {
|
||||
cmd += "\n";
|
||||
exec.jshell.getOutputStream().write(cmd.getBytes());
|
||||
exec.jshell.getOutputStream().flush();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public RemoteAsyncResult executeAsync(String cmd) {
|
||||
RemoteMethod execute = getMethod("execute");
|
||||
return execute.invokeAsync(Map.of("cmd", cmd));
|
||||
}
|
||||
|
||||
public List<String> executeCapture(String cmd) {
|
||||
try {
|
||||
if (reader == null) {
|
||||
reader = Files.newBufferedReader(exec.test.outFile);
|
||||
}
|
||||
if (cmd != null) {
|
||||
execute(cmd);
|
||||
}
|
||||
List<String> collect = waitForPass(() -> {
|
||||
List<String> list = reader.lines().collect(Collectors.toList());
|
||||
assertFalse(list.isEmpty());
|
||||
return list;
|
||||
});
|
||||
return collect;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
Msg.info(this, "Cleaning up jshell");
|
||||
execute("/exit");
|
||||
try {
|
||||
JshellResult r = exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
r.handle();
|
||||
connection.close();
|
||||
}
|
||||
finally {
|
||||
exec.jshell.destroyForcibly();
|
||||
}
|
||||
}
|
||||
|
||||
public void waitOnClosed() {
|
||||
waitForPass(() -> assertTrue(connection.isClosed()));
|
||||
}
|
||||
}
|
||||
|
||||
protected JshellAndConnection startAndConnectJshell(Function<String, String> scriptSupplier)
|
||||
throws Exception {
|
||||
TraceRmiAcceptor acceptor = traceRmi.acceptOne(null);
|
||||
ExecInJshell exec =
|
||||
execInJshell(scriptSupplier.apply(sockToStringForJshell(acceptor.getAddress())));
|
||||
acceptor.setTimeout(TIMEOUT_SECONDS * 1000);
|
||||
try {
|
||||
TraceRmiConnection connection = acceptor.accept();
|
||||
return new JshellAndConnection(exec, connection);
|
||||
}
|
||||
catch (SocketTimeoutException e) {
|
||||
exec.jshell.destroyForcibly();
|
||||
exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).handle();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected JshellAndConnection startAndConnectJshell() throws Exception {
|
||||
return startAndConnectJshell(addr -> """
|
||||
%s
|
||||
cmds.ghidraTraceConnect("%s");
|
||||
""".formatted(PREAMBLE, addr));
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
protected String runThrowError(Function<String, String> scriptSupplier)
|
||||
throws Exception {
|
||||
JshellAndConnection conn = startAndConnectJshell(scriptSupplier);
|
||||
JshellResult r = conn.exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
String stdout = r.handle();
|
||||
/**
|
||||
* We know at this point the process should be terminated. Depending on how cleanly that
|
||||
* happened, the socket may or may not be closed. Do not assert/wait for it to close. Just
|
||||
* clean up by closing our end
|
||||
*/
|
||||
conn.connection.close();
|
||||
assertFalse(stdout.contains("Error:"));
|
||||
return stdout;
|
||||
}
|
||||
|
||||
protected void waitStopped(String message, Long snap) {
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("VMs[]", Lifespan.at(snap)));
|
||||
waitForPass(() -> assertEquals(message, "STOPPED", tb.objValue(proc, 0, "_state")));
|
||||
waitTxDone();
|
||||
}
|
||||
|
||||
protected void waitRunning(String message, Long snap) {
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("VMs[]", Lifespan.at(snap)));
|
||||
waitForPass(() -> assertEquals(message, "RUNNING", tb.objValue(proc, 0, "_state")));
|
||||
waitTxDone();
|
||||
}
|
||||
|
||||
protected void waitAny(String message, Long snap) {
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("VMs[]", Lifespan.at(snap)));
|
||||
waitForPass(() -> assertNotNull(message, tb.objValue(proc, 0, "_state")));
|
||||
waitTxDone();
|
||||
}
|
||||
|
||||
protected String extractOutSection(String out, String head) {
|
||||
String[] split = out.split("\n");
|
||||
String xout = "";
|
||||
for (String s : split) {
|
||||
if (!s.startsWith("jshell>") && !s.equals("")) {
|
||||
if (s.startsWith("INFO") || s.startsWith("ERROR") || s.startsWith("WARN")) {
|
||||
if (s.indexOf("(") > 0) {
|
||||
s = s.substring(0, s.lastIndexOf("(")).trim();
|
||||
}
|
||||
}
|
||||
xout += s + "\n";
|
||||
}
|
||||
}
|
||||
return xout.split(head)[1].split("---")[0].replace("jshell>", "").trim();
|
||||
}
|
||||
|
||||
record MemDump(long address, byte[] data) {}
|
||||
|
||||
protected MemDump parseHexDump(String dump) throws IOException {
|
||||
// First, get the address. Assume contiguous, so only need top line.
|
||||
List<String> lines = List.of(dump.split("\n"));
|
||||
String bytes = lines.get(0);
|
||||
bytes = bytes.substring(bytes.indexOf("{") + 1, bytes.indexOf("}"));
|
||||
List<String> toks = List.of(bytes.split(",\\s+"));
|
||||
String addrstr = lines.get(1);
|
||||
long address = Long.parseLong(addrstr, 16);
|
||||
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
byte[] lineData = new byte[toks.size()];
|
||||
int i = 0;
|
||||
for (String t : toks) {
|
||||
lineData[i++] = Byte.parseByte(t.trim());
|
||||
}
|
||||
buf.write(lineData);
|
||||
return new MemDump(address, buf.toByteArray());
|
||||
}
|
||||
|
||||
protected ManagedDomainObject openDomainObject(String path) throws Exception {
|
||||
DomainFile dfx = waitForPass(() -> {
|
||||
DomainFile df = env.getProject().getProjectData().getFile(path);
|
||||
assertNotNull(df);
|
||||
return df;
|
||||
}, TIMEOUT_SECONDS * 1000, 500);
|
||||
return new ManagedDomainObject(dfx, 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 void waitTxDone() {
|
||||
waitForCondition(() -> tb.trace.getCurrentTransactionInfo() == null,
|
||||
() -> "Error waiting for transaction to finish");
|
||||
}
|
||||
|
||||
public static <T> T waitForPass(Supplier<T> supplier) {
|
||||
var locals = new Object() {
|
||||
AssertionError lastError;
|
||||
T value;
|
||||
};
|
||||
waitForCondition(() -> {
|
||||
try {
|
||||
locals.value = supplier.get();
|
||||
return true;
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
locals.lastError = e;
|
||||
return false;
|
||||
}
|
||||
}, () -> locals.lastError.getMessage());
|
||||
return locals.value;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static void waitForPass(Runnable runnable, long timeoutMs, long retryDelayMs) {
|
||||
long start = System.currentTimeMillis();
|
||||
AssertionError lastError = null;
|
||||
while (System.currentTimeMillis() - start < timeoutMs) {
|
||||
try {
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
lastError = e;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(retryDelayMs);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// Retry sooner, I guess.
|
||||
}
|
||||
}
|
||||
if (lastError == null) {
|
||||
throw new AssertionError("Timed out before first try?");
|
||||
}
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
public static <T> T waitForPass(Supplier<T> supplier, long timeoutMs, long retryDelayMs) {
|
||||
var locals = new Object() {
|
||||
T value;
|
||||
};
|
||||
waitForPass(() -> {
|
||||
locals.value = supplier.get();
|
||||
}, timeoutMs, retryDelayMs);
|
||||
return locals.value;
|
||||
}
|
||||
|
||||
protected long getMaxSnap() {
|
||||
Long maxSnap = tb.trace.getTimeManager().getMaxSnap();
|
||||
return maxSnap == null ? 0 : maxSnap;
|
||||
}
|
||||
|
||||
protected TraceObject waitForObject(String path) {
|
||||
return waitForPass(() -> {
|
||||
TraceObject obj = tb.objAny(path, Lifespan.at(getMaxSnap()));
|
||||
assertNotNull("Object " + path + " never appeared.", obj);
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
protected List<TraceObjectValue> getValues(String path) {
|
||||
return tb.trace.getObjectManager()
|
||||
.getValuePaths(Lifespan.at(getMaxSnap()), PathPredicates.parse(path))
|
||||
.map(p -> p.getLastEntry())
|
||||
.sorted(Comparator.comparing(TraceObjectValue::getEntryKey))
|
||||
.toList();
|
||||
}
|
||||
|
||||
protected List<TraceObjectValue> waitForValuesPass(String path,
|
||||
Consumer<List<TraceObjectValue>> asserter) {
|
||||
return waitForPass(() -> {
|
||||
List<TraceObjectValue> vals = getValues(path);
|
||||
asserter.accept(vals);
|
||||
return vals;
|
||||
});
|
||||
}
|
||||
|
||||
protected List<TraceObjectValue> waitForValues(String path) {
|
||||
return waitForValuesPass(path, vals -> assertFalse(vals.isEmpty()));
|
||||
}
|
||||
|
||||
protected Address getPC() {
|
||||
return (Address) Unique
|
||||
.assertOne(tb.objValues(getMaxSnap(), "VMs[].Threads[main].Stack[0].PC"));
|
||||
}
|
||||
|
||||
protected Address waitForPC(Consumer<Address> asserter) {
|
||||
return waitForPass(() -> {
|
||||
Address pc = getPC();
|
||||
asserter.accept(pc);
|
||||
return pc;
|
||||
});
|
||||
}
|
||||
|
||||
public static void assertMatches(String expectedPattern, TraceObject object) {
|
||||
assertTrue("Expected matches " + expectedPattern + " but was " +
|
||||
object.getCanonicalPath().toString(),
|
||||
PathPredicates.parse(expectedPattern).matches(object.getCanonicalPath().getKeyList()));
|
||||
}
|
||||
|
||||
protected void waitForLocation(String clsName, String methodName, long codeIndex) {
|
||||
try {
|
||||
waitForValuesPass("VMs[].Threads[main].Stack[0].Location.Method", methods -> {
|
||||
assertMatches("VMs[].Classes[" + clsName + "].Methods[" + methodName + "]",
|
||||
Unique.assertOne(methods).getChild());
|
||||
});
|
||||
waitForValuesPass("VMs[].Threads[main].Stack[0].Location.Index", indices -> {
|
||||
assertEquals(codeIndex, Unique.assertOne(indices).getValue());
|
||||
});
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
TraceObjectValue locVal = Unique.assertAtMostOne(
|
||||
getValues("VMs[].Threads[main].Stack[0].Location"));
|
||||
if (locVal == null) {
|
||||
throw new AssertionError("Wrong location. Expected %s.%s:%d but was null"
|
||||
.formatted(clsName, methodName, codeIndex));
|
||||
}
|
||||
TraceObject loc = locVal.getChild();
|
||||
long snap = getMaxSnap();
|
||||
throw new AssertionError("Wrong location. Expected %s.%s:%d but was %s:%d"
|
||||
.formatted(clsName, methodName, codeIndex,
|
||||
loc.getAttribute(snap, "Method").getValue(),
|
||||
loc.getAttribute(snap, "Index").getValue()));
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,381 @@
|
|||
/* ###
|
||||
* 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.java.rmi;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.Unique;
|
||||
import ghidra.app.plugin.core.debug.utils.ManagedDomainObject;
|
||||
import ghidra.debug.api.tracermi.RemoteMethod;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
|
||||
public class JavaHooksTest extends AbstractJavaTraceRmiTest {
|
||||
|
||||
@Test
|
||||
public void testOnStep() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshMethod = conn.getMethod("refresh_method");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
conn.execute("VirtualMachine vm = manager.getCurrentVM();");
|
||||
conn.execute("ThreadReference thread = manager.getCurrentThread();");
|
||||
conn.execute("EventRequestManager mgr = vm.eventRequestManager();");
|
||||
conn.execute(
|
||||
"StepRequest req = mgr.createStepRequest(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO);");
|
||||
conn.execute("req.enable();");
|
||||
conn.execute("vm.resume();");
|
||||
|
||||
txPut(conn, "Threads");
|
||||
waitForObject("VMs[].Threads[main]");
|
||||
txPut(conn, "Frames");
|
||||
|
||||
List<TraceObjectValue> pcs =
|
||||
waitForValues("VMs[].Threads[main].Stack[].Location.Method");
|
||||
assertTrue(pcs.get(0).getValue().toString().contains("<init>"));
|
||||
|
||||
TraceObject checkName = pcs.get(0).getChild();
|
||||
refreshMethod.invoke(Map.of("method", checkName));
|
||||
waitTxDone();
|
||||
|
||||
AddressRange range = Unique
|
||||
.assertOne(
|
||||
waitForValues(checkName.getCanonicalPath().extend("Range").toString()))
|
||||
.castValue();
|
||||
waitForPC(start -> start.equals(range.getMinAddress()));
|
||||
|
||||
waitForValuesPass("VMs[]._display", vms -> assertContainsString("Step",
|
||||
vms.get(0).getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnMethodEntry() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod breakOnEnter = conn.getMethod("break_enter_thread");
|
||||
RemoteMethod refreshMethod = conn.getMethod("refresh_method");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
breakOnEnter.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
txPut(conn, "Frames");
|
||||
|
||||
List<TraceObjectValue> eventVals = waitForValues("VMs[].Events[]");
|
||||
assertEquals(2, eventVals.size());
|
||||
assertTrue(eventVals.get(0).getEntryKey().contains("method entry request"));
|
||||
|
||||
List<TraceObjectValue> pcs =
|
||||
waitForValues("VMs[].Threads[main].Stack[].Location.Method");
|
||||
assertTrue(pcs.get(0).getValue().toString().contains("checkName"));
|
||||
assertTrue(pcs.get(1).getValue().toString().contains("<init>"));
|
||||
|
||||
TraceObject checkName = pcs.get(0).getChild();
|
||||
refreshMethod.invoke(Map.of("method", checkName));
|
||||
waitTxDone();
|
||||
|
||||
AddressRange range = Unique
|
||||
.assertOne(
|
||||
waitForValues(checkName.getCanonicalPath().extend("Range").toString()))
|
||||
.castValue();
|
||||
waitForPC(start -> start.equals(range.getMinAddress()));
|
||||
|
||||
waitForValuesPass("VMs[]._display", vms -> assertContainsString("MethodEntry",
|
||||
vms.get(0).getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnMethodExit() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod breakOnExit = conn.getMethod("break_exit_thread");
|
||||
RemoteMethod refreshMethod = conn.getMethod("refresh_method");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
breakOnExit.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
txPut(conn, "Frames");
|
||||
|
||||
List<TraceObjectValue> eventVals = waitForValues("VMs[].Events[]");
|
||||
assertEquals(2, eventVals.size());
|
||||
assertTrue(eventVals.get(0).getEntryKey().contains("method exit request"));
|
||||
|
||||
List<TraceObjectValue> pcs =
|
||||
waitForValues("VMs[].Threads[main].Stack[].Location.Method");
|
||||
assertTrue(pcs.get(0).getValue().toString().contains("checkName"));
|
||||
assertTrue(pcs.get(1).getValue().toString().contains("<init>"));
|
||||
|
||||
TraceObject checkName = pcs.get(0).getChild();
|
||||
refreshMethod.invoke(Map.of("method", checkName));
|
||||
waitTxDone();
|
||||
|
||||
AddressRange range = Unique
|
||||
.assertOne(
|
||||
waitForValues(checkName.getCanonicalPath().extend("Range").toString()))
|
||||
.castValue();
|
||||
waitForPC(start -> start.equals(range.getMaxAddress()));
|
||||
|
||||
waitForValuesPass("VMs[]._display", vms -> assertContainsString("MethodExit",
|
||||
vms.get(0).getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClassLoad() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod breakLoad = conn.getMethod("break_load_container");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
TraceObject events = waitForObject("VMs[].Events");
|
||||
breakLoad.invoke(Map.of("container", events));
|
||||
waitTxDone();
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> eventVals = waitForValues("VMs[].Events[]");
|
||||
assertEquals(2, eventVals.size());
|
||||
assertTrue(eventVals.get(0).getEntryKey().contains("class prepare request"));
|
||||
|
||||
waitForValuesPass("VMs[]._display", vms -> assertContainsString("ClassPrepare",
|
||||
vms.get(0).getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnThreadStart() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod breakStart = conn.getMethod("break_started_container");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
TraceObject events = waitForObject("VMs[].Events");
|
||||
breakStart.invoke(Map.of("container", events));
|
||||
waitTxDone();
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> eventVals = waitForValues("VMs[].Events[]");
|
||||
assertTrue(eventVals.get(0).getEntryKey().contains("thread start request"));
|
||||
|
||||
waitForValuesPass("VMs[]._display", vms -> assertContainsString("ThreadStart",
|
||||
vms.get(0).getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnThreadDeath() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod breakDeath = conn.getMethod("break_death_container");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
TraceObject events = waitForObject("VMs[].Events");
|
||||
breakDeath.invoke(Map.of("container", events));
|
||||
waitTxDone();
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> eventVals = waitForValues("VMs[].Events[]");
|
||||
assertEquals(2, eventVals.size());
|
||||
assertTrue(eventVals.get(1).getEntryKey().contains("thread death request"));
|
||||
|
||||
waitForValuesPass("VMs[]._display", vms -> assertContainsString("ThreadDeath",
|
||||
vms.get(0).getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnBreakpoint() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod refreshMethod = conn.getMethod("refresh_method");
|
||||
RemoteMethod refreshLocations = conn.getMethod("refresh_locations");
|
||||
RemoteMethod breakOnExecute = conn.getMethod("break_location");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
TraceObject access =
|
||||
waitForObject("VMs[].Classes[java.lang.Thread].Methods[checkAccess]");
|
||||
refreshMethod.invoke(Map.of("method", access));
|
||||
waitTxDone();
|
||||
TraceObject locCont =
|
||||
waitForObject("VMs[].Classes[java.lang.Thread].Methods[checkAccess].Locations");
|
||||
refreshLocations.invoke(Map.of("container", locCont));
|
||||
waitTxDone();
|
||||
List<TraceObjectValue> locations = waitForValues(
|
||||
"VMs[].Classes[java.lang.Thread].Methods[checkAccess].Locations[]");
|
||||
for (TraceObjectValue loc : locations) {
|
||||
breakOnExecute.invoke(Map.of("location", loc.getChild()));
|
||||
waitTxDone();
|
||||
}
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> brkVals = waitForValues("VMs[].Breakpoints[]");
|
||||
assertEquals(4, brkVals.size());
|
||||
|
||||
txPut(conn, "Frames");
|
||||
waitForValuesPass("VMs[].Threads[main].Stack[0]._display",
|
||||
pcs -> assertContainsString("checkAccess", pcs.get(0).getValue().toString()));
|
||||
|
||||
waitForValuesPass("VMs[]._display", vms -> assertContainsString("Breakpoint",
|
||||
vms.get(0).getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnAccessWatchpoint() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod refreshFields = conn.getMethod("refresh_canonical_fields");
|
||||
RemoteMethod breakOnAccess = conn.getMethod("break_access");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
TraceObject fieldCont = waitForObject("VMs[].Classes[java.lang.Thread].Fields");
|
||||
refreshFields.invoke(Map.of("container", fieldCont));
|
||||
waitTxDone();
|
||||
List<TraceObjectValue> fields =
|
||||
waitForValues("VMs[].Classes[java.lang.Thread].Fields[NEW_THREAD_BINDINGS]");
|
||||
for (TraceObjectValue f : fields) {
|
||||
breakOnAccess.invoke(Map.of("field", f.getChild()));
|
||||
waitTxDone();
|
||||
}
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> brkVals = waitForValues("VMs[].Breakpoints[]");
|
||||
assertEquals(1, brkVals.size());
|
||||
|
||||
txPut(conn, "Frames");
|
||||
List<TraceObjectValue> pcs = waitForValues("VMs[].Threads[main].Stack[0]._display");
|
||||
assertTrue(pcs.get(0).getValue().toString().contains("<init>"));
|
||||
|
||||
waitForValuesPass("VMs[]._display", vms -> assertContainsString("AccessWatchpoint",
|
||||
vms.get(0).getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void start(JshellAndConnection conn, String obj) {
|
||||
if (obj != null) {
|
||||
conn.execute("cmds.ghidraTraceStart(\"" + obj + "\");");
|
||||
}
|
||||
else {
|
||||
conn.execute("mds.ghidraTraceStart();");
|
||||
}
|
||||
conn.execute("cmds.ghidraTraceCreate(System.getenv());");
|
||||
conn.execute("cmds.ghidraTraceTxStart(\"Create snapshot\")");
|
||||
conn.execute("cmds.ghidraTraceNewSnap(\"Scripted snapshot\")");
|
||||
conn.execute("cmds.ghidraTraceTxCommit()");
|
||||
}
|
||||
|
||||
private void txPut(JshellAndConnection conn, String obj) {
|
||||
conn.execute("cmds.ghidraTraceTxStart(\"Tx\");");
|
||||
conn.execute("cmds.ghidraTracePut" + obj + "();");
|
||||
conn.execute("cmds.ghidraTraceTxCommit();");
|
||||
waitTxDone();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,806 @@
|
|||
/* ###
|
||||
* 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.java.rmi;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.Unique;
|
||||
import ghidra.app.plugin.core.debug.utils.ManagedDomainObject;
|
||||
import ghidra.debug.api.tracermi.RemoteMethod;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
|
||||
public class JavaMethodsTest extends AbstractJavaTraceRmiTest {
|
||||
|
||||
/**
|
||||
* Because we control the target artifact, we know the steps precisely. NOTE: This must be the
|
||||
* same at least for step_into and step_over, otherwise, we can't know for sure step_over
|
||||
* actually behaves any differently than step_into. By matching with step_into, we can assure
|
||||
* ourselves that step_over actually encountered an invoke* bytecode instruction, because if it
|
||||
* didn't then step_into ought to fail.
|
||||
*/
|
||||
public void do2Steps(JshellAndConnection conn, RemoteMethod step, TraceObject thread) {
|
||||
step.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
txPut(conn, "Frames");
|
||||
waitForLocation("HelloWorld", "main", 3);
|
||||
|
||||
step.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
txPut(conn, "Frames");
|
||||
waitForLocation("HelloWorld", "main", 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEvaluate() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
conn.executeCapture(null);
|
||||
List<String> res = conn.executeCapture("3+4*2;");
|
||||
conn.execute("/exit");
|
||||
assertTrue(res.get(0).contains("11"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
// Just confirm it's present
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshEvents() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshEvents = conn.getMethod("refresh_events");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
TraceObject events = waitForObject("VMs[].Events");
|
||||
refreshEvents.invoke(Map.of("container", events));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> eventVals = waitForValues("VMs[].Events[]");
|
||||
assertEquals(1, eventVals.size());
|
||||
assertTrue(eventVals.get(0).getEntryKey().contains("step request"));
|
||||
|
||||
conn.execute("VirtualMachine vm = manager.getCurrentVM(); ");
|
||||
conn.execute(
|
||||
"MethodEntryRequest brkReq = vm.eventRequestManager().createMethodEntryRequest();");
|
||||
|
||||
eventVals = waitForPass(() -> {
|
||||
refreshEvents.invoke(Map.of("container", events));
|
||||
List<TraceObjectValue> evs = waitForValues("VMs[].Events[]");
|
||||
assertEquals(2, evs.size());
|
||||
return evs;
|
||||
});
|
||||
assertTrue(eventVals.get(0).getEntryKey().contains("method entry request"));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshBreakpoints() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshBreakpoints = conn.getMethod("refresh_breakpoints");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
conn.execute("VirtualMachine vm = manager.getCurrentVM();");
|
||||
conn.execute("Location loc = manager.getCurrentLocation();");
|
||||
conn.execute(
|
||||
"BreakpointRequest brkReq = vm.eventRequestManager().createBreakpointRequest(loc);");
|
||||
|
||||
TraceObject brkSet = waitForObject("VMs[].Breakpoints");
|
||||
List<TraceObjectValue> brks = waitForPass(() -> {
|
||||
refreshBreakpoints.invoke(Map.of("container", brkSet));
|
||||
List<TraceObjectValue> d = waitForValues("VMs[].Breakpoints[]");
|
||||
assertEquals(1, d.size());
|
||||
return d;
|
||||
});
|
||||
assertTrue(brks.get(0).getEntryKey().contains("breakpoint request"));
|
||||
|
||||
conn.execute(
|
||||
"String path = \"VMs[OpenJDK 64-Bit Server VM].Classes[java.lang.Thread]\";");
|
||||
conn.execute(
|
||||
"ReferenceType reftype = (ReferenceType) jdiManager.objForPath(path);");
|
||||
conn.execute("Field field = reftype.fieldByName(\"tid\");");
|
||||
conn.execute(
|
||||
"AccessWatchpointRequest brkReq = vm.eventRequestManager().createAccessWatchpointRequest(field);");
|
||||
|
||||
TraceObject brkSet2 = waitForObject("VMs[].Breakpoints");
|
||||
brks = waitForPass(() -> {
|
||||
refreshBreakpoints.invoke(Map.of("container", brkSet2));
|
||||
List<TraceObjectValue> d = waitForValues("VMs[].Breakpoints[]");
|
||||
assertEquals(2, d.size());
|
||||
return d;
|
||||
});
|
||||
assertTrue(brks.get(0).getEntryKey().contains("access watchpoint request"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshVMs() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshVM = conn.getMethod("refresh_vm");
|
||||
RemoteMethod refreshProcess = conn.getMethod("refresh_process");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "VMs");
|
||||
|
||||
TraceObject vm = waitForObject("VMs[]");
|
||||
List<TraceObjectValue> children = waitForValues("VMs[].");
|
||||
int sz = children.size();
|
||||
refreshVM.invoke(Map.of("vm", vm));
|
||||
children = waitForValues("VMs[].");
|
||||
assertTrue(children.size() >= sz);
|
||||
List<TraceObjectValue> eventThread = waitForValues("VMs[]._event_thread");
|
||||
assertTrue(eventThread.get(0).getValue().toString().contains("main"));
|
||||
|
||||
TraceObject proc = waitForObject("VMs[].Processes[]");
|
||||
refreshProcess.invoke(Map.of("process", proc));
|
||||
children = waitForValues("VMs[].Processes[].");
|
||||
assertTrue(children.get(0).getEntryKey().contains("Alive"));
|
||||
children = waitForValues("VMs[].Processes[].CommandLine");
|
||||
assertTrue(((String) children.get(0).getValue()).contains("HelloWorld"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshThreads() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshThreads = conn.getMethod("refresh_threads");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "VMs");
|
||||
|
||||
TraceObject threads = waitForObject("VMs[].Threads");
|
||||
refreshThreads.invoke(Map.of("container", threads));
|
||||
List<TraceObjectValue> children = waitForValues("VMs[].Threads[]");
|
||||
assertEquals(4, children.size());
|
||||
assertTrue(children.get(children.size() - 1).getEntryKey().contains("main"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshStack() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod brkEnter = conn.getMethod("break_enter_container");
|
||||
RemoteMethod setClsFilt = conn.getMethod("set_class_filter");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod refreshStack = conn.getMethod("refresh_stack");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject events = waitForObject("VMs[].Events");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
brkEnter.invoke(Map.of("container", events));
|
||||
TraceObject evtEnter = waitForObject("VMs[].Events[]");
|
||||
setClsFilt.invoke(Map.of(
|
||||
"event", evtEnter,
|
||||
"filter", "HelloWorld*",
|
||||
"exclude", false));
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
TraceObject stack = waitForObject("VMs[].Threads[main].Stack");
|
||||
refreshStack.invoke(Map.of("stack", stack));
|
||||
waitTxDone();
|
||||
waitForValuesPass("VMs[].Threads[main].Stack[]",
|
||||
frames -> assertEquals(1, frames.size()));
|
||||
|
||||
// Because main is static, there is no preceding call to <init>
|
||||
// This class has no <clinit>, so we get main first!
|
||||
waitForLocation("HelloWorld", "main", 0);
|
||||
Address start = getPC();
|
||||
|
||||
do2Steps(conn, stepInto, thread);
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
refreshStack.invoke(Map.of("stack", stack));
|
||||
waitTxDone();
|
||||
waitForValuesPass("VMs[].Threads[main].Stack[]", frames -> {
|
||||
assertEquals(2, frames.size());
|
||||
});
|
||||
waitForValuesPass("VMs[].Threads[main].Stack[].PC", pcs -> {
|
||||
assertEquals(2, pcs.size());
|
||||
assertEquals(start.add(5), pcs.get(1).getValue());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshRegisters() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshRegisters = conn.getMethod("refresh_registers");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
}
|
||||
|
||||
TraceObject regContainer = waitForObject("VMs[].Threads[main].Stack[0].Registers");
|
||||
refreshRegisters.invoke(Map.of("container", regContainer));
|
||||
List<TraceObjectValue> registers =
|
||||
waitForValues("VMs[].Threads[main].Stack[0].Registers[]");
|
||||
assertEquals(2, registers.size());
|
||||
assertTrue(registers.get(0).getEntryKey().contains("PC"));
|
||||
assertTrue(registers.get(1).getEntryKey().contains("return_address"));
|
||||
|
||||
waitForObject("VMs[].Threads[main].Stack[1].Registers");
|
||||
refreshRegisters.invoke(Map.of("container", regContainer));
|
||||
registers = waitForValues("VMs[].Threads[main].Stack[1].Registers[]");
|
||||
assertEquals(1, registers.size());
|
||||
assertTrue(registers.get(0).getEntryKey().contains("PC"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@Test Too slow for testing.
|
||||
public void testRefreshMemory() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshMemory = conn.getMethod("refresh_memory");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> children = waitForValues("VMs[].Memory[]");
|
||||
assertTrue(children.size() < 100);
|
||||
|
||||
TraceObject memory = waitForObject("VMs[].Memory");
|
||||
refreshMemory.invoke(Map.of("memory", memory));
|
||||
children = waitForValues("VMs[].Memory[]");
|
||||
assertTrue(children.size() > 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@Test Too slow for testing.
|
||||
public void testRefreshClasses() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshClasses = conn.getMethod("refresh_reference_types");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> children = waitForValues("VMs[].Classes[]");
|
||||
assertEquals(1, children.size());
|
||||
|
||||
TraceObject classes = waitForObject("VMs[].Classes");
|
||||
refreshClasses.invoke(Map.of("container", classes));
|
||||
children = waitForValues("VMs[].Classes[]");
|
||||
assertTrue(children.size() > 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshModules() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshModules = conn.getMethod("refresh_modules");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> children = waitForValues("VMs[].ModuleRefs[]");
|
||||
assertTrue(children.size() < 100);
|
||||
int sz = children.size();
|
||||
|
||||
TraceObject modules = waitForObject("VMs[].ModuleRefs");
|
||||
refreshModules.invoke(Map.of("container", modules));
|
||||
children = waitForValues("VMs[].ModuleRefs[]");
|
||||
assertEquals(sz, children.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivate() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshThreads = conn.getMethod("refresh_threads");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "VMs");
|
||||
|
||||
TraceObject threads = waitForObject("VMs[].Threads");
|
||||
refreshThreads.invoke(Map.of("container", threads));
|
||||
List<TraceObjectValue> children = waitForValues("VMs[].Threads[]");
|
||||
assertEquals(4, children.size());
|
||||
|
||||
waitForPass(() -> {
|
||||
TraceObject obj = traceManager.getCurrentObject();
|
||||
assertFalse(obj == null);
|
||||
assertContainsString("[main]", obj.getCanonicalPath().toString());
|
||||
});
|
||||
|
||||
conn.execute(
|
||||
"cmds.ghidraTraceActivate(\"VMs[OpenJDK 64-Bit Server VM].Threads[Finalizer]\")");
|
||||
waitTxDone();
|
||||
Thread.sleep(1000); // Why?
|
||||
|
||||
waitForPass(() -> {
|
||||
TraceObject obj = traceManager.getCurrentObject();
|
||||
assertFalse(obj == null);
|
||||
assertContainsString("[Finalizer]", obj.getCanonicalPath().toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@Test Unclear how to test
|
||||
public void testKill() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod kill = conn.getMethod("kill");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "VMs");
|
||||
|
||||
TraceObject vm = waitForObject("VMs[]");
|
||||
kill.invoke(Map.of("vm", vm));
|
||||
waitTxDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Still a race condition between the last call to putFrames and the final assert")
|
||||
public void testStepInto() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
/**
|
||||
* NOTE: If using break_load_container, you'd need to follow with set_source_filter
|
||||
* using "HelloWorld*". I still haven't looked far enough to figure out what the exact
|
||||
* match should be. (It's not "HelloWorld", but maybe it's "HelloWorld.class". I've just
|
||||
* switched over to break_enter_container, instead.
|
||||
*/
|
||||
|
||||
RemoteMethod brkEnter = conn.getMethod("break_enter_container");
|
||||
RemoteMethod setClsFilt = conn.getMethod("set_class_filter");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject events = waitForObject("VMs[].Events");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
brkEnter.invoke(Map.of("container", events));
|
||||
TraceObject evtEnter = waitForObject("VMs[].Events[]");
|
||||
setClsFilt.invoke(Map.of(
|
||||
"event", evtEnter,
|
||||
"filter", "HelloWorld*",
|
||||
"exclude", false));
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
txPut(conn, "Frames");
|
||||
// Because main is static, there is no preceding call to <init>
|
||||
// This class has no <clinit>, so we get main first!
|
||||
waitForLocation("HelloWorld", "main", 0);
|
||||
Address start = waitForPC(pc -> assertNotEquals(0, pc.getOffset()));
|
||||
|
||||
do2Steps(conn, stepInto, thread);
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
txPut(conn, "Frames");
|
||||
|
||||
waitForLocation("java.io.PrintStream", "println", 0);
|
||||
waitForValuesPass("VMs[].Threads[main].Stack[0].Registers[return_address]",
|
||||
rets -> {
|
||||
long ret = Long.parseLong(Unique.assertOne(rets).castValue(), 16);
|
||||
assertEquals(start.getOffset() + 8, ret);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Still a race condition between the last call to putFrames and the final assert")
|
||||
public void testStepOver() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod brkEnter = conn.getMethod("break_enter_container");
|
||||
RemoteMethod setClsFilt = conn.getMethod("set_class_filter");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod stepOver = conn.getMethod("step_over");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject events = waitForObject("VMs[].Events");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
brkEnter.invoke(Map.of("container", events));
|
||||
TraceObject evtEnter = waitForObject("VMs[].Events[]");
|
||||
setClsFilt.invoke(Map.of(
|
||||
"event", evtEnter,
|
||||
"filter", "HelloWorld*",
|
||||
"exclude", false));
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
txPut(conn, "Frames");
|
||||
waitForLocation("HelloWorld", "main", 0);
|
||||
Address start = waitForPC(pc -> assertNotEquals(0, pc.getOffset()));
|
||||
|
||||
do2Steps(conn, stepOver, thread);
|
||||
|
||||
stepOver.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
txPut(conn, "Frames");
|
||||
|
||||
waitForLocation("HelloWorld", "main", 8);
|
||||
waitForPC(pc -> assertEquals(start.add(8), pc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Still a race condition between the last call to putFrames and the final assert")
|
||||
public void testStepOut() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod brkEnter = conn.getMethod("break_enter_container");
|
||||
RemoteMethod setClsFilt = conn.getMethod("set_class_filter");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod stepOut = conn.getMethod("step_out");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject events = waitForObject("VMs[].Events");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
brkEnter.invoke(Map.of("container", events));
|
||||
TraceObject evtEnter = waitForObject("VMs[].Events[]");
|
||||
setClsFilt.invoke(Map.of(
|
||||
"event", evtEnter,
|
||||
"filter", "HelloWorld*",
|
||||
"exclude", false));
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
txPut(conn, "Frames");
|
||||
// Because main is static, there is no preceding call to <init>
|
||||
// This class has no <clinit>, so we get main first!
|
||||
waitForLocation("HelloWorld", "main", 0);
|
||||
Address start = waitForPC(pc -> assertNotEquals(0, pc.getOffset()));
|
||||
|
||||
do2Steps(conn, stepInto, thread);
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
txPut(conn, "Frames");
|
||||
waitForLocation("java.io.PrintStream", "println", 0);
|
||||
|
||||
stepOut.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
waitForLocation("HelloWorld", "main", 8);
|
||||
waitForPC(pc -> assertEquals(start.add(8), pc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreakByEvent() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod breakOnEnter = conn.getMethod("break_enter_thread");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
breakOnEnter.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> eventVals = waitForValues("VMs[].Events[]");
|
||||
assertEquals(2, eventVals.size());
|
||||
assertTrue(eventVals.get(0).getEntryKey().contains("method entry request"));
|
||||
|
||||
List<TraceObjectValue> pcs =
|
||||
waitForValues("VMs[].Threads[main].Stack[].Location.Method");
|
||||
assertTrue(pcs.get(0).getValue().toString().contains("checkName"));
|
||||
assertTrue(pcs.get(1).getValue().toString().contains("<init>"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreakByLocation() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod refreshMethod = conn.getMethod("refresh_method");
|
||||
RemoteMethod refreshLocations = conn.getMethod("refresh_locations");
|
||||
RemoteMethod breakOnExecute = conn.getMethod("break_location");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
TraceObject access =
|
||||
waitForObject("VMs[].Classes[java.lang.Thread].Methods[checkAccess]");
|
||||
refreshMethod.invoke(Map.of("method", access));
|
||||
waitTxDone();
|
||||
TraceObject locCont =
|
||||
waitForObject("VMs[].Classes[java.lang.Thread].Methods[checkAccess].Locations");
|
||||
refreshLocations.invoke(Map.of("container", locCont));
|
||||
waitTxDone();
|
||||
List<TraceObjectValue> locations = waitForValues(
|
||||
"VMs[].Classes[java.lang.Thread].Methods[checkAccess].Locations[]");
|
||||
for (TraceObjectValue loc : locations) {
|
||||
breakOnExecute.invoke(Map.of("location", loc.getChild()));
|
||||
waitTxDone();
|
||||
}
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> brkVals = waitForValues("VMs[].Breakpoints[]");
|
||||
assertEquals(4, brkVals.size());
|
||||
//assertTrue(eventVals.get(0).getEntryKey().contains("method entry request"));
|
||||
|
||||
txPut(conn, "Frames");
|
||||
waitForValuesPass("VMs[].Threads[main].Stack[0]._display",
|
||||
pcs -> assertContainsString("checkAccess", pcs.get(0).getValue().toString()));
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreakOnAccess() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
RemoteMethod resume = conn.getMethod("resume_thread");
|
||||
RemoteMethod refreshFields = conn.getMethod("refresh_canonical_fields");
|
||||
RemoteMethod breakOnAccess = conn.getMethod("break_access");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
TraceObject fieldCont = waitForObject("VMs[].Classes[java.lang.Thread].Fields");
|
||||
refreshFields.invoke(Map.of("container", fieldCont));
|
||||
waitTxDone();
|
||||
List<TraceObjectValue> fields =
|
||||
waitForValues("VMs[].Classes[java.lang.Thread].Fields[NEW_THREAD_BINDINGS]");
|
||||
for (TraceObjectValue f : fields) {
|
||||
breakOnAccess.invoke(Map.of("field", f.getChild()));
|
||||
waitTxDone();
|
||||
}
|
||||
|
||||
resume.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
List<TraceObjectValue> brkVals = waitForValues("VMs[].Breakpoints[]");
|
||||
assertEquals(1, brkVals.size());
|
||||
//assertTrue(eventVals.get(0).getEntryKey().contains("method entry request"));
|
||||
|
||||
txPut(conn, "Frames");
|
||||
List<TraceObjectValue> pcs = waitForValues("VMs[].Threads[main].Stack[0]._display");
|
||||
assertTrue(pcs.get(0).getValue().toString().contains("<init>"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleBreakpoint() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshBreakpoints = conn.getMethod("refresh_breakpoints");
|
||||
RemoteMethod toggleBreakpoint = conn.getMethod("toggle_breakpoint");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
conn.execute("VirtualMachine vm = manager.getCurrentVM();");
|
||||
conn.execute("Location loc = manager.getCurrentLocation();");
|
||||
conn.execute(
|
||||
"BreakpointRequest brkReq = vm.eventRequestManager().createBreakpointRequest(loc);");
|
||||
|
||||
TraceObject brkPts = waitForObject("VMs[].Breakpoints");
|
||||
List<TraceObjectValue> descs = waitForPass(() -> {
|
||||
refreshBreakpoints.invoke(Map.of("container", brkPts));
|
||||
List<TraceObjectValue> d = waitForValues("VMs[].Breakpoints[]._display");
|
||||
assertEquals(1, d.size());
|
||||
return d;
|
||||
});
|
||||
|
||||
assertTrue(descs.get(0).getValue().toString().contains("breakpoint request"));
|
||||
assertTrue(descs.get(0).getValue().toString().contains("disabled"));
|
||||
|
||||
TraceObject brk = waitForObject("VMs[].Breakpoints[]");
|
||||
toggleBreakpoint.invoke(Map.of("breakpoint", brk));
|
||||
|
||||
refreshBreakpoints.invoke(Map.of("container", brkPts));
|
||||
descs = waitForValues("VMs[].Breakpoints[]._display");
|
||||
assertTrue(descs.get(0).getValue().toString().contains("enabled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteBreakpoint() throws Exception {
|
||||
try (JshellAndConnection conn = startAndConnectJshell()) {
|
||||
start(conn, "HelloWorld.class");
|
||||
|
||||
RemoteMethod refreshBreakpoints = conn.getMethod("refresh_breakpoints");
|
||||
RemoteMethod deleteBreakpoint = conn.getMethod("delete_breakpoint");
|
||||
RemoteMethod stepInto = conn.getMethod("step_into");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
txPut(conn, "Threads");
|
||||
|
||||
TraceObject thread = waitForObject("VMs[].Threads[main]");
|
||||
stepInto.invoke(Map.of("thread", thread));
|
||||
waitTxDone();
|
||||
|
||||
conn.execute("VirtualMachine vm = manager.getCurrentVM();");
|
||||
conn.execute("Location loc = manager.getCurrentLocation();");
|
||||
conn.execute(
|
||||
"BreakpointRequest brkReq = vm.eventRequestManager().createBreakpointRequest(loc);");
|
||||
|
||||
TraceObject brkPts = waitForObject("VMs[].Breakpoints");
|
||||
waitForPass(() -> {
|
||||
refreshBreakpoints.invoke(Map.of("container", brkPts));
|
||||
List<TraceObjectValue> d = waitForValues("VMs[].Breakpoints[]._display");
|
||||
assertEquals(1, d.size());
|
||||
});
|
||||
|
||||
TraceObject brk = waitForObject("VMs[].Breakpoints[]");
|
||||
deleteBreakpoint.invoke(Map.of("breakpoint", brk));
|
||||
|
||||
waitForPass(() -> {
|
||||
TraceObject bpts = waitForObject("VMs[].Breakpoints");
|
||||
refreshBreakpoints.invoke(Map.of("container", bpts));
|
||||
Collection<? extends TraceObjectValue> elements =
|
||||
bpts.getElements(Lifespan.at(getMaxSnap()));
|
||||
assertTrue(elements.isEmpty());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void start(JshellAndConnection conn, String obj) {
|
||||
if (obj != null) {
|
||||
conn.execute("cmds.ghidraTraceStart(\"" + obj + "\");");
|
||||
}
|
||||
else {
|
||||
conn.execute("mds.ghidraTraceStart();");
|
||||
}
|
||||
conn.execute("cmds.ghidraTraceCreate(System.getenv());");
|
||||
conn.execute("cmds.ghidraTraceTxStart(\"Create snapshot\")");
|
||||
conn.execute("cmds.ghidraTraceNewSnap(\"Scripted snapshot\")");
|
||||
conn.execute("cmds.ghidraTraceTxCommit()");
|
||||
}
|
||||
|
||||
private void txPut(JshellAndConnection conn, String obj) {
|
||||
conn.execute("cmds.ghidraTraceTxStart(\"Tx\");");
|
||||
conn.execute("cmds.ghidraTracePut" + obj + "();");
|
||||
conn.execute("cmds.ghidraTraceTxCommit();");
|
||||
waitTxDone();
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -89,7 +89,7 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
|
|||
start(conn, "%s".formatted(cloneExit));
|
||||
conn.execute("break set -n work");
|
||||
waitForPass(() -> {
|
||||
TraceObject proc = tb.objAny("Processes[]");
|
||||
TraceObject proc = tb.objAny0("Processes[]");
|
||||
assertNotNull(proc);
|
||||
assertEquals("STOPPED", tb.objValue(proc, lastSnap(conn), "_state"));
|
||||
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||
|
@ -119,7 +119,7 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
|
|||
conn.execute("break set -n work");
|
||||
|
||||
waitForPass(() -> {
|
||||
TraceObject inf = tb.objAny("Processes[]");
|
||||
TraceObject inf = tb.objAny0("Processes[]");
|
||||
assertNotNull(inf);
|
||||
assertEquals("STOPPED", tb.objValue(inf, lastSnap(conn), "_state"));
|
||||
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||
|
@ -131,7 +131,7 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
|
|||
conn.execute("continue");
|
||||
waitStopped(conn.conn);
|
||||
waitForPass(() -> {
|
||||
TraceObject inf = tb.objAny("Processes[]");
|
||||
TraceObject inf = tb.objAny0("Processes[]");
|
||||
assertNotNull(inf);
|
||||
assertEquals("STOPPED", tb.objValue(inf, lastSnap(conn), "_state"));
|
||||
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||
|
@ -283,7 +283,7 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
|
|||
conn.execute("cont");
|
||||
waitRunning(conn.conn);
|
||||
|
||||
TraceObject proc = waitForValue(() -> tb.objAny("Processes[]"));
|
||||
TraceObject proc = waitForValue(() -> tb.objAny0("Processes[]"));
|
||||
waitForPass(() -> {
|
||||
assertEquals("RUNNING", tb.objValue(proc, lastSnap(conn), "_state"));
|
||||
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||
|
@ -297,7 +297,7 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
|
|||
try (LldbAndTrace conn = startAndSyncLldb()) {
|
||||
start(conn, getSpecimenPrint());
|
||||
|
||||
TraceObject inf = waitForValue(() -> tb.objAny("Processes[]"));
|
||||
TraceObject inf = waitForValue(() -> tb.objAny0("Processes[]"));
|
||||
waitForPass(() -> {
|
||||
assertEquals("STOPPED", tb.objValue(inf, lastSnap(conn), "_state"));
|
||||
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||
|
@ -318,7 +318,7 @@ public class LldbHooksTest extends AbstractLldbTraceRmiTest {
|
|||
assertNotNull(snapshot);
|
||||
assertEquals("Exited with code 72", snapshot.getDescription());
|
||||
|
||||
TraceObject proc = tb.objAny("Processes[]");
|
||||
TraceObject proc = tb.objAny0("Processes[]");
|
||||
assertNotNull(proc);
|
||||
Object val = tb.objValue(proc, lastSnap(conn), "_exit_code");
|
||||
assertThat(val, instanceOf(Number.class));
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -79,7 +79,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
RemoteMethod refreshAvailable = conn.getMethod("refresh_available");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject available = Objects.requireNonNull(tb.objAny("Available"));
|
||||
TraceObject available = Objects.requireNonNull(tb.objAny0("Available"));
|
||||
|
||||
refreshAvailable.invoke(Map.of("node", available));
|
||||
|
||||
|
@ -107,7 +107,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
conn.execute("breakpoint set --name main");
|
||||
conn.execute("breakpoint set -H --name main");
|
||||
txPut(conn, "breakpoints");
|
||||
TraceObject breakpoints = Objects.requireNonNull(tb.objAny("Breakpoints"));
|
||||
TraceObject breakpoints = Objects.requireNonNull(tb.objAny0("Breakpoints"));
|
||||
refreshBreakpoints.invoke(Map.of("node", breakpoints));
|
||||
|
||||
List<TraceObjectValue> procBreakLocVals = tb.trace.getObjectManager()
|
||||
|
@ -145,7 +145,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
waitStopped(conn);
|
||||
|
||||
TraceObject locations =
|
||||
Objects.requireNonNull(tb.objAny("Processes[].Breakpoints"));
|
||||
Objects.requireNonNull(tb.objAny0("Processes[].Breakpoints"));
|
||||
conn.execute("breakpoint set --name main");
|
||||
conn.execute("breakpoint set -H --name main");
|
||||
refreshProcBreakpoints.invoke(Map.of("node", locations));
|
||||
|
@ -183,7 +183,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
waitStopped(conn);
|
||||
|
||||
TraceObject locations =
|
||||
Objects.requireNonNull(tb.objAny("Processes[].Watchpoints"));
|
||||
Objects.requireNonNull(tb.objAny0("Processes[].Watchpoints"));
|
||||
conn.execute("watchpoint set expression -s 1 -- `(void(*)())main`");
|
||||
conn.execute("watchpoint set expression -s 1 -w read -- `(void(*)())main`+-0x20");
|
||||
conn.execute(
|
||||
|
@ -230,7 +230,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
RemoteMethod refreshProcesses = conn.getMethod("refresh_processes");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject processes = Objects.requireNonNull(tb.objAny("Processes"));
|
||||
TraceObject processes = Objects.requireNonNull(tb.objAny0("Processes"));
|
||||
|
||||
refreshProcesses.invoke(Map.of("node", processes));
|
||||
|
||||
|
@ -254,7 +254,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
RemoteMethod refreshEnvironment = conn.getMethod("refresh_environment");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject env = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject env = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshEnvironment.invoke(Map.of("node", env));
|
||||
|
||||
|
@ -276,7 +276,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
RemoteMethod refreshThreads = conn.getMethod("refresh_threads");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject threads = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject threads = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshThreads.invoke(Map.of("node", threads));
|
||||
|
||||
|
@ -302,7 +302,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
waitTxDone();
|
||||
|
||||
txPut(conn, "frames");
|
||||
TraceObject stack = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject stack = Objects.requireNonNull(tb.objAny0(path));
|
||||
refreshStack.invoke(Map.of("node", stack));
|
||||
|
||||
// Would be nice to control / validate the specifics
|
||||
|
@ -355,7 +355,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
RemoteMethod refreshMappings = conn.getMethod("refresh_mappings");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject memory = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject memory = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshMappings.invoke(Map.of("node", memory));
|
||||
|
||||
|
@ -377,7 +377,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
RemoteMethod refreshModules = conn.getMethod("refresh_modules");
|
||||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
TraceObject modules = Objects.requireNonNull(tb.objAny(path));
|
||||
TraceObject modules = Objects.requireNonNull(tb.objAny0(path));
|
||||
|
||||
refreshModules.invoke(Map.of("node", modules));
|
||||
|
||||
|
@ -474,7 +474,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject proc2 = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc2 = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
removeProcess.invoke(Map.of("process", proc2));
|
||||
|
||||
String out = conn.executeCapture("target list");
|
||||
|
@ -545,7 +545,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
detach.invoke(Map.of("process", proc));
|
||||
|
||||
String out = conn.executeCapture("target list");
|
||||
|
@ -565,7 +565,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
launch.invoke(Map.ofEntries(
|
||||
Map.entry("process", proc),
|
||||
Map.entry("file", getSpecimenPrint())));
|
||||
|
@ -587,7 +587,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/noname")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
launch.invoke(Map.ofEntries(
|
||||
Map.entry("process", proc),
|
||||
Map.entry("file", getSpecimenRead())));
|
||||
|
@ -619,7 +619,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped(conn);
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
kill.invoke(Map.of("process", proc));
|
||||
|
||||
String out = conn.executeCapture("target list");
|
||||
|
@ -677,7 +677,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
txPut(conn, "threads");
|
||||
conn.execute("script lldb.debugger.SetAsync(False)");
|
||||
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny("Processes[].Threads[]"));
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny0("Processes[].Threads[]"));
|
||||
stepToCall(conn, step_into, thread);
|
||||
|
||||
String dis2 = conn.executeCapture("dis -c2 -s '$pc'");
|
||||
|
@ -704,7 +704,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
txPut(conn, "threads");
|
||||
conn.execute("script lldb.debugger.SetAsync(False)");
|
||||
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny("Processes[].Threads[]"));
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny0("Processes[].Threads[]"));
|
||||
stepToCall(conn, step_over, thread);
|
||||
|
||||
String dis2 = conn.executeCapture("dis -c2 -s '$pc'");
|
||||
|
@ -733,7 +733,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
txPut(conn, "threads");
|
||||
conn.execute("script lldb.debugger.SetAsync(False)");
|
||||
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny("Processes[].Threads[]"));
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny0("Processes[].Threads[]"));
|
||||
String dis3 = conn.executeCapture("disassemble -c3 -s '$pc'");
|
||||
// TODO: Examine for control transfer?
|
||||
List<String> lines = List.of(dis3.split("\n"));
|
||||
|
@ -766,7 +766,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
|
||||
txPut(conn, "threads");
|
||||
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny("Processes[].Threads[]"));
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny0("Processes[].Threads[]"));
|
||||
activate.invoke(Map.of("thread", thread));
|
||||
|
||||
int initDepth = getDepth(conn);
|
||||
|
@ -796,7 +796,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
|
||||
txPut(conn, "threads");
|
||||
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny("Processes[].Threads[]"));
|
||||
TraceObject thread = Objects.requireNonNull(tb.objAny0("Processes[].Threads[]"));
|
||||
activate.invoke(Map.of("thread", thread));
|
||||
|
||||
int initDepth = getDepth(conn);
|
||||
|
@ -819,7 +819,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
try (ManagedDomainObject mdo = openDomainObject("/New Traces/lldb/expPrint")) {
|
||||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
long address = Long.decode(conn.executeCapture("dis -c1 -n main").split("\\s+")[1]);
|
||||
breakAddress.invoke(Map.of("process", proc, "address", tb.addr(address)));
|
||||
|
||||
|
@ -861,7 +861,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped(conn);
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
long address = Long.decode(conn.executeCapture("dis -c1 -n main").split("\\s+")[1]);
|
||||
breakAddress.invoke(Map.of("process", proc, "address", tb.addr(address)));
|
||||
|
||||
|
@ -903,7 +903,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped(conn);
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
long address = Long.decode(conn.executeCapture("dis -c1 -n main").split("\\s+")[1]);
|
||||
AddressRange range = tb.range(address, address + 0); // length 1
|
||||
breakRange.invoke(Map.of("process", proc, "range", range));
|
||||
|
@ -949,7 +949,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped(conn);
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
long address = Long.decode(conn.executeCapture("dis -c1 -n main").split("\\s+")[1]);
|
||||
AddressRange range = tb.range(address, address + 0); // length 1
|
||||
breakRange.invoke(Map.of("process", proc, "range", range));
|
||||
|
@ -995,7 +995,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||
waitStopped(conn);
|
||||
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]"));
|
||||
TraceObject proc = Objects.requireNonNull(tb.objAny0("Processes[]"));
|
||||
long address = Long.decode(conn.executeCapture("dis -c1 -n main").split("\\s+")[1]);
|
||||
AddressRange range = tb.range(address, address + 0); // length 1
|
||||
breakRange.invoke(Map.of("process", proc, "range", range));
|
||||
|
@ -1063,7 +1063,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
|
||||
conn.execute("breakpoint set -n main");
|
||||
txPut(conn, "breakpoints");
|
||||
TraceObject bpt = Objects.requireNonNull(tb.objAny("Breakpoints[]"));
|
||||
TraceObject bpt = Objects.requireNonNull(tb.objAny0("Breakpoints[]"));
|
||||
|
||||
toggleBreakpoint.invoke(Map.of("breakpoint", bpt, "enabled", false));
|
||||
|
||||
|
@ -1087,7 +1087,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
conn.execute("breakpoint set -n main");
|
||||
txPut(conn, "breakpoints");
|
||||
|
||||
TraceObject loc = Objects.requireNonNull(tb.objAny("Breakpoints[][]"));
|
||||
TraceObject loc = Objects.requireNonNull(tb.objAny0("Breakpoints[][]"));
|
||||
|
||||
toggleBreakpointLocation.invoke(Map.of("location", loc, "enabled", false));
|
||||
|
||||
|
@ -1110,7 +1110,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
|
||||
conn.execute("breakpoint set -n main");
|
||||
txPut(conn, "breakpoints");
|
||||
TraceObject bpt = Objects.requireNonNull(tb.objAny("Breakpoints[]"));
|
||||
TraceObject bpt = Objects.requireNonNull(tb.objAny0("Breakpoints[]"));
|
||||
|
||||
deleteBreakpoint.invoke(Map.of("breakpoint", bpt));
|
||||
|
||||
|
@ -1140,7 +1140,7 @@ public class LldbMethodsTest extends AbstractLldbTraceRmiTest {
|
|||
assertThat(out, containsString(Long.toHexString(address)));
|
||||
|
||||
txPut(conn, "watchpoints");
|
||||
TraceObject wpt = Objects.requireNonNull(tb.objAny("Processes[].Watchpoints[]"));
|
||||
TraceObject wpt = Objects.requireNonNull(tb.objAny0("Processes[].Watchpoints[]"));
|
||||
|
||||
deleteWatchpoint.invoke(Map.of("watchpoint", wpt));
|
||||
|
||||
|
|
|
@ -26,13 +26,22 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
|
||||
import db.Transaction;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.analysis.AnalysisBackgroundCommand;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
|
||||
import ghidra.app.services.TraceRmiLauncherService;
|
||||
import ghidra.app.util.importer.AutoImporter;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.LoadResults;
|
||||
import ghidra.debug.api.ValStr;
|
||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.*;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.ConsoleTaskMonitor;
|
||||
|
||||
public class TraceRmiLauncherServicePluginTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
|
@ -68,6 +77,20 @@ public class TraceRmiLauncherServicePluginTest extends AbstractGhidraHeadedDebug
|
|||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetClassName() throws Exception {
|
||||
ResourceFile rf = Application.getModuleDataFile("TestResources", "HelloWorld.class");
|
||||
LoadResults<Program> results = AutoImporter.importByUsingBestGuess(rf.getFile(false),
|
||||
env.getProject(), "/", this, new MessageLog(), monitor);
|
||||
program = results.getPrimaryDomainObject();
|
||||
AutoAnalysisManager analyzer = AutoAnalysisManager.getAnalysisManager(program);
|
||||
analyzer.reAnalyzeAll(null);
|
||||
Command<Program> cmd = new AnalysisBackgroundCommand(analyzer, false);
|
||||
tool.execute(cmd, program);
|
||||
waitForBusyTool(tool);
|
||||
assertEquals("HelloWorld", TraceRmiLauncherServicePlugin.tryProgramJvmClass(program));
|
||||
}
|
||||
|
||||
// @Test // This is currently hanging the test machine. The gdb process is left running
|
||||
public void testLaunchLocalGdb() throws Exception {
|
||||
assumeTrue(OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.LINUX);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue