From cce33f772e0e32b65de6e5d4667aa14367ce11bf Mon Sep 17 00:00:00 2001 From: d-millar <33498836+d-millar@users.noreply.github.com> Date: Sat, 14 Sep 2024 02:17:12 +0000 Subject: [PATCH] GP-4894: Improve and better test Java debug connector. --- .../data/debugger-launchers/local-java.jsh | 8 +- .../dbg/jdi/manager/JdiEventHandler.java | 1 + .../dbg/jdi/manager/impl/JdiManagerImpl.java | 34 +- .../jpda/{TraceJdiArch.java => JdiArch.java} | 2 +- .../ghidra/dbg/jdi/rmi/jpda/JdiArguments.java | 109 ++ .../dbg/jdi/rmi/jpda/JdiClientThread.java | 83 +- ...TraceJdiCommands.java => JdiCommands.java} | 511 +++++--- .../{TraceJdiHooks.java => JdiHooks.java} | 89 +- .../{TraceJdiManager.java => JdiManager.java} | 191 +-- .../{TraceJdiMethods.java => JdiMethods.java} | 1088 +++++++++-------- .../core/debug/client/tracermi/jdi_schema.xml | 46 +- .../TraceRmiLauncherServicePlugin.html | 2 +- .../debug/client/tracermi/ProtobufSocket.java | 10 + .../core/debug/client/tracermi/RmiBatch.java | 48 +- .../core/debug/client/tracermi/RmiClient.java | 295 +++-- .../tracermi/RmiMethodHandlerThread.java | 75 -- .../client/tracermi/RmiMethodRegistry.java | 11 +- .../client/tracermi/RmiRemoteMethod.java | 61 +- .../tracermi/RmiRemoteMethodParameter.java | 20 +- .../tracermi/RmiReplyHandlerThread.java | 112 ++ .../core/debug/client/tracermi/RmiTrace.java | 165 ++- .../debug/client/tracermi/RmiTraceObject.java | 60 +- .../client/tracermi/RmiTraceObjectValue.java | 22 + .../debug/client/tracermi/RmiTransaction.java | 4 - .../RemoteMethodInvocationDialog.java | 11 +- .../TraceRmiLauncherServicePlugin.java | 118 +- .../service/tracermi/TraceRmiHandler.java | 14 +- .../service/tracermi/TraceRmiPlugin.java | 41 +- .../RemoteMethodInvocationDialogTest.java | 141 +++ .../debug/gui/InvocationDialogHelper.java | 7 + .../java/ghidra/dbg/target/TargetMethod.java | 52 +- .../main/java/ghidra/dbg/util/ShellUtils.java | 6 +- .../trace/database/ToyDBTraceBuilder.java | 16 +- .../core/misc/RecoverySnapshotMgrPlugin.java | 5 +- .../docking/options/editor/StringEditor.java | 32 +- .../agent/dbgeng/rmi/DbgEngHooksTest.java | 14 +- .../agent/dbgeng/rmi/DbgEngMethodsTest.java | 56 +- .../java/rmi/AbstractJavaTraceRmiTest.java | 581 +++++++++ .../java/agent/java/rmi/JavaCommandsTest.java | 1075 ++++++++++++++++ .../java/agent/java/rmi/JavaHooksTest.java | 381 ++++++ .../java/agent/java/rmi/JavaMethodsTest.java | 806 ++++++++++++ .../java/agent/lldb/rmi/LldbHooksTest.java | 16 +- .../java/agent/lldb/rmi/LldbMethodsTest.java | 62 +- .../TraceRmiLauncherServicePluginTest.java | 23 + 44 files changed, 5159 insertions(+), 1345 deletions(-) rename Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/{TraceJdiArch.java => JdiArch.java} (98%) create mode 100644 Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArguments.java rename Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/{TraceJdiCommands.java => JdiCommands.java} (81%) rename Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/{TraceJdiHooks.java => JdiHooks.java} (87%) rename Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/{TraceJdiManager.java => JdiManager.java} (57%) rename Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/{TraceJdiMethods.java => JdiMethods.java} (53%) delete mode 100644 Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiMethodHandlerThread.java create mode 100644 Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiReplyHandlerThread.java create mode 100644 Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/client/tracermi/RmiTraceObjectValue.java create mode 100644 Ghidra/Debug/Debugger-rmi-trace/src/test/java/ghidra/app/plugin/core/debug/gui/tracermi/RemoteMethodInvocationDialogTest.java create mode 100644 Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/AbstractJavaTraceRmiTest.java create mode 100644 Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaCommandsTest.java create mode 100644 Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaHooksTest.java create mode 100644 Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaMethodsTest.java diff --git a/Ghidra/Debug/Debugger-jpda/data/debugger-launchers/local-java.jsh b/Ghidra/Debug/Debugger-jpda/data/debugger-launchers/local-java.jsh index 409b733e43..ef53572a03 100755 --- a/Ghidra/Debug/Debugger-jpda/data/debugger-launchers/local-java.jsh +++ b/Ghidra/Debug/Debugger-jpda/data/debugger-launchers/local-java.jsh @@ -1,5 +1,6 @@ -//@title java launch -//@timeout 20000 +//@title java +////@image-opt env:OPT_TARGET_CLASS +//@timeout 2000000 //@desc //@desc

Launch with java

//@desc

@@ -10,10 +11,11 @@ //@menu-group local //@icon icon.debugger //@help TraceRmiLauncherServicePlugin#java +//@env OPT_TARGET_CLASS:str="" "Image" "The Main Class to launch (defaults to current program)." +//@env OPT_TARGET_CLASSPATH:str="" "ClassPath" "The JVM classpath" //@args "Arguments" "Command-line arguments to pass to the target" //@enum Arch:str JVM Dalvik //@env OPT_ARCH:Arch="JVM" "Arch" "Either 'JVM' or 'Dalvik'" -//@env OPT_TARGET_CLASS:str="" "Image" "The Main Class to launch (defaults to current program)." ////@env OPT_SUSPEND:bool=true "Suspend" "Suspend the VM on launch." //@env OPT_INCLUDE:str=n "Include virtual threads" "Include virtual threads." //@env OPT_JSHELL_PATH:file="" "JShell cmd (if desired)" "The full path to jshell." diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventHandler.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventHandler.java index 10176849da..2efc29bfcc 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventHandler.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/JdiEventHandler.java @@ -140,6 +140,7 @@ public class JdiEventHandler implements Runnable { //System.err.println(event + ":" + vm); return switch (event) { case ExceptionEvent ev -> processException(ev); + case BreakpointEvent ev -> processBreakpoint(ev); case AccessWatchpointEvent ev -> processAccessWatchpoint(ev); case ModificationWatchpointEvent ev -> processWatchpointModification(ev); case WatchpointEvent ev -> processWatchpoint(ev); diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/JdiManagerImpl.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/JdiManagerImpl.java index d8c58efe95..f71e379d0f 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/JdiManagerImpl.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/manager/impl/JdiManagerImpl.java @@ -17,7 +17,7 @@ package ghidra.dbg.jdi.manager.impl; import static ghidra.lifecycle.Unfinished.TODO; -import java.io.IOException; +import java.io.*; import java.util.*; import java.util.concurrent.*; @@ -29,6 +29,7 @@ import com.sun.jdi.event.Event; import ghidra.dbg.jdi.manager.*; import ghidra.dbg.jdi.manager.JdiCause.Causes; +import ghidra.dbg.jdi.rmi.jpda.JdiArguments; import ghidra.util.Msg; import ghidra.util.datastruct.ListenerSet; @@ -61,23 +62,40 @@ public class JdiManagerImpl implements JdiManager { virtualMachineManager = Bootstrap.virtualMachineManager(); } + private static void pumpStream(InputStream in, OutputStream out) { + try { + in.transferTo(out); + } + catch (IOException e) { + // We're done! + } + } + public VirtualMachine connectVM(Connector cx, Map arguments) throws Exception { - if (cx instanceof LaunchingConnector) { - LaunchingConnector lcx = (LaunchingConnector) cx; - return lcx.launch(arguments); + if (cx instanceof LaunchingConnector lcx) { + VirtualMachine vm = lcx.launch(arguments); + new Thread(() -> pumpStream(vm.process().getErrorStream(), System.err)).start(); + new Thread(() -> pumpStream(vm.process().getInputStream(), System.out)).start(); + return vm; } - if (cx instanceof AttachingConnector) { - AttachingConnector acx = (AttachingConnector) cx; + if (cx instanceof AttachingConnector acx) { return acx.attach(arguments); } - if (cx instanceof ListeningConnector) { - ListeningConnector lcx = (ListeningConnector) cx; + if (cx instanceof ListeningConnector lcx) { return lcx.accept(arguments); } throw new Exception("Unknown connector type"); } + public VirtualMachine createVM(Map env) { + JdiArguments args = new JdiArguments(env); + Connector cx = args.getConnector(virtualMachineManager); + Map defaultArguments = cx.defaultArguments(); + args.putArguments(defaultArguments); + return addVM(cx, defaultArguments); + } + @Override public void terminate() { /** diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiArch.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java similarity index 98% rename from Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiArch.java rename to Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java index 93d360eacd..27d444eb98 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiArch.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java @@ -23,7 +23,7 @@ import ghidra.app.plugin.core.debug.client.tracermi.DefaultRegisterMapper; import ghidra.program.model.lang.*; import ghidra.program.util.DefaultLanguageService; -public class TraceJdiArch { +public class JdiArch { private LanguageID langID; private Language language; diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArguments.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArguments.java new file mode 100644 index 0000000000..01073ca7eb --- /dev/null +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArguments.java @@ -0,0 +1,109 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.dbg.jdi.rmi.jpda; + +import java.util.Map; + +import com.sun.jdi.VirtualMachineManager; +import com.sun.jdi.connect.AttachingConnector; +import com.sun.jdi.connect.Connector; +import com.sun.jdi.connect.Connector.Argument; + +import ghidra.dbg.util.ShellUtils; + +public class JdiArguments { + enum Mode { + ATTACH_PORT, ATTACH_PID, LAUNCH; + } + + private final Map env; + private final Mode mode; + + public JdiArguments(Map env) { + this.env = Map.copyOf(env); + this.mode = computeMode(); + } + + /** + * Compute/detect the launch mode using the environment map. + * + *

+ * It'd be nice if this were selected/specified in the script body, rather than by what options + * are present in its header. The reason we can't, though, is that the JDI client thread needs + * to also work within Ghidra's JVM, i.e., without launching a jshell subprocess. By far, the + * simplest way to accomplish this is to keep all the logic here, and just pass the environment + * map in. For the jshell-subprocess case, it's the environment map proper. For the + * in-Ghidra's-VM case, it's the map we would have passed when creating the subprocess. + * + * @return the mode. + */ + protected Mode computeMode() { + if (env.containsKey("OPT_PORT")) { + return Mode.ATTACH_PORT; + } + if (env.containsKey("OPT_PID")) { + return Mode.ATTACH_PID; + } + return Mode.LAUNCH; + } + + protected AttachingConnector findConnectorByArgKey(VirtualMachineManager vmm, String key) { + return vmm.attachingConnectors() + .stream() + .filter(ac -> ac.defaultArguments().containsKey(key)) + .findFirst() + .orElseThrow(); + } + + public Connector getConnector(VirtualMachineManager vmm) { + return switch (mode) { + case ATTACH_PORT -> findConnectorByArgKey(vmm, "port"); + case ATTACH_PID -> findConnectorByArgKey(vmm, "pid"); + case LAUNCH -> vmm.defaultConnector(); + }; + } + + public void putArguments(Map args) { + switch (mode) { + case ATTACH_PORT -> { + args.get("hostname").setValue(env.get("OPT_HOST").toString()); + args.get("port").setValue(env.get("OPT_PORT").toString()); + args.get("timeout").setValue(env.get("OPT_TIMEOUT").toString()); + } + case ATTACH_PID -> { + args.get("pid").setValue(env.get("OPT_PID").toString()); + args.get("timeout").setValue(env.get("OPT_TIMEOUT").toString()); + } + case LAUNCH -> { + args.get("main").setValue(env.get("OPT_TARGET_CLASS")); + Argument argSuspend = args.get("suspend"); + String optSuspend = env.get("OPT_SUSPEND"); + if (argSuspend != null && optSuspend != null) { + argSuspend.setValue(optSuspend); + } + Argument argIncludeVirtualThreads = args.get("includevirtualthreads"); + String optInclude = env.get("OPT_INCLUDE"); + if (argIncludeVirtualThreads != null && optInclude != null) { + argIncludeVirtualThreads.setValue(optInclude); + } + String cp = env.get("OPT_TARGET_CLASSPATH"); + if (!cp.isBlank()) { + args.get("options").setValue("-cp " + ShellUtils.generateArgument(cp)); + } + } + } + } +} diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiClientThread.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiClientThread.java index ab2ac07b91..ae0c79d45a 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiClientThread.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiClientThread.java @@ -17,7 +17,6 @@ package ghidra.dbg.jdi.rmi.jpda; import java.util.Map; -import com.sun.jdi.connect.AttachingConnector; import com.sun.jdi.connect.Connector; import com.sun.jdi.connect.Connector.Argument; @@ -26,70 +25,31 @@ import ghidra.dbg.jdi.manager.impl.JdiManagerImpl; import ghidra.util.Msg; public class JdiClientThread extends Thread { - enum Mode { - ATTACH_PORT, ATTACH_PID, LAUNCH; - } private final Map env; - private final Mode mode; + private final JdiArguments arguments; private JdiManagerImpl manager; - private TraceJdiManager traceJdiManager; + private JdiManager jdiManager; public JdiClientThread(Map env) { this.env = env; - this.mode = computeMode(); - } - - /** - * Compute/detect the launch mode using the environment map. - * - *

- * It'd be nice if this were selected/specified in the script body, rather than by what options - * are present in its header. The reason we can't, though, is that the JDI client thread needs - * to also work within Ghidra's JVM, i.e., without launching a jshell subprocess. By far, the - * simplest way to accomplish this is to keep all the logic here, and just pass the environment - * map in. For the jshell-subprocess case, it's the environment map proper. For the - * in-Ghidra's-VM case, it's the map we would have passed when creating the subprocess. - * - * @return the mode. - */ - Mode computeMode() { - if (env.containsKey("OPT_PORT")) { - return Mode.ATTACH_PORT; - } - if (env.containsKey("OPT_PID")) { - return Mode.ATTACH_PID; - } - return Mode.LAUNCH; - } - - AttachingConnector findConnectorByArgKey(String key) { - return manager.getVirtualMachineManager() - .attachingConnectors() - .stream() - .filter(ac -> ac.defaultArguments().containsKey(key)) - .findFirst() - .orElseThrow(); + this.arguments = new JdiArguments(env); } @Override public void run() { try { manager = new JdiManagerImpl(); - traceJdiManager = new TraceJdiManager(manager, env); + jdiManager = new JdiManager(manager, env); - Connector cx = switch (mode) { - case ATTACH_PORT -> findConnectorByArgKey("port"); - case ATTACH_PID -> findConnectorByArgKey("pid"); - case LAUNCH -> manager.getVirtualMachineManager().defaultConnector(); - }; + Connector cx = arguments.getConnector(manager.getVirtualMachineManager()); Map args = cx.defaultArguments(); - putArguments(args); + arguments.putArguments(args); if (manager.addVM(cx, args) != null) { - traceJdiManager.getCommands().ghidraTraceSyncEnable(); - traceJdiManager.getHooks().vmStarted(null, Causes.UNCLAIMED); + jdiManager.getCommands().ghidraTraceSyncEnable(); + jdiManager.getHooks().vmStarted(null, Causes.UNCLAIMED); } else { // Nothing. addVM should already have reported the error. @@ -100,30 +60,11 @@ public class JdiClientThread extends Thread { } } - protected void putArguments(Map args) { - switch (mode) { - case ATTACH_PORT -> { - args.get("hostname").setValue(env.get("OPT_HOST").toString()); - args.get("port").setValue(env.get("OPT_PORT").toString()); - args.get("timeout").setValue(env.get("OPT_TIMEOUT").toString()); - } - case ATTACH_PID -> { - args.get("pid").setValue(env.get("OPT_PID").toString()); - args.get("timeout").setValue(env.get("OPT_TIMEOUT").toString()); - } - case LAUNCH -> { - args.get("main").setValue(env.get("OPT_TARGET_CLASS")); - //args.get("suspend").setValue(env.get("OPT_SUSPEND")); - args.get("includevirtualthreads").setValue(env.get("OPT_INCLUDE")); - } - } + public JdiManager mgr() { + return jdiManager; } - public TraceJdiManager mgr() { - return traceJdiManager; - } - - public TraceJdiCommands cmds() { - return traceJdiManager.getCommands(); + public JdiCommands cmds() { + return jdiManager.getCommands(); } } diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiCommands.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java similarity index 81% rename from Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiCommands.java rename to Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java index 4299d79b2b..75fff119cb 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiCommands.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java @@ -15,7 +15,10 @@ */ package ghidra.dbg.jdi.rmi.jpda; +import static ghidra.dbg.jdi.rmi.jpda.JdiManager.*; + import java.io.IOException; +import java.io.PrintStream; import java.lang.ProcessHandle.Info; import java.math.BigInteger; import java.net.InetSocketAddress; @@ -23,6 +26,9 @@ import java.nio.channels.*; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; +import java.util.stream.IntStream; + +import org.apache.commons.lang3.StringUtils; import com.sun.jdi.*; import com.sun.jdi.Method; @@ -36,6 +42,7 @@ import ghidra.program.model.address.*; import ghidra.program.model.lang.*; import ghidra.program.model.lang.Language; import ghidra.rmi.trace.TraceRmi.*; +import ghidra.trace.model.Lifespan; import ghidra.util.Msg; /* @@ -49,7 +56,7 @@ import ghidra.util.Msg; class State { - RmiClient client; + public RmiClient client; public RmiTrace trace; RmiTransaction tx; @@ -109,25 +116,16 @@ class State { } -public class TraceJdiCommands { +public class JdiCommands { - private TraceJdiManager manager; + private JdiManager manager; private JdiManagerImpl jdi; public State state; private String[] regNames = { "PC", "return_address" }; public long MAX_REFS = 100; -// protected static final TargetStepKindSet SUPPORTED_KINDS = TargetStepKindSet.of( // -// TargetStepKind.FINISH, // -// TargetStepKind.LINE, // -// TargetStepKind.OVER, // -// TargetStepKind.OVER_LINE, // -// TargetStepKind.RETURN, // -// TargetStepKind.UNTIL, // -// TargetStepKind.EXTENDED); - - public TraceJdiCommands(TraceJdiManager manager) { + public JdiCommands(JdiManager manager) { this.manager = manager; this.jdi = manager.getJdi(); state = new State(); @@ -145,7 +143,7 @@ public class TraceJdiCommands { state.client = new RmiClient(channel, "jdi"); state.client.setRegistry(manager.remoteMethodRegistry); state.client.negotiate("Connect"); - Msg.info(this, "Connected to " + state.client.getDescription() + " at " + address); + Msg.out("Connected to " + state.client.getDescription()); } catch (NumberFormatException e) { throw new RuntimeException("Port must be numeric"); @@ -187,8 +185,7 @@ public class TraceJdiCommands { state.client = new RmiClient(client, "jdi"); state.client.setRegistry(manager.remoteMethodRegistry); client.configureBlocking(false); - Msg.info(this, "Connected from " + state.client.getDescription() + " at " + - client.getLocalAddress()); + Msg.out("Connected from " + state.client.getDescription()); } } keyIterator.remove(); @@ -219,7 +216,7 @@ public class TraceJdiCommands { } public void startTrace(String name) { - TraceJdiArch arch = manager.getArch(); + JdiArch arch = manager.getArch(); LanguageID language = arch.computeGhidraLanguage(); CompilerSpecID compiler = arch.computeGhidraCompiler(language); state.trace = state.client.createTrace(name, language, compiler); @@ -245,6 +242,9 @@ public class TraceJdiCommands { if (name == null) { name = computeName(); } + else if (name.contains("/")) { + name = name.substring(name.lastIndexOf("/")); + } state.requireNoTrace(); startTrace(name); } @@ -266,19 +266,26 @@ public class TraceJdiCommands { startTrace(name); } + public VirtualMachine ghidraTraceCreate(Map env) { + return manager.getJdi().createVM(env); + } + public void ghidraTraceInfo() { if (state.client == null) { Msg.error(this, "Not connected to Ghidra"); + return; } - Msg.info(this, "Connected to" + state.client.getDescription()); + Msg.info(this, "Connected to " + state.client.getDescription()); if (state.trace == null) { Msg.error(this, "No trace"); } - Msg.info(this, "Trace active"); + else { + Msg.info(this, "Trace active"); + } } public void ghidraTraceInfoLcsp() { - TraceJdiArch arch = manager.getArch(); + JdiArch arch = manager.getArch(); LanguageID language = arch.computeGhidraLanguage(); CompilerSpecID compiler = arch.computeGhidraCompiler(language); Msg.info(this, "Selected Ghidra language: " + language); @@ -286,7 +293,7 @@ public class TraceJdiCommands { } public void ghidraTraceTxStart(String description) { - state.requireTx(); + state.requireNoTx(); state.tx = state.requireTrace().startTx(description, false); } @@ -332,7 +339,6 @@ public class TraceJdiCommands { public void ghidraTraceDelMem(Address address, long length) { state.requireTrace(); try (RmiTransaction tx = state.trace.startTx("ghidraTraceDelMem", false)) { - VirtualMachine currentVM = manager.getJdi().getCurrentVM(); Address mapped = state.trace.memoryMapper.map(address); AddressRangeImpl range = new AddressRangeImpl(mapped, mapped.add(length - 1)); state.trace.deleteBytes(range, state.trace.getSnap()); @@ -369,19 +375,26 @@ public class TraceJdiCommands { public void ghidraTraceInsertObj(String path) { state.requireTx(); try (RmiTransaction tx = state.trace.startTx("ghidraTraceInsertObj", false)) { - state.trace.proxyObjectPath(path).insert(state.trace.getSnap(), Resolution.CR_ADJUST); + Lifespan span = state.trace.proxyObjectPath(path) + .insert(state.trace.getSnap(), Resolution.CR_ADJUST); + System.out.println("Inserted object: lifespan=" + span); } } public void ghidraTraceRemoveObj(String path) { state.requireTx(); try (RmiTransaction tx = state.trace.startTx("ghidraTraceRemoveObj", false)) { - state.trace.proxyObjectPath(path).remove(state.trace.getSnap(), false); + Lifespan span = state.trace.proxyObjectPath(path).remove(state.trace.getSnap(), false); + System.out.println("Removed object: lifespan=" + span); } } -// public void ghidraTraceSetValue(String path, String key, Object value, TargetObjectSchema schema) { -// } + public void ghidraTraceSetValue(String path, String key, Object value) { + state.requireTx(); + try (RmiTransaction tx = state.trace.startTx("ghidraTraceSetValue", false)) { + setValue(path, key, value); + } + } public void ghidraTraceRetainValues(String kind, String path, Set keys) { state.requireTx(); @@ -405,11 +418,77 @@ public class TraceJdiCommands { return state.trace.proxyObjectPath(path); } -// public void ghidraTraceGetValues(String pattern) { -// } -// -// public void ghidraTraceGetValuesRng() { -// } + public static class Tabulator { + static class Column { + int width; + + public void measure(String string) { + width = Math.max(width, string.length()); + } + + public void print(PrintStream out, String string) { + out.print(pad(string)); + } + + private String pad(String string) { + return StringUtils.rightPad(string, width); + } + } + + private final PrintStream out; + private final List columns; + + public Tabulator(PrintStream out, int colCount) { + this.out = out; + this.columns = IntStream.range(0, colCount).mapToObj(i -> new Column()).toList(); + } + + public void measure(Object... row) { + if (row.length != columns.size()) { + throw new IllegalArgumentException("Column count mismatch"); + } + for (int i = 0; i < row.length; i++) { + columns.get(i).measure(row[i].toString()); + } + } + + public void print(Object... row) { + if (row.length != columns.size()) { + throw new IllegalArgumentException("Column count mismatch"); + } + for (int i = 0; i < row.length; i++) { + if (i != 0) { + out.print(" "); + } + columns.get(i).print(out, row[i].toString()); + } + out.println(); + } + } + + public void printValues(List values) { + Tabulator tab = new Tabulator(System.out, 5); + tab.measure("Parent", "Key", "Span", "Value", "Type"); + for (RmiTraceObjectValue d : values) { + tab.measure(d.parent().getPath(), d.span(), d.key(), d.value(), d.schema()); + } + tab.print("Parent", "Key", "Span", "Value", "Type"); + for (RmiTraceObjectValue d : values) { + tab.print(d.parent().getPath(), d.span(), d.key(), d.value(), d.schema()); + } + } + + public void ghidraTraceGetValues(String pattern) { + state.requireTrace(); + List values = state.trace.getValues(pattern); + printValues(values); + } + + public void ghidraTraceGetValuesRng(Address addr, long sz) { + state.requireTrace(); + List values = state.trace.getValuesRng(addr, sz); + printValues(values); + } public void ghidraTracePutVMs() { state.requireTrace(); @@ -467,7 +546,6 @@ public class TraceJdiCommands { public void ghidraTraceDisassemble(Address address) { state.requireTrace(); - VirtualMachine currentVM = manager.getJdi().getCurrentVM(); MemoryMapper mapper = state.trace.memoryMapper; Address mappedAddress = mapper.map(address); AddressSpace addressSpace = mappedAddress.getAddressSpace(); @@ -480,7 +558,6 @@ public class TraceJdiCommands { // STATE // public void putMemState(Address start, long length, MemoryState memState, boolean usePages) { - VirtualMachine currentVM = manager.getJdi().getCurrentVM(); Address mapped = state.trace.memoryMapper.map(start); if (mapped.getAddressSpace() != start.getAddressSpace() && !memState.equals(MemoryState.MS_UNKNOWN)) { @@ -503,7 +580,7 @@ public class TraceJdiCommands { } public RegisterValue[] putRegisters(StackFrame frame, String ppath) { - TraceJdiArch arch = manager.getArch(); + JdiArch arch = manager.getArch(); Language lang = arch.getLanguage(); Set keys = new HashSet<>(); RegisterValue[] rvs = new RegisterValue[regNames.length]; @@ -568,7 +645,7 @@ public class TraceJdiCommands { Address addr = manager.getAddressFromLocation(loc); RegisterMapper mapper = state.trace.registerMapper; String regName = mapper.mapName(name); - TraceJdiArch arch = manager.getArch(); + JdiArch arch = manager.getArch(); Language lang = arch.getLanguage(); Register register = lang.getRegister(name); RegisterValue rv = new RegisterValue(register, addr.getOffsetAsBigInteger()); @@ -584,7 +661,6 @@ public class TraceJdiCommands { } public void putMem(Address address, long length, boolean create) { - VirtualMachine vm = manager.getJdi().getCurrentVM(); MemoryMapper mapper = state.trace.memoryMapper; Address mappedAddress = mapper.map(address); AddressSpace addressSpace = mappedAddress.getAddressSpace(); @@ -636,8 +712,8 @@ public class TraceJdiCommands { } public void putTypeDetails(String path, Type type) { - setValue(path, "_display", "Type: " + type.name()); - setValue(path, "Signature", type.signature()); + setValue(path, ATTR_DISPLAY, "Type: " + type.name()); + setValue(path, ATTR_SIGNATURE, type.signature()); } public void putReferenceTypeContainer(String ppath, List reftypes) { @@ -666,17 +742,28 @@ public class TraceJdiCommands { if (name.indexOf(".") > 0) { name = name.substring(name.lastIndexOf(".") + 1); } - setValue(path, TraceJdiManager.MODULE_NAME_ATTRIBUTE_NAME, name + ".class"); + setValue(path, ATTR_MODULE_NAME, name + ".class"); putRefTypeAttributes(path, reftype); - createObject(path + ".Fields"); - createObject(path + ".Instances"); - createObject(path + ".Locations"); - //createObject(path + ".Methods"); + String fpath = createObject(path + ".Fields"); + String ipath = createObject(path + ".Instances"); + String lpath = createObject(path + ".Locations"); + insertObject(fpath); + insertObject(ipath); + insertObject(lpath); putMethodContainer(path + ".Methods", reftype); String rpath = createObject(path + ".Relations"); + insertObject(rpath); ModuleReference module = reftype.module(); - createObject(module, module.name(), rpath + ".ModuleRef"); + String moduleName = module.name(); + if (moduleName == null) { + moduleName = ""; + } + if (moduleName.contains(".")) { + moduleName = "\"" + moduleName + "\""; + } + String mrpath = createObject(module, moduleName, rpath + ".ModuleRef"); + insertObject(mrpath); if (reftype instanceof ArrayType at) { putArrayTypeDetails(rpath, at); } @@ -713,6 +800,7 @@ public class TraceJdiCommands { setValue(path, "defaultStratum", reftype.defaultStratum()); setValue(path, "availableStata", reftype.availableStrata()); setValue(path, "failedToInitialize", reftype.failedToInitialize()); + insertObject(path); } private void registerMemory(String path, ReferenceType reftype) { @@ -727,17 +815,17 @@ public class TraceJdiCommands { bounds.add(range); String mpath = createObject(mempath + manager.key(m.toString())); - setValue(mpath, "Range", range); - insertObject(path); + setValue(mpath, ATTR_RANGE, range); + insertObject(mpath); } } } AddressRange range = manager.putAddressRange(reftype, bounds); - setValue(path, "Range", range); + setValue(path, ATTR_RANGE, range); - setValue(path, "Count", reftype.constantPoolCount()); + setValue(path, ATTR_COUNT, reftype.constantPoolCount()); range = manager.getPoolAddressRange(reftype, getSize(reftype) - 1); - setValue(path, "RangeCP", range); + setValue(path, ATTR_RANGE_CP, range); try { putMem(range.getMinAddress(), range.getLength(), true); } @@ -755,19 +843,24 @@ public class TraceJdiCommands { } } - public void loadReferenceType(String ppath, List reftypes, String targetClass) { + public boolean loadReferenceType(String ppath, List reftypes, + String targetClass) { + boolean result = false; List classes = reftypes; for (ReferenceType ref : classes) { if (ref.name().contains(targetClass)) { putReferenceType(ppath, ref, true); + result = true; } } + return result; } public void putArrayTypeDetails(String path, ArrayType type) { + String cpath = createObject(path + ".ComponentType"); setValue(path, "ComponentSignature", type.componentSignature()); setValue(path, "ComponentTypeName", type.componentTypeName()); - createObject(path + ".ComponentType"); + insertObject(cpath); } public void putClassTypes(String ppath, List reftypes) { @@ -781,10 +874,14 @@ public class TraceJdiCommands { public void putClassTypeDetails(String path, ClassType type) { setValue(path, "IsEnum", type.isEnum()); - createObject(path + ".AllInterfaces"); - createObject(path + ".Interfaces"); - createObject(path + ".SubClasses"); - createObject(path + ".ClassType"); + String aipath = createObject(path + ".AllInterfaces"); + String ipath = createObject(path + ".Interfaces"); + String scpath = createObject(path + ".SubClasses"); + String cpath = createObject(path + ".ClassType"); + insertObject(aipath); + insertObject(ipath); + insertObject(scpath); + insertObject(cpath); } public void putInterfaceTypes(String ppath, List reftypes) { @@ -797,9 +894,12 @@ public class TraceJdiCommands { } public void putInterfaceTypeDetails(String path, InterfaceType type) { - createObject(path + ".Implementors"); - createObject(path + ".SubInterfaces"); - createObject(path + ".SuperInterfaces"); + String impath = createObject(path + ".Implementors"); + String sbpath = createObject(path + ".SubInterfaces"); + String sppath = createObject(path + ".SuperInterfaces"); + insertObject(impath); + insertObject(sbpath); + insertObject(sppath); } // VALUES // @@ -812,7 +912,7 @@ public class TraceJdiCommands { public void putValue(String ppath, String key, Value value) { String path = createObject(value, key, ppath); - setValue(path, "_display", "Value: " + value.toString()); + setValue(path, ATTR_DISPLAY, "Value: " + value.toString()); //putValueDetailsByType(path, value); insertObject(path); } @@ -848,35 +948,35 @@ public class TraceJdiCommands { } public void putValueDetails(String path, Value value) { - putType(path, "Type", value.type()); + putType(path, ATTR_TYPE, value.type()); } public void putPrimitiveValue(String ppath, PrimitiveValue value) { String path = createObject(value, value.toString(), ppath); putValueDetails(path, value); if (value instanceof BooleanValue v) { - setValue(path, "Value", v.booleanValue()); + setValue(path, ATTR_VALUE, v.booleanValue()); } if (value instanceof ByteValue b) { - setValue(path, "Value", b.byteValue()); + setValue(path, ATTR_VALUE, b.byteValue()); } if (value instanceof CharValue v) { - setValue(path, "Value", v.charValue()); + setValue(path, ATTR_VALUE, v.charValue()); } if (value instanceof ShortValue v) { - setValue(path, "Value", v.shortValue()); + setValue(path, ATTR_VALUE, v.shortValue()); } if (value instanceof IntegerValue v) { - setValue(path, "Value", v.intValue()); + setValue(path, ATTR_VALUE, v.intValue()); } if (value instanceof LongValue v) { - setValue(path, "Value", v.longValue()); + setValue(path, ATTR_VALUE, v.longValue()); } if (value instanceof FloatValue v) { - setValue(path, "Value", v.floatValue()); + setValue(path, ATTR_VALUE, v.floatValue()); } if (value instanceof DoubleValue v) { - setValue(path, "Value", v.doubleValue()); + setValue(path, ATTR_VALUE, v.doubleValue()); } insertObject(path); } @@ -962,7 +1062,8 @@ public class TraceJdiCommands { public void putObjectContainer(String path, List objects) { for (ObjectReference obj : objects) { - createObject(obj, obj.toString(), path); + String opath = createObject(obj, obj.toString(), path); + insertObject(opath); } } @@ -983,27 +1084,34 @@ public class TraceJdiCommands { // IGNORE } setValue(apath, "isCollected", ref.isCollected()); + insertObject(apath); String rpath = createObject(path + ".Relations"); try { if (ref.owningThread() != null) { - createObject(rpath + ".OwningThread"); + String otpath = createObject(rpath + ".OwningThread"); + insertObject(otpath); } if (ref.waitingThreads() != null) { - createObject(rpath + ".WaitingThreads"); + String wtpath = createObject(rpath + ".WaitingThreads"); + insertObject(wtpath); } } catch (IncompatibleThreadStateException e) { // IGNORE } if (ref.referenceType() != null) { - createObject(rpath + ".ReferenceType"); + String rtpath = createObject(rpath + ".ReferenceType"); + insertObject(rtpath); } if (ref.referringObjects(MAX_REFS) != null) { - createObject(rpath + ".ReferringObjects"); + String ropath = createObject(rpath + ".ReferringObjects"); + insertObject(ropath); } if (!(ref instanceof ArrayReference)) { - createObject(path + ".Variables"); + String vpath = createObject(path + ".Variables"); + insertObject(vpath); } + insertObject(rpath); } public void putArrayReference(String ppath, ArrayReference ref) { @@ -1014,8 +1122,9 @@ public class TraceJdiCommands { public void putArrayReferenceDetails(String path, ArrayReference ref) { putObjectReferenceDetails(path, ref); - setValue(path, "Length", ref.length()); - createObject(path + ".Values"); + setValue(path, ATTR_LENGTH, ref.length()); + String vpath = createObject(path + ".Values"); + insertObject(vpath); } public void putClassLoaderReference(String ppath, ClassLoaderReference ref) { @@ -1026,8 +1135,10 @@ public class TraceJdiCommands { public void putClassLoaderReferenceDetails(String path, ClassLoaderReference ref) { putObjectReferenceDetails(path, ref); - createObject(path + ".DefinedClasses"); - createObject(path + ".VisibleClasses"); + String dcpath = createObject(path + ".DefinedClasses"); + String vcpath = createObject(path + ".VisibleClasses"); + insertObject(dcpath); + insertObject(vcpath); } public void putClassObjectReference(String ppath, ClassObjectReference ref) { @@ -1038,7 +1149,8 @@ public class TraceJdiCommands { public void putClassObjectReferenceDetails(String path, ClassObjectReference ref) { putObjectReferenceDetails(path, ref); - createObject(path + ".ReflectedType"); + String rtpath = createObject(path + ".ReflectedType"); + insertObject(rtpath); } public void putModuleReferenceContainer() { @@ -1048,7 +1160,8 @@ public class TraceJdiCommands { List modules = vm.allModules(); for (ModuleReference ref : modules) { keys.add(manager.key(ref.name())); - createObject(ref, ref.name(), ppath); + String mpath = createObject(ref, ref.name(), ppath); + insertObject(mpath); } retainKeys(ppath, keys); } @@ -1061,7 +1174,8 @@ public class TraceJdiCommands { public void putModuleReferenceDetails(String path, ModuleReference ref) { putObjectReferenceDetails(path, ref); - createObject(path + ".ClassLoader"); + String clpath = createObject(path + ".ClassLoader"); + insertObject(clpath); } public void putStringReference(String ppath, StringReference ref) { @@ -1072,7 +1186,7 @@ public class TraceJdiCommands { public void putStringReferenceDetails(String path, StringReference ref) { putObjectReferenceDetails(path, ref); - setValue(path, "Value", ref.value()); + setValue(path, ATTR_VALUE, ref.value()); } public void putThreadGroupContainer(String refpath, List refs) { @@ -1094,10 +1208,13 @@ public class TraceJdiCommands { public void putThreadGroupReferenceDetails(String path, ThreadGroupReference ref) { putObjectReferenceDetails(path, ref); if (ref.parent() != null) { - createObject(path + ".Parent"); + String ppath = createObject(path + ".Parent"); + insertObject(ppath); } - createObject(path + ".ThreadGroups"); - createObject(path + ".Threads"); + String tgpath = createObject(path + ".ThreadGroups"); + String tpath = createObject(path + ".Threads"); + insertObject(tgpath); + insertObject(tpath); } public void putThreadContainer(String refpath, List refs, boolean asLink) { @@ -1123,13 +1240,18 @@ public class TraceJdiCommands { public void putThreadReferenceDetails(String path, ThreadReference ref) { putObjectReferenceDetails(path, ref); - createObject(path + ".Stack"); + String spath = createObject(path + ".Stack"); String rpath = createObject(path + ".Relations"); - createObject(rpath + ".CurrentContendedMonitor"); - createObject(rpath + ".OwnedMonitors"); - createObject(rpath + ".OwnedMonitorsAndFrames"); - createObject(rpath + ".ThreadGroup"); + String ccpath = createObject(rpath + ".CurrentContendedMonitor"); + String ompath = createObject(rpath + ".OwnedMonitors"); + String omfpath = createObject(rpath + ".OwnedMonitorsAndFrames"); + String tgpath = createObject(rpath + ".ThreadGroup"); putThreadAttributes(ref, path); + insertObject(spath); + insertObject(ccpath); + insertObject(ompath); + insertObject(omfpath); + insertObject(tgpath); } void putThreadAttributes(ThreadReference thread, String ppath) { @@ -1152,18 +1274,22 @@ public class TraceJdiCommands { // Ignore } setValue(path, "suspendCount", thread.suspendCount()); + insertObject(path); } public void putMonitorInfoContainer(String path, List info) { for (MonitorInfo f : info) { - createObject(f, f.toString(), path); + String ipath = createObject(f, f.toString(), path); + insertObject(ipath); } } public void putMonitorInfoDetails(String path, MonitorInfo info) { setValue(path, "StackDepth", info.stackDepth()); - createObject(path + ".Monitor"); - createObject(path + ".Thread"); + String mpath = createObject(path + ".Monitor"); + String tpath = createObject(path + ".Thread"); + insertObject(mpath); + insertObject(tpath); } // TYPE COMPONENTS @@ -1183,6 +1309,7 @@ public class TraceJdiCommands { catch (IllegalArgumentException iae) { // IGNORE } + keys.add(manager.key(f.name())); putField(path, f, value); } retainKeys(path, keys); @@ -1201,6 +1328,7 @@ public class TraceJdiCommands { catch (IllegalArgumentException iae) { // IGNORE } + keys.add(manager.key(f.name())); putField(path, f, value); } retainKeys(path, keys); @@ -1210,23 +1338,23 @@ public class TraceJdiCommands { String path = createObject(f, f.name(), ppath); putFieldDetails(path, f); if (value != null) { - putValue(path, "Value", value); - setValue(path, "_display", f.name() + " (" + f.typeName() + ") : " + value); + putValue(path, ATTR_VALUE, value); + setValue(path, ATTR_DISPLAY, f.name() + " (" + f.typeName() + ") : " + value); } else { - setValue(path, "_display", f.name() + " (" + f.typeName() + ")"); + setValue(path, ATTR_DISPLAY, f.name() + " (" + f.typeName() + ")"); } insertObject(path); } public void putFieldDetails(String path, Field f) { - setValue(path, TraceJdiManager.MODULE_NAME_ATTRIBUTE_NAME, f.declaringType().name()); + setValue(path, ATTR_MODULE_NAME, f.declaringType().name()); if (f.genericSignature() != null) { setValue(path, "GenericSignature", f.genericSignature()); } putFieldAttributes(path, f); try { - putType(path, "Type", f.type()); + putType(path, ATTR_TYPE, f.type()); } catch (ClassNotLoadedException e) { // IGNORE @@ -1247,6 +1375,7 @@ public class TraceJdiCommands { setValue(path, "isSynthetic", f.isSynthetic()); setValue(path, "isTransient", f.isTransient()); setValue(path, "isVolatile", f.isVolatile()); + insertObject(path); } public void putMethodContainer(String path, ReferenceType reftype) { @@ -1268,24 +1397,27 @@ public class TraceJdiCommands { public void putMethodDetails(String path, Method m, boolean partial) { ReferenceType declaringType = m.declaringType(); - setValue(path, TraceJdiManager.MODULE_NAME_ATTRIBUTE_NAME, declaringType.name()); + setValue(path, ATTR_MODULE_NAME, declaringType.name()); createLink(m, "DeclaringType", declaringType); if (!partial) { - createObject(path + ".Arguments"); + String apath = createObject(path + ".Arguments"); if (m.genericSignature() != null) { setValue(path, "GenericSignature", m.genericSignature()); } - createObject(path + ".Locations"); + String lpath = createObject(path + ".Locations"); setValue(path, "Modifiers", m.modifiers()); setValue(path, "ReturnType", m.returnTypeName()); setValue(path, "Signature", m.signature()); - createObject(path + ".Variables"); + String vpath = createObject(path + ".Variables"); putMethodAttributes(path, m); + insertObject(apath); + insertObject(lpath); + insertObject(vpath); } if (m.location() != null) { AddressRange range = manager.getAddressRange(m); if (!range.equals(manager.defaultRange)) { - setValue(path, "Range", range); + setValue(path, ATTR_RANGE, range); } } String bytes = ""; @@ -1308,6 +1440,12 @@ public class TraceJdiCommands { setValue(path, "isPrivate", m.isPrivate()); setValue(path, "isProtected", m.isProtected()); setValue(path, "isPublic", m.isPublic()); + setValue(path, "isStatic", m.isStatic()); + setValue(path, "isStaticInitializer", m.isStaticInitializer()); + setValue(path, "isSynchronized", m.isSynchronized()); + setValue(path, "isSynthetic", m.isSynthetic()); + setValue(path, "isVarArgs", m.isVarArgs()); + insertObject(path); } // OTHER OBJECTS // @@ -1334,17 +1472,21 @@ public class TraceJdiCommands { } public void putVMDetails(String path, VirtualMachine vm) { - createObject(path + ".Classes"); - createObject(path + ".Memory"); - createObject(path + ".ThreadGroups"); - createObject(path + ".Threads"); + String cpath = createObject(path + ".Classes"); + String mpath = createObject(path + ".Memory"); + String tgpath = createObject(path + ".ThreadGroups"); + String tpath = createObject(path + ".Threads"); Event currentEvent = jdi.getCurrentEvent(); String shortName = vm.name().substring(0, vm.name().indexOf(" ")); String display = currentEvent == null ? shortName : shortName + " [" + currentEvent + "]"; - setValue(path, TraceJdiManager.DISPLAY_ATTRIBUTE_NAME, display); - setValue(path, TraceJdiManager.ARCH_ATTRIBUTE_NAME, vm.name()); - setValue(path, TraceJdiManager.DEBUGGER_ATTRIBUTE_NAME, vm.description()); - setValue(path, TraceJdiManager.OS_ATTRIBUTE_NAME, vm.version()); + setValue(path, ATTR_DISPLAY, display); + setValue(path, ATTR_ARCH, vm.name()); + setValue(path, ATTR_DEBUGGER, vm.description()); + setValue(path, ATTR_OS, vm.version()); + insertObject(cpath); + insertObject(mpath); + insertObject(tgpath); + insertObject(tpath); } public void putProcesses() { @@ -1383,13 +1525,13 @@ public class TraceJdiCommands { Info info = proc.info(); Optional optional = info.command(); if (optional.isPresent()) { - setValue(path, "Executable", optional.get()); + setValue(path, ATTR_EXECUTABLE, optional.get()); } optional = info.commandLine(); if (optional.isPresent()) { - setValue(path, "CommandLine", optional.get()); + setValue(path, ATTR_COMMAND_LINE, optional.get()); } - setValue(path, "Alive", proc.isAlive()); + setValue(path, ATTR_ALIVE, proc.isAlive()); } public void putFrames() { @@ -1409,6 +1551,7 @@ public class TraceJdiCommands { // IGNORE } retainKeys(ppath, keys); + insertObject(ppath); } private void putFrame(String ppath, StackFrame frame, String key) { @@ -1419,17 +1562,20 @@ public class TraceJdiCommands { private void putFrameDetails(String path, StackFrame frame, String key) { Location location = frame.location(); - setValue(path, "_display", "[" + key + "] " + location + ":" + location.method().name() + + setValue(path, ATTR_DISPLAY, "[" + key + "] " + location + ":" + location.method().name() + ":" + location.codeIndex()); - putLocation(path, "Location", location); + putLocation(path, ATTR_LOCATION, location); Address addr = manager.getAddressFromLocation(location); - setValue(path, "PC", addr); + setValue(path, ATTR_PC, addr); String rpath = createObject(path + ".Registers"); putRegisters(frame, rpath); - createObject(path + ".Variables"); + insertObject(rpath); + String vpath = createObject(path + ".Variables"); + insertObject(vpath); try { - createObject(frame.thisObject(), "This", path); + String thpath = createObject(frame.thisObject(), "This", path); + insertObject(thpath); } catch (Exception e) { // Ignore @@ -1439,7 +1585,8 @@ public class TraceJdiCommands { public void putLocationContainer(String path, Method m) { try { for (Location loc : m.allLineLocations()) { - createObject(loc, loc.toString(), path); + String lpath = createObject(loc, loc.toString(), path); + insertObject(lpath); } } catch (AbsentInformationException e) { @@ -1450,7 +1597,8 @@ public class TraceJdiCommands { public void putLocationContainer(String path, ReferenceType ref) { try { for (Location loc : ref.allLineLocations()) { - createObject(loc, loc.toString(), path); + String lpath = createObject(loc, loc.toString(), path); + insertObject(lpath); } } catch (AbsentInformationException e) { @@ -1467,29 +1615,32 @@ public class TraceJdiCommands { public void putLocationDetails(String path, Location location) { Address addr = manager.getAddressFromLocation(location); if (isLoaded(location)) { - setValue(path, "_display", manager.key(location.toString()) + ": " + addr); - setValue(path, "Addr", addr); + setValue(path, ATTR_DISPLAY, manager.key(location.toString()) + ": " + addr); + setValue(path, ATTR_ADDRESS, addr); } - setValue(path, "Index", location.codeIndex()); - setValue(path, "Line#", location.lineNumber()); + setValue(path, ATTR_INDEX, location.codeIndex()); + setValue(path, ATTR_LINENO, location.lineNumber()); try { - setValue(path, "Name", location.sourceName()); + setValue(path, ATTR_NAME, location.sourceName()); } catch (AbsentInformationException e) { - // IGNORE + // sourceName is not available. IGNORE } try { setValue(path, "Path", location.sourcePath()); } catch (AbsentInformationException e) { - // IGNORE + // sourcePath is not available. IGNORE } Method method = location.method(); + RmiTraceObject methodObject = proxyObject(method); + if (methodObject == null) { + String ppath = getVmPath(method.virtualMachine()) + ".Classes"; + putReferenceType(ppath, method.declaringType(), true); + } createLink(location, "Method", method); createLink(location, "DeclaringType", location.declaringType()); createLink(location, "ModuleRef", location.declaringType().module()); - //createObject(method, method.name(), path+".Method"); - //putMethodDetails(path, method); } private boolean isLoaded(Location location) { @@ -1513,15 +1664,15 @@ public class TraceJdiCommands { String path = createObject(lv, lv.name(), ppath); putLocalVariableDetails(path, lv); if (value != null) { - putValue(path, "Value", value); - setValue(path, "_display", lv.name() + ": " + value); + putValue(path, ATTR_VALUE, value); + setValue(path, ATTR_DISPLAY, lv.name() + ": " + value); } insertObject(path); } public void putLocalVariableDetails(String path, LocalVariable lv) { try { - putType(path, "Type", lv.type()); + putType(path, ATTR_TYPE, lv.type()); } catch (ClassNotLoadedException e) { // IGNORE @@ -1536,16 +1687,19 @@ public class TraceJdiCommands { setValue(path, "GenericSignature", lv.genericSignature()); } setValue(path, "Signature", lv.signature()); + insertObject(path); } public void putMethodTypeContainer(String ppath, Method m) { try { for (Type type : m.argumentTypes()) { - createObject(type, type.name(), ppath); + String tpath = createObject(type, type.name(), ppath); + insertObject(tpath); } } catch (ClassNotLoadedException e) { - createObject(ppath + "Class Not Loaded"); + String epath = createObject(ppath + "Class Not Loaded"); + insertObject(epath); } } @@ -1553,7 +1707,7 @@ public class TraceJdiCommands { VirtualMachine vm = manager.getJdi().getCurrentVM(); EventRequestManager requestManager = vm.eventRequestManager(); String ppath = getPath(vm) + ".Breakpoints"; - createObject(ppath); + String path = createObject(ppath); Set keys = new HashSet<>(); List brkReqs = requestManager.breakpointRequests(); @@ -1579,13 +1733,14 @@ public class TraceJdiCommands { } retainKeys(ppath, keys); + insertObject(path); } public void putEvents() { VirtualMachine vm = manager.getJdi().getCurrentVM(); EventRequestManager requestManager = vm.eventRequestManager(); String ppath = getPath(vm) + ".Events"; - createObject(ppath); + String path = createObject(ppath); Set keys = new HashSet<>(); List deathReqs = requestManager.vmDeathRequests(); @@ -1669,6 +1824,7 @@ public class TraceJdiCommands { } retainKeys(ppath, keys); + insertObject(path); } // REQUESTS // @@ -1711,13 +1867,14 @@ public class TraceJdiCommands { private void putReqBreakpointDetails(String path, BreakpointRequest req, String key) { Location location = req.location(); - setValue(path, "_display", "[" + key + "] " + location + ":" + location.method().name() + + setValue(path, ATTR_DISPLAY, "[" + key + "] " + location + ":" + location.method().name() + ":" + location.codeIndex()); Address addr = manager.getAddressFromLocation(location); AddressRangeImpl range = new AddressRangeImpl(addr, addr); - setValue(path, "Range", range); - createObject(location, location.toString(), path + ".Location"); - setValue(path, "Enabled", req.isEnabled()); + setValue(path, ATTR_RANGE, range); + String lpath = createObject(location, location.toString(), path + ".Location"); + insertObject(lpath); + setValue(path, ATTR_ENABLED, req.isEnabled()); putFilterDetails(path, req); } @@ -1730,13 +1887,14 @@ public class TraceJdiCommands { private void putReqAccessWatchpointDetails(String path, AccessWatchpointRequest req, String key) { Field field = req.field(); - setValue(path, "_display", "[" + key + "] " + field + ":" + field.declaringType()); + setValue(path, ATTR_DISPLAY, "[" + key + "] " + field + ":" + field.declaringType()); // NB: This isn't correct, but we need a range (any range) AddressRange range = manager.getPoolAddressRange(field.declaringType(), getSize(field.declaringType())); - setValue(path, "Range", range); - createObject(field, field.toString(), path + ".Field"); - setValue(path, "Enabled", req.isEnabled()); + setValue(path, ATTR_RANGE, range); + String fpath = createObject(field, field.toString(), path + ".Field"); + insertObject(fpath); + setValue(path, ATTR_ENABLED, req.isEnabled()); putFilterDetails(path, req); } @@ -1750,13 +1908,14 @@ public class TraceJdiCommands { private void putReqModificationWatchpointDetails(String path, ModificationWatchpointRequest req, String key) { Field field = req.field(); - setValue(path, "_display", "[" + key + "] " + field + ":" + field.declaringType()); + setValue(path, ATTR_DISPLAY, "[" + key + "] " + field + ":" + field.declaringType()); // NB: This isn't correct, but we need a range (any range) AddressRange range = manager.getPoolAddressRange(field.declaringType(), getSize(field.declaringType())); - setValue(path, "Range", range); - createObject(field, field.toString(), path + ".Field"); - setValue(path, "Enabled", req.isEnabled()); + setValue(path, ATTR_RANGE, range); + String fpath = createObject(field, field.toString(), path + ".Field"); + insertObject(fpath); + setValue(path, ATTR_ENABLED, req.isEnabled()); putFilterDetails(path, req); } @@ -1767,7 +1926,7 @@ public class TraceJdiCommands { } private void putReqExceptionDetails(String path, ExceptionRequest req, String key) { - setValue(path, "Enabled", req.isEnabled()); + setValue(path, ATTR_ENABLED, req.isEnabled()); } private void putReqClassLoad(String ppath, ClassPrepareRequest req, String key) { @@ -1777,7 +1936,7 @@ public class TraceJdiCommands { } private void putReqClassLoadDetails(String path, ClassPrepareRequest req, String key) { - setValue(path, "Enabled", req.isEnabled()); + setValue(path, ATTR_ENABLED, req.isEnabled()); putFilterDetails(path, req); } @@ -1788,7 +1947,7 @@ public class TraceJdiCommands { } private void putReqClassUnloadDetails(String path, ClassUnloadRequest req, String key) { - setValue(path, "Enabled", req.isEnabled()); + setValue(path, ATTR_ENABLED, req.isEnabled()); putFilterDetails(path, req); } @@ -1799,7 +1958,7 @@ public class TraceJdiCommands { } private void putReqMethodEntryDetails(String path, MethodEntryRequest req, String key) { - setValue(path, "Enabled", req.isEnabled()); + setValue(path, ATTR_ENABLED, req.isEnabled()); putFilterDetails(path, req); } @@ -1810,7 +1969,7 @@ public class TraceJdiCommands { } private void putReqMethodExitDetails(String path, MethodExitRequest req, String key) { - setValue(path, "Enabled", req.isEnabled()); + setValue(path, ATTR_ENABLED, req.isEnabled()); putFilterDetails(path, req); } @@ -1821,7 +1980,7 @@ public class TraceJdiCommands { } private void putReqStepRequestDetails(String path, StepRequest req, String key) { - setValue(path, "Enabled", req.isEnabled()); + setValue(path, ATTR_ENABLED, req.isEnabled()); putFilterDetails(path, req); } @@ -1873,19 +2032,19 @@ public class TraceJdiCommands { Object property = req.getProperty("Class"); if (property != null) { if (property instanceof ReferenceType reftype) { - setValue(path, "Class", reftype.name()); + setValue(path, ATTR_CLASS, reftype.name()); } } property = req.getProperty("Instance"); if (property != null) { if (property instanceof ObjectReference ref) { - setValue(path, "Instance", ref.toString()); + setValue(path, ATTR_INSTANCE, ref.toString()); } } property = req.getProperty("Thread"); if (property != null) { if (property instanceof ThreadReference ref) { - setValue(path, "Thread", ref.name()); + setValue(path, ATTR_THREAD, ref.name()); } } } @@ -1946,7 +2105,7 @@ public class TraceJdiCommands { public void ghidraTraceSyncEnable() { try (RmiTransaction tx = state.trace.startTx("ghidraTraceSyncEnable", false)) { - TraceJdiHooks hooks = manager.getHooks(); + JdiHooks hooks = manager.getHooks(); hooks.installHooks(); hooks.enableCurrentVM(); } @@ -1981,6 +2140,28 @@ public class TraceJdiCommands { } } + public void execute(ClassType ct, ThreadReference thread, Method method, List args, + int options) { + try { + Value val = ct.invokeMethod(thread, method, args, options); + System.err.println(val); + } + catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } + + public void execute(ObjectReference ref, ThreadReference thread, Method method, + List args, int options) { + try { + Value val = ref.invokeMethod(thread, method, args, options); + System.out.println(val); + } + catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } + private int getSize(ReferenceType reftype) { byte[] cp = reftype.constantPool(); int sz = 1; @@ -1998,7 +2179,7 @@ public class TraceJdiCommands { return manager.pathForObj(obj); } - RmiTraceObject proxyObject(Object obj) { + public RmiTraceObject proxyObject(Object obj) { String path = getPath(obj); return path == null ? null : RmiTraceObject.fromPath(state.trace, path); } @@ -2038,7 +2219,8 @@ public class TraceJdiCommands { if (child instanceof Method m) { key = m.name(); } - createObject(child, key, ppath + "." + label); + String lpath = createObject(child, key, ppath + "." + label); + insertObject(lpath); } } @@ -2070,12 +2252,13 @@ public class TraceJdiCommands { String shortName = vm.name().substring(0, vm.name().indexOf(" ")); name = currentEvent == null ? shortName : shortName + " [" + currentEvent + "]"; } - setValue(path, TraceJdiManager.ACCESSIBLE_ATTRIBUTE_NAME, suspended); + setValue(path, ATTR_ACCESSIBLE, suspended); String annotation = suspended ? "(S)" : "(R)"; - setValue(path, TraceJdiManager.DISPLAY_ATTRIBUTE_NAME, name + " " + annotation); + setValue(path, ATTR_DISPLAY, name + " " + annotation); String tstate = suspended ? "STOPPED" : "RUNNING"; - setValue(path, TraceJdiManager.STATE_ATTRIBUTE_NAME, tstate); + setValue(path, ATTR_STATE, tstate); stopped |= suspended; return stopped; } + } diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiHooks.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiHooks.java similarity index 87% rename from Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiHooks.java rename to Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiHooks.java index 3375788ab1..e5e5a8484f 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiHooks.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiHooks.java @@ -15,47 +15,35 @@ */ package ghidra.dbg.jdi.rmi.jpda; +import static ghidra.dbg.jdi.rmi.jpda.JdiManager.*; + import java.util.*; import com.sun.jdi.*; import com.sun.jdi.event.*; -import ghidra.app.plugin.core.debug.client.tracermi.RmiTrace; -import ghidra.app.plugin.core.debug.client.tracermi.RmiTransaction; +import ghidra.app.plugin.core.debug.client.tracermi.*; import ghidra.dbg.jdi.manager.*; import ghidra.dbg.jdi.manager.impl.DebugStatus; import ghidra.dbg.jdi.manager.impl.JdiManagerImpl; class HookState { - private TraceJdiCommands cmds; - private Object batch; + private JdiCommands cmds; - public HookState(TraceJdiCommands cmds) { + public HookState(JdiCommands cmds) { this.cmds = cmds; - this.batch = null; } - public void ensureBatch() { - if (batch == null) { - batch = cmds.state.client.startBatch(); - } + public RmiBatch batch() { + return cmds.state.client.startBatch(); } - - public void endBatch() { - if (batch == null) { - return; - } - batch = null; - cmds.state.client.endBatch(); - } - } class VmState { - private TraceJdiManager manager; - private TraceJdiCommands cmds; + private JdiManager manager; + private JdiCommands cmds; private boolean firstPass; boolean classes; boolean modules; @@ -65,7 +53,7 @@ class VmState { boolean events; Set visited; - public VmState(TraceJdiManager manager) { + public VmState(JdiManager manager) { this.manager = manager; this.cmds = manager.getCommands(); this.firstPass = true; @@ -109,9 +97,14 @@ class VmState { } StackFrame frame = manager.getJdi().getCurrentFrame(); if (frame != null) { - if (first || !visited.contains(frame)) { - cmds.putReg(frame); - visited.add(frame); + try { + if (first || !visited.contains(frame)) { + cmds.putReg(frame); + visited.add(frame); + } + } + catch (InvalidStackFrameException e) { + manager.getJdi().setCurrentFrame(null); } } } @@ -154,7 +147,7 @@ class VmState { Process proc = vm.process(); String path = cmds.getPath(proc); if (path != null) { - cmds.setValue(path, "Alive", proc.isAlive()); + cmds.setValue(path, ATTR_ALIVE, proc.isAlive()); } setState(vm); } @@ -168,8 +161,8 @@ class VmState { if (process != null) { exitCode = process.exitValue(); String procpath = cmds.getPath(vm.process()); - cmds.setValue(procpath, "ExitCode", exitCode); - cmds.setValue(procpath, TraceJdiManager.STATE_ATTRIBUTE_NAME, "TERMINATED"); + cmds.setValue(procpath, ATTR_EXIT_CODE, exitCode); + cmds.setValue(procpath, ATTR_STATE, "TERMINATED"); } } catch (IllegalThreadStateException e) { @@ -178,25 +171,25 @@ class VmState { if (description != null) { cmds.state.trace.snapshot(description, "", null); } - cmds.setValue(path, "ExitCode", exitCode); - cmds.setValue(path, TraceJdiManager.STATE_ATTRIBUTE_NAME, "TERMINATED"); + cmds.setValue(path, ATTR_EXIT_CODE, exitCode); + cmds.setValue(path, ATTR_STATE, "TERMINATED"); } } -public class TraceJdiHooks implements JdiEventsListenerAdapter { +public class JdiHooks implements JdiEventsListenerAdapter { - private TraceJdiManager manager; - private TraceJdiCommands cmds; + private JdiManager manager; + private JdiCommands cmds; private HookState hookState; private Map vmStates = new HashMap<>(); - public TraceJdiHooks(TraceJdiManager manager) { + public JdiHooks(JdiManager manager, JdiCommands cmds) { this.manager = manager; - this.cmds = manager.getCommands(); + this.cmds = cmds; } - private void setCommands(TraceJdiCommands commands) { + private void setCommands(JdiCommands commands) { this.cmds = commands; hookState = new HookState(commands); } @@ -204,17 +197,19 @@ public class TraceJdiHooks implements JdiEventsListenerAdapter { @Override public DebugStatus vmStarted(VMStartEvent event, JdiCause cause) { setCommands(manager.getCommands()); - hookState.ensureBatch(); - RmiTrace trace = cmds.state.trace; JdiManagerImpl jdi = manager.getJdi(); VirtualMachine vm = event == null ? jdi.getCurrentVM() : event.virtualMachine(); - try (RmiTransaction tx = trace.openTx("New VM " + vm.description())) { - jdi.setCurrentVM(vm); - jdi.addVM(vm); + jdi.setCurrentVM(vm); + jdi.addVM(vm); + RmiTrace trace = cmds.state.trace; + if (trace == null) { + return DebugStatus.NO_CHANGE; + } + try (RmiBatch batch = hookState.batch(); + RmiTransaction tx = trace.openTx("New VM " + vm.description())) { cmds.putVMs(); enableCurrentVM(); } - hookState.endBatch(); return DebugStatus.NO_CHANGE; } @@ -417,12 +412,11 @@ public class TraceJdiHooks implements JdiEventsListenerAdapter { } VmState state = vmStates.get(vm); state.visited.clear(); - hookState.ensureBatch(); - try (RmiTransaction tx = trace.openTx("Stopped")) { + try (RmiBatch batch = hookState.batch(); + RmiTransaction tx = trace.openTx("Stopped")) { state.recordState("Stopped"); cmds.activate(null); } - hookState.endBatch(); } private void setCurrent(Event event) { @@ -446,12 +440,11 @@ public class TraceJdiHooks implements JdiEventsListenerAdapter { void onContinue() { VirtualMachine currentVM = manager.getJdi().getCurrentVM(); VmState state = vmStates.get(currentVM); - hookState.ensureBatch(); - try (RmiTransaction tx = cmds.state.trace.openTx("Continue")) { + try (RmiBatch batch = hookState.batch(); + RmiTransaction tx = cmds.state.trace.openTx("Continue")) { state.recordStateContinued(currentVM); cmds.activate(null); } - hookState.endBatch(); } public void installHooks() { diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiManager.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java similarity index 57% rename from Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiManager.java rename to Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java index eec55960f5..ce7ba8a173 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiManager.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java @@ -21,6 +21,7 @@ import java.util.Map; import com.sun.jdi.*; import ghidra.app.plugin.core.debug.client.tracermi.*; +import ghidra.app.plugin.core.debug.client.tracermi.RmiMethodRegistry.TraceMethod; import ghidra.dbg.jdi.manager.impl.DebugStatus; import ghidra.dbg.jdi.manager.impl.JdiManagerImpl; import ghidra.dbg.target.schema.EnumerableTargetObjectSchema; @@ -28,65 +29,87 @@ import ghidra.dbg.target.schema.TargetObjectSchema; import ghidra.program.model.address.*; import ghidra.util.Msg; -public class TraceJdiManager { +public class JdiManager { private static final int STATIC_METHOD_SEPARATION = 3; public static final long BLOCK_SIZE = 0x1000L; public static final long DEFAULT_SECTION = 0x0000L; - public static final String PREFIX_INVISIBLE = "_"; - public static final String DISPLAY_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "display"; - public static final String STATE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "state"; - public static final String MODULE_NAME_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "module_name"; - public static final String ARCH_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "arch"; - public static final String DEBUGGER_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "debugger"; - public static final String OS_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "os"; - public static final String ENDIAN_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "endian"; - public static final String ACCESSIBLE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "accessible"; + public static final String ATTR_DISPLAY = "_display"; + public static final String ATTR_STATE = "_state"; + public static final String ATTR_MODULE_NAME = "_module_name"; + public static final String ATTR_ARCH = "_arch"; + public static final String ATTR_DEBUGGER = "_debugger"; + public static final String ATTR_OS = "_os"; + public static final String ATTR_ENDIAN = "_endian"; + public static final String ATTR_ACCESSIBLE = "_accessible"; + public static final String ATTR_ADDRESS = "Address"; + public static final String ATTR_ALIVE = "Alive"; + public static final String ATTR_CLASS = "Class"; + public static final String ATTR_COMMAND_LINE = "CommandLine"; + public static final String ATTR_COUNT = "Count"; + public static final String ATTR_ENABLED = "Enabled"; + public static final String ATTR_EXECUTABLE = "Executable"; + public static final String ATTR_EXIT_CODE = "ExitCode"; + public static final String ATTR_INDEX = "Index"; + public static final String ATTR_INSTANCE = "Instance"; + public static final String ATTR_LENGTH = "Length"; + public static final String ATTR_LINENO = "LineNo"; + public static final String ATTR_LOCATION = "Location"; + public static final String ATTR_NAME = "Name"; + public static final String ATTR_PC = "PC"; + public static final String ATTR_PLATFORM_ONLY = "PlatformOnly"; + public static final String ATTR_RANGE = "Range"; + public static final String ATTR_RANGE_CP = "RangeCP"; // Constant pool + public static final String ATTR_SIGNATURE = "Signature"; + public static final String ATTR_THREAD = "Thread"; + public static final String ATTR_TYPE = "Type"; + public static final String ATTR_VALUE = "Value"; + public static final String ATTR_EXCLUDE = "Exclude"; + public static final String ATTR_INCLUDE = "Include"; - private JdiManagerImpl manager; - private TraceJdiArch arch; - private TraceJdiHooks hooks; - private TraceJdiMethods methods; - private TraceJdiCommands commands; + private final JdiManagerImpl manager; + private final JdiArch arch; + private final JdiHooks hooks; + private final JdiMethods methods; + private final JdiCommands commands; - Map objectRegistry = new HashMap<>(); - Map reverseRegistry = new HashMap<>(); - RmiMethodRegistry remoteMethodRegistry = new RmiMethodRegistry(); - Map scopeRegistry = new HashMap<>(); + final Map objectRegistry = new HashMap<>(); + final Map reverseRegistry = new HashMap<>(); + final RmiMethodRegistry remoteMethodRegistry = new RmiMethodRegistry(); + final Map scopeRegistry = new HashMap<>(); protected final AddressSpace ram = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 0); - protected Long ramIndex = Long.valueOf(BLOCK_SIZE); + protected Long ramIndex = BLOCK_SIZE; protected final AddressSpace pool = new GenericAddressSpace("constantPool", 64, AddressSpace.TYPE_RAM, 0); - protected Long poolIndex = Long.valueOf(0x0L); - public AddressRangeImpl defaultRange; + protected Long poolIndex = 0x0L; + public final AddressRangeImpl defaultRange; - private Map addressRangeByMethod = new HashMap<>(); - private Map methodsByKey = new HashMap<>(); - private Map addressRangeByClass = new HashMap<>(); - private Map cpAddressRangeByClass = new HashMap<>(); + private final Map addressRangeByMethod = new HashMap<>(); + private final Map methodsByKey = new HashMap<>(); + private final Map addressRangeByClass = new HashMap<>(); + private final Map cpAddressRangeByClass = new HashMap<>(); - private Map returnStatusMap = new HashMap<>(); - TargetObjectSchema rootSchema; + private final Map returnStatusMap = new HashMap<>(); + final TargetObjectSchema rootSchema; - public TraceJdiManager(JdiManagerImpl manager, Map env) { + public JdiManager(JdiManagerImpl manager, Map env) { this(manager); commands.ghidraTraceConnect(env.get("GHIDRA_TRACE_RMI_ADDR")); commands.ghidraTraceStart(env.get("OPT_TARGET_CLASS")); } - // NB: Needed for testing - public TraceJdiManager(JdiManagerImpl manager) { + public JdiManager(JdiManagerImpl manager) { this.manager = manager; Address start = ram.getAddress(DEFAULT_SECTION); defaultRange = new AddressRangeImpl(start, start.add(BLOCK_SIZE - 1)); rootSchema = RmiClient.loadSchema("jdi_schema.xml", "Debugger"); - arch = new TraceJdiArch(); - commands = new TraceJdiCommands(this); // Must precede methods/hooks - methods = new TraceJdiMethods(this); - hooks = new TraceJdiHooks(this); + arch = new JdiArch(); + commands = new JdiCommands(this); // Must precede methods/hooks + methods = new JdiMethods(this, commands); + hooks = new JdiHooks(this, commands); hooks.installHooks(); } @@ -94,19 +117,19 @@ public class TraceJdiManager { return manager; } - public TraceJdiArch getArch() { + public JdiArch getArch() { return arch; } - public TraceJdiCommands getCommands() { + public JdiCommands getCommands() { return commands; } - public TraceJdiMethods getMethods() { + public JdiMethods getMethods() { return methods; } - public TraceJdiHooks getHooks() { + public JdiHooks getHooks() { return hooks; } @@ -114,12 +137,11 @@ public class TraceJdiManager { return commands.state.client; } - public void registerRemoteMethod(TraceJdiMethods methods, java.lang.reflect.Method m, - String name) { + public void registerRemoteMethod(JdiMethods methods, java.lang.reflect.Method m, String name) { String action = name; String display = name; String description = name; - RmiMethodRegistry.TraceMethod annot = m.getAnnotation(RmiMethodRegistry.TraceMethod.class); + TraceMethod annot = m.getAnnotation(TraceMethod.class); if (annot == null) { return; } @@ -134,6 +156,10 @@ public class TraceJdiManager { if (pcount < 1) { return; } + /** + * TODO: The return type should be reflected from the method; however, none of the parameter + * collection routines currently use the return type, so just use ANY for now. + */ TargetObjectSchema schema = EnumerableTargetObjectSchema.ANY; RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, action, display, description, schema, methods, m); @@ -152,27 +178,33 @@ public class TraceJdiManager { } public AddressRange putAddressRange(ReferenceType cls, AddressSet set) { - if (set.isEmpty()) { - addressRangeByClass.put(cls, defaultRange); - return defaultRange; + synchronized (addressRangeByClass) { + if (set.isEmpty()) { + addressRangeByClass.put(cls, defaultRange); + return defaultRange; + } + AddressRange range = new AddressRangeImpl(set.getMinAddress(), set.getMaxAddress()); + addressRangeByClass.put(cls, range); + return range; } - AddressRange range = new AddressRangeImpl(set.getMinAddress(), set.getMaxAddress()); - addressRangeByClass.put(cls, range); - return range; } public AddressRange getAddressRange(ReferenceType cls) { if (cls == null) { return defaultRange; } - return addressRangeByClass.get(cls); + synchronized (addressRangeByClass) { + return addressRangeByClass.get(cls); + } } public ReferenceType getReferenceTypeForAddress(Address address) { - for (ReferenceType ref : addressRangeByClass.keySet()) { - AddressRange range = addressRangeByClass.get(ref); - if (range.contains(address)) { - return ref; + synchronized (addressRangeByClass) { + for (ReferenceType ref : addressRangeByClass.keySet()) { + AddressRange range = addressRangeByClass.get(ref); + if (range.contains(address)) { + return ref; + } } } return null; @@ -182,19 +214,21 @@ public class TraceJdiManager { if (cls == null) { return defaultRange; } - AddressRange range = cpAddressRangeByClass.get(cls); - if (range == null) { - registerConstantPool(cls, sz); - range = cpAddressRangeByClass.get(cls); + synchronized (cpAddressRangeByClass) { + AddressRange range = cpAddressRangeByClass.get(cls); + if (range == null) { + registerConstantPool(cls, sz); + range = cpAddressRangeByClass.get(cls); + } + return range; } - return range; } public void registerConstantPool(ReferenceType declaringType, int sz) { - if (!cpAddressRangeByClass.containsKey(declaringType)) { - if (manager.getCurrentVM().canGetConstantPool()) { - long length = sz == 0 ? 2 : sz; - synchronized (cpAddressRangeByClass) { + synchronized (cpAddressRangeByClass) { + if (!cpAddressRangeByClass.containsKey(declaringType)) { + if (manager.getCurrentVM().canGetConstantPool()) { + long length = sz == 0 ? 2 : sz; Address start = pool.getAddress(poolIndex); AddressRangeImpl range = new AddressRangeImpl(start, start.add(length - 1)); @@ -208,10 +242,12 @@ public class TraceJdiManager { } public ReferenceType getReferenceTypeForPoolAddress(Address address) { - for (ReferenceType ref : cpAddressRangeByClass.keySet()) { - AddressRange range = cpAddressRangeByClass.get(ref); - if (range.contains(address)) { - return ref; + synchronized (cpAddressRangeByClass) { + for (ReferenceType ref : cpAddressRangeByClass.keySet()) { + AddressRange range = cpAddressRangeByClass.get(ref); + if (range.contains(address)) { + return ref; + } } } return null; @@ -221,21 +257,25 @@ public class TraceJdiManager { if (method == null) { return defaultRange; } - AddressRange range = addressRangeByMethod.get(methodToKey(method)); - if (range == null) { - return defaultRange; + synchronized (addressRangeByMethod) { + AddressRange range = addressRangeByMethod.get(methodToKey(method)); + if (range == null) { + return defaultRange; + } + return range; } - return range; } public Method getMethodForAddress(Address address) { - for (String methodName : addressRangeByMethod.keySet()) { - AddressRange range = addressRangeByMethod.get(methodName); - if (range.contains(address)) { - return methodsByKey.get(methodName); + synchronized (addressRangeByMethod) { + for (String methodName : addressRangeByMethod.keySet()) { + AddressRange range = addressRangeByMethod.get(methodName); + if (range.contains(address)) { + return methodsByKey.get(methodName); + } } + return null; } - return null; } public Address getAddressFromLocation(Location location) { @@ -364,5 +404,4 @@ public class TraceJdiManager { } } } - } diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiMethods.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java similarity index 53% rename from Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiMethods.java rename to Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java index 46771fdb6a..1ed7dabdfc 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/TraceJdiMethods.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java @@ -15,6 +15,8 @@ */ package ghidra.dbg.jdi.rmi.jpda; +import static ghidra.dbg.jdi.rmi.jpda.JdiManager.*; + import java.io.IOException; import java.util.*; @@ -22,47 +24,36 @@ import com.sun.jdi.*; import com.sun.jdi.request.*; import ghidra.app.plugin.core.debug.client.tracermi.*; -import ghidra.dbg.target.TargetMethod; +import ghidra.app.plugin.core.debug.client.tracermi.RmiMethodRegistry.TraceMethod; +import ghidra.dbg.target.TargetMethod.Param; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRange; import ghidra.rmi.trace.TraceRmi.MemoryState; import ghidra.util.Msg; -public class TraceJdiMethods implements RmiMethods { +public class JdiMethods implements RmiMethods { - private TraceJdiManager manager; - private TraceJdiCommands cmds; + private JdiManager manager; + private JdiCommands cmds; - public TraceJdiMethods(TraceJdiManager manager) { + public JdiMethods(JdiManager manager, JdiCommands cmds) { this.manager = manager; - this.cmds = manager.getCommands(); + this.cmds = cmds; registerMethods(); } public void registerMethods() { Class cls = this.getClass(); for (java.lang.reflect.Method m : cls.getMethods()) { - RmiMethodRegistry.TraceMethod annot = - m.getAnnotation(RmiMethodRegistry.TraceMethod.class); + TraceMethod annot = m.getAnnotation(TraceMethod.class); if (annot != null) { manager.registerRemoteMethod(this, m, m.getName()); } } } -// public void execute(String cmd) { -// -// } - -// public void refresh_available(Object obj) { -// -// } - - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh VM", - schema = "VirtualMachine") - public void refresh_vm(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh VM") + public void refresh_vm(@Param(schema = "VirtualMachine", name = "vm") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshVM")) { String path = obj.getPath(); VirtualMachine vm = (VirtualMachine) getObjectFromPath(path); @@ -70,11 +61,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh process", - schema = "ProcessRef") - public void refresh_process(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh process") + public void refresh_process( + @Param(schema = "ProcessRef", name = "process") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshProcess")) { String path = obj.getPath(); Process proc = (Process) getObjectFromPath(path); @@ -82,11 +71,11 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh thread groups", - schema = "ThreadGroupReferenceContainer") - public void refresh_thread_groups(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh thread groups") + public void refresh_thread_groups( + @Param( + schema = "ThreadGroupReferenceContainer", + name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreadGroups")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -100,11 +89,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh thread group", - schema = "ThreadGroupReferenceProxy") - public void refresh_thread_group_proxy(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh thread group") + public void refresh_thread_group_proxy( + @Param(schema = "ThreadGroupReferenceProxy", name = "proxy") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreadGroup")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -118,11 +105,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh thread group", - schema = "ThreadGroupReference") - public void refresh_thread_group(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh thread group") + public void refresh_thread_group( + @Param(schema = "ThreadGroupReference", name = "group") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreadGroup")) { String path = obj.getPath(); ThreadGroupReference ref = (ThreadGroupReference) getObjectFromPath(path); @@ -130,11 +115,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh threads", - schema = "ThreadContainer") - public void refresh_threads(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh threads") + public void refresh_threads( + @Param(schema = "ThreadContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreads")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -143,11 +126,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh threads", - schema = "ThreadReferenceContainer") - public void refresh_threadrefs(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh threads") + public void refresh_threadrefs( + @Param(schema = "ThreadReferenceContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThreads")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -166,11 +147,8 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh Thread", - schema = "Thread") - public void refresh_thread(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh thread") + public void refresh_thread(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshThread")) { String path = obj.getPath(); ThreadReference ref = (ThreadReference) getObjectFromPath(path); @@ -178,38 +156,32 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod(action = "refresh", display = "Refresh Stack", schema = "Stack") - public void refresh_stack(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh stack") + public void refresh_stack(@Param(schema = "Stack", name = "stack") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshStack")) { cmds.ghidraTracePutFrames(); } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh registers", - schema = "RegisterContainer") - public void refresh_registers(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh registers") + public void refresh_registers( + @Param(schema = "RegisterContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshRegisters")) { cmds.ghidraTracePutFrames(); } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh modules", - schema = "ModuleReferenceContainer") - public void refresh_modules(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh modules") + public void refresh_modules( + @Param(schema = "ModuleReferenceContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshModules")) { cmds.putModuleReferenceContainer(); } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh module", - schema = "ModuleReference") - public void refresh_module(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh module") + public void refresh_module( + @Param(schema = "ModuleReference", name = "module") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshModule")) { String path = obj.getPath(); ModuleReference ref = (ModuleReference) getObjectFromPath(path); @@ -217,11 +189,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh monitor info", - schema = "MonitorInfoContainer") - public void refresh_monitors(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh monitor info") + public void refresh_monitors( + @Param(schema = "MonitorInfoContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshMonitorInfo")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -233,11 +203,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh monitor info", - schema = "MonitorInfo") - public void refresh_monitor_info(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh monitor info") + public void refresh_monitor_info( + @Param(schema = "MonitorInfo", name = "monitor_info") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshMonitorInfo")) { String path = obj.getPath(); MonitorInfo mi = (MonitorInfo) getObjectFromPath(path); @@ -245,11 +213,15 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh fields", - schema = "FieldContainer") - public void refresh_fields(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh fields") + public void refresh_canonical_fields( + @Param(schema = "CanonicalFieldContainer", name = "container") RmiTraceObject obj) { + refresh_fields(obj); + } + + @TraceMethod(action = "refresh", display = "Refresh fields") + public void refresh_fields( + @Param(schema = "FieldContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshFields")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -263,20 +235,9 @@ public class TraceJdiMethods implements RmiMethods { } } -// @RmiMethodRegistry.method(action = "refresh", display = "Refresh Field", schema = "Field") -// public void refresh_field(RmiTraceObject obj) { -// try (RmiTransaction tx = cmds.state.trace.openTx("RefreshField")) { -// String path = obj.getPath(); -// Field field = (Field) getObjectFromPath(path); -// cmds.putFieldDetails(path, field); -// } -// } - - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh objects", - schema = "ObjectReferenceContainer") - public void refresh_objects(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh objects") + public void refresh_objects( + @Param(schema = "ObjectReferenceContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshObjects")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -300,11 +261,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh object", - schema = "ObjectReferenceProxy") - public void refresh_object_proxy(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh object") + public void refresh_object_proxy( + @Param(schema = "ObjectReferenceProxy", name = "proxy") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshObject")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -324,11 +283,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh object", - schema = "ObjectReference") - public void refresh_object(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh object") + public void refresh_object( + @Param(schema = "ObjectReference", name = "object") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshInstance")) { String path = obj.getPath(); ObjectReference method = (ObjectReference) getObjectFromPath(path); @@ -336,11 +293,15 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh methods", - schema = "MethodContainer") - public void refresh_methods(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh methods") + public void refresh_canonical_methods( + @Param(schema = "CanonicalMethodContainer", name = "container") RmiTraceObject obj) { + refresh_methods(obj); + } + + @TraceMethod(action = "refresh", display = "Refresh methods") + public void refresh_methods( + @Param(schema = "MethodContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshMethods")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -349,11 +310,8 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh method", - schema = "Method") - public void refresh_method(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh method") + public void refresh_method(@Param(schema = "Method", name = "method") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshMethod")) { String path = obj.getPath(); Method method = (Method) getObjectFromPath(path); @@ -361,11 +319,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh arguments", - schema = "ArgumentContainer") - public void refresh_arguments(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh arguments") + public void refresh_arguments( + @Param(schema = "ArgumentContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshArguments")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -374,12 +330,14 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "load_class", - display = "Load class", - schema = "ReferenceTypeContainer") - public void find_class(RmiTraceObject obj, - @TargetMethod.Param( + @TraceMethod(display = "Load class") + public boolean find_class( + @Param( + schema = "ReferenceTypeContainer", + description = "Container", + display = "Container", + name = "container") RmiTraceObject obj, + @Param( description = "Class to open", display = "Class", name = "find") String targetClass) { @@ -388,24 +346,34 @@ public class TraceJdiMethods implements RmiMethods { String ppath = cmds.getParentPath(path); Object parent = getObjectFromPath(ppath); if (parent instanceof VirtualMachine vm) { - cmds.loadReferenceType(path, vm.allClasses(), targetClass); + return cmds.loadReferenceType(path, vm.allClasses(), targetClass); } + return false; } } - @RmiMethodRegistry.TraceMethod( - action = "refresh_memory", - display = "Refresh memory", - schema = "Memory") - public void refresh_memory(RmiTraceObject obj) { + /** + * NB. Did not assign action="refresh" because this method is expensive. Assigning that action + * name will cause the UI to do it upon expanding the node, which we do not want. + */ + @TraceMethod(display = "Refresh memory") + public void refresh_memory(@Param(schema = "Memory", name = "memory") RmiTraceObject obj) { refresh_reference_types(obj); } - @RmiMethodRegistry.TraceMethod( - action = "refresh_types", - display = "Refresh reference types", - schema = "ReferenceTypeContainer") - public void refresh_reference_types(RmiTraceObject obj) { + @TraceMethod(display = "Refresh reference types") + public void refresh_canonical_reference_types( + @Param(schema = "CanonicalReferenceTypeContainer", name = "container") RmiTraceObject obj) { + refresh_reference_types(obj); + } + + /** + * NB. Did not assign action="refresh" because this method is expensive. Assigning that action + * name will cause the UI to do it upon expanding the node, which we do not want. + */ + @TraceMethod(display = "Refresh reference types") + public void refresh_reference_types( + @Param(schema = "ReferenceTypeContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshReferenceTypes")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -446,11 +414,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh reference type", - schema = "ReferenceTypeProxy") - public void refresh_reference_type_proxy(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh reference type") + public void refresh_reference_type_proxy( + @Param(schema = "ReferenceTypeProxy", name = "proxy") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshReferenceType")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -470,11 +436,15 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh reference type", - schema = "ReferenceType") - public void refresh_reference_type(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh reference type") + public void refresh_canonical_reference_type( + @Param(schema = "CanonicalReferenceType", name = "container") RmiTraceObject obj) { + refresh_reference_type(obj); + } + + @TraceMethod(action = "refresh", display = "Refresh reference type") + public void refresh_reference_type( + @Param(schema = "ReferenceType", name = "reference_type") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshReferenceType")) { String path = obj.getPath(); ReferenceType refType = (ReferenceType) getObjectFromPath(path); @@ -482,11 +452,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "load", - display = "Load reference", - schema = "ReferenceType") - public void load_reftype(RmiTraceObject obj) { + @TraceMethod(display = "Load reference type") + public void load_reftype( + @Param(schema = "ReferenceType", name = "reference_type") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshReferenceType")) { VirtualMachine vm = manager.getJdi().getCurrentVM(); String path = obj.getPath(); @@ -496,11 +464,15 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh variables", - schema = "VariableContainer") - public void refresh_variables(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh variables") + public void refresh_canonical_variables( + @Param(schema = "CanonicalVariableContainer", name = "container") RmiTraceObject obj) { + refresh_variables(obj); + } + + @TraceMethod(action = "refresh", display = "Refresh variables") + public void refresh_variables( + @Param(schema = "VariableContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshVariables")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -525,11 +497,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh variable", - schema = "Variable") - public void refresh_variable(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh variable") + public void refresh_variable( + @Param(schema = "Variable", name = "variable") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshVariable")) { String path = obj.getPath(); Object object = getObjectFromPath(path); @@ -539,11 +509,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh locations", - schema = "LocationContainer") - public void refresh_locations(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh locations") + public void refresh_locations( + @Param(schema = "LocationContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocations")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -559,11 +527,9 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh location", - schema = "Location") - public void refresh_location(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh location") + public void refresh_location( + @Param(schema = "Location", name = "location") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocation")) { String path = obj.getPath(); Location loc = (Location) getObjectFromPath(path); @@ -571,31 +537,25 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh breakpoints", - schema = "BreakpointContainer") - public void refresh_breakpoints(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh breakpoints") + public void refresh_breakpoints( + @Param(schema = "BreakpointContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshBreakpoints")) { cmds.putBreakpoints(); } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh events", - schema = "EventContainer") - public void refresh_events(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh events") + public void refresh_events( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshEvents")) { cmds.putEvents(); } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh values", - schema = "ValueContainer") - public void refresh_values(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh values") + public void refresh_values( + @Param(schema = "ValueContainer", name = "container") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshValues")) { String path = obj.getPath(); String ppath = cmds.getParentPath(path); @@ -606,11 +566,8 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "refresh", - display = "Refresh value", - schema = "Value") - public void refresh_value(RmiTraceObject obj) { + @TraceMethod(action = "refresh", display = "Refresh value") + public void refresh_value(@Param(schema = "Value", name = "value") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocation")) { String path = obj.getPath(); Value val = (Value) getObjectFromPath(path); @@ -618,11 +575,17 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "set", - display = "Set value", - schema = "Variable") - public void set_value_lvar(RmiTraceObject obj, String value) { + @TraceMethod(display = "Set value") + public void set_value_lvar( + @Param( + schema = "Variable", + description = "Variable", + display = "Variable", + name = "variable") RmiTraceObject obj, + @Param( + description = "Value", + display = "Value", + name = "value") String value) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocation")) { String path = obj.getPath(); LocalVariable lvar = (LocalVariable) getObjectFromPath(path); @@ -630,11 +593,17 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "set", - display = "Set value", - schema = "Field") - public void set_value_field(RmiTraceObject obj, String value) { + @TraceMethod(display = "Set value") + public void set_value_field( + @Param( + schema = "Field", + description = "Field", + display = "Field", + name = "field") RmiTraceObject obj, + @Param( + description = "Value", + display = "Value", + name = "value") String value) { try (RmiTransaction tx = cmds.state.trace.openTx("RefreshLocation")) { String path = obj.getPath(); Field field = (Field) getObjectFromPath(path); @@ -642,16 +611,18 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod(action = "activate", display = "Activate", schema = "ANY") - public void activate(RmiTraceObject obj) { + @TraceMethod(action = "activate", display = "Activate") + public void activate(@Param(schema = "OBJECT", name = "object") RmiTraceObject obj) { try (RmiTransaction tx = cmds.state.trace.openTx("Activate")) { String path = obj.getPath(); cmds.activate(path); } } - @RmiMethodRegistry.TraceMethod(action = "kill", display = "Kill", schema = "VirtualMachine") - public void kill(RmiTraceObject obj) { + @TraceMethod(action = "kill", display = "Terminate") + public void kill(@Param(schema = "VirtualMachine", name = "vm") RmiTraceObject obj) { + VirtualMachine vm = (VirtualMachine) getObjectFromPath(obj.getPath()); + vm.exit(143); try { manager.getJdi().sendInterruptNow(); } @@ -660,52 +631,40 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod(action = "resume", display = "Resume", schema = "VirtualMachine") - public void resume_vm(RmiTraceObject obj) { + @TraceMethod(action = "resume", display = "Resume") + public void resume_vm(@Param(schema = "VirtualMachine", name = "vm") RmiTraceObject obj) { VirtualMachine vm = (VirtualMachine) getObjectFromPath(obj.getPath()); vm.resume(); manager.getHooks().setState(vm); } - @RmiMethodRegistry.TraceMethod(action = "resume", display = "Resume", schema = "Thread") - public void resume(RmiTraceObject obj) { + @TraceMethod(action = "resume", display = "Resume") + public void resume_thread(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { ThreadReference thread = (ThreadReference) getObjectFromPath(obj.getPath()); thread.resume(); manager.getHooks().setState(thread.virtualMachine()); } - @RmiMethodRegistry.TraceMethod( - action = "suspend", - display = "Suspend", - schema = "VirtualMachine") - public void suspend(RmiTraceObject obj) { - Object object = getObjectFromPath(obj.getPath()); - if (object instanceof ThreadReference thread) { - thread.suspend(); - manager.getHooks().setState(thread.virtualMachine()); - } - else { - VirtualMachine vm = manager.getJdi().getCurrentVM(); - vm.suspend(); - manager.getHooks().setState(vm); - } + @TraceMethod(action = "interrupt", display = "Suspend") + public void suspend_vm(@Param(schema = "VirtualMachine", name = "vm") RmiTraceObject obj) { + VirtualMachine vm = (VirtualMachine) getObjectFromPath(obj.getPath()); + vm.suspend(); + manager.getHooks().setState(vm); } - @RmiMethodRegistry.TraceMethod( - action = "interrupt", - display = "Interrupt", - schema = "VirtualMachine") - public void interrupt(RmiTraceObject obj) { - suspend(obj); + @TraceMethod(action = "interrupt", display = "Suspend") + public void suspend_thread(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { + ThreadReference thread = (ThreadReference) getObjectFromPath(obj.getPath()); + thread.suspend(); + manager.getHooks().setState(thread.virtualMachine()); } - // NB: For the VirtualMachine, the step methods add requests for break-on-step for all threads. - // These requests will remain pending until the VM is resumed. - @RmiMethodRegistry.TraceMethod( - action = "step_into", - display = "Step into", - schema = "VirtualMachine") - public void step_vm_into(RmiTraceObject obj) { + /** + * NB: For the VirtualMachine, the step methods add requests for break-on-step for all threads. + * These requests will remain pending until the VM is resumed. + */ + @TraceMethod(action = "step_into", display = "Step into") + public void step_vm_into(@Param(schema = "VirtualMachine", name = "vm") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); List threads = getThreadsFromValue(obj); for (ThreadReference thread : threads) { @@ -722,11 +681,8 @@ public class TraceJdiMethods implements RmiMethods { vm.resume(); } - @RmiMethodRegistry.TraceMethod( - action = "step_over", - display = "Step over", - schema = "VirtualMachine") - public void step_vm_over(RmiTraceObject obj) { + @TraceMethod(action = "step_over", display = "Step over") + public void step_vm_over(@Param(schema = "VirtualMachine", name = "vm") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); List threads = getThreadsFromValue(obj); for (ThreadReference thread : threads) { @@ -743,11 +699,8 @@ public class TraceJdiMethods implements RmiMethods { vm.resume(); } - @RmiMethodRegistry.TraceMethod( - action = "step_out", - display = "Step out", - schema = "VirtualMachine") - public void step_vm_out(RmiTraceObject obj) { + @TraceMethod(action = "step_out", display = "Step out") + public void step_vm_out(@Param(schema = "VirtualMachine", name = "vm") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); List threads = getThreadsFromValue(obj); for (ThreadReference thread : threads) { @@ -764,8 +717,8 @@ public class TraceJdiMethods implements RmiMethods { vm.resume(); } - @RmiMethodRegistry.TraceMethod(action = "step_into", display = "Step into", schema = "Thread") - public void step_into(RmiTraceObject obj) { + @TraceMethod(action = "step_into", display = "Step into") + public void step_into(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); ThreadReference thread = (ThreadReference) getObjectFromPath(obj.getPath()); StepRequest stepReq = vm.eventRequestManager() @@ -775,36 +728,30 @@ public class TraceJdiMethods implements RmiMethods { vm.resume(); } - @RmiMethodRegistry.TraceMethod(action = "step_over", display = "Step over", schema = "Thread") - public void step_over(RmiTraceObject obj) { + @TraceMethod(action = "step_over", display = "Step over") + public void step_over(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); ThreadReference thread = (ThreadReference) getObjectFromPath(obj.getPath()); StepRequest stepReq = vm.eventRequestManager() - .createStepRequest(thread, StepRequest.STEP_OVER, - StepRequest.STEP_INTO); + .createStepRequest(thread, StepRequest.STEP_MIN, + StepRequest.STEP_OVER); stepReq.enable(); vm.resume(); } - @RmiMethodRegistry.TraceMethod(action = "step_out", display = "Step out", schema = "Thread") - public void step_out(RmiTraceObject obj) { + @TraceMethod(action = "step_out", display = "Step out") + public void step_out(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); ThreadReference thread = (ThreadReference) getObjectFromPath(obj.getPath()); StepRequest stepReq = vm.eventRequestManager() - .createStepRequest(thread, StepRequest.STEP_OUT, - StepRequest.STEP_INTO); + .createStepRequest(thread, StepRequest.STEP_MIN, + StepRequest.STEP_OUT); stepReq.enable(); vm.resume(); } -// public void step_advance(Object obj) {} -// public void step_return(Object obj) {} - - @RmiMethodRegistry.TraceMethod( - action = "thread_interrupt", - display = "Thread Interrupt", - schema = "Thread") - public void thread_interrupt(RmiTraceObject obj) { + @TraceMethod(display = "Thread Interrupt") + public void thread_interrupt(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { Object object = getObjectFromPath(obj.getPath()); if (object instanceof ThreadReference thread) { thread.interrupt(); @@ -812,11 +759,8 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "pop_stack", - display = "Pop stack", - schema = "StackFrame") - public void pop_stack(RmiTraceObject obj) { + @TraceMethod(action = "step_ext", display = "Pop stack") + public void pop_stack(@Param(schema = "StackFrame", name = "frame") RmiTraceObject obj) { StackFrame frame = (StackFrame) getObjectFromPath(obj.getPath()); ThreadReference thread = frame.thread(); try { @@ -827,11 +771,8 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "break_location", - display = "Break on execute", - schema = "Location") - public void break_location(RmiTraceObject obj) { + @TraceMethod(display = "Break on execute") + public void break_location(@Param(schema = "Location", name = "location") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof Location loc) { @@ -842,11 +783,8 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "break_field_access", - display = "Break on access", - schema = "Field") - public void break_access(RmiTraceObject obj) { + @TraceMethod(display = "Break on access") + public void break_access(@Param(schema = "Field", name = "field") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof Field field) { @@ -857,11 +795,8 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "break_field_modified", - display = "Break on modify", - schema = "Field") - public void break_modify(RmiTraceObject obj) { + @TraceMethod(display = "Break on modify") + public void break_modify(@Param(schema = "Field", name = "field") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof Field field) { @@ -872,19 +807,21 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "break_exception", - display = "Break on exception", - schema = "ReferenceType") - public void break_exception(RmiTraceObject obj, - @TargetMethod.Param( + @TraceMethod(display = "Break on exception") + public void break_exception( + @Param( + schema = "ReferenceType", + description = "Reference Type (Class)", + display = "Type", + name = "reference_type") RmiTraceObject obj, + @Param( description = "Caught exceptions will be notified", display = "NotifyCaught", - name = "notifyC") Boolean notifyCaught, - @TargetMethod.Param( + name = "notify_caught") boolean notifyCaught, + @Param( description = "Uncaught exceptions will be notified", display = "NotifyUncaught", - name = "notifyU") Boolean notifyUncaught) { + name = "notify_uncaught") boolean notifyUncaught) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ReferenceType reftype) { @@ -908,19 +845,15 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_started", - display = "Break on thread start", - schema = "EventContainer") - public void break_started_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on thread start") + public void break_started_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_started(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_started", - display = "Break on thread start", - schema = "Thread") - public void break_started_thread(RmiTraceObject obj) { + @TraceMethod(display = "Break on thread start") + public void break_started_thread( + @Param(schema = "Thread", name = "thread") RmiTraceObject obj) { break_started(obj); } @@ -937,27 +870,19 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_death", - display = "Break on thread exit", - schema = "EventContainer") - public void break_death_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on thread exit") + public void break_death_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_death(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_death", - display = "Break on thread exit", - schema = "Thread") - public void break_death_thread(RmiTraceObject obj) { + @TraceMethod(display = "Break on thread exit") + public void break_death_thread(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { break_death(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_vm_death", - display = "Break on VM death", - schema = "VirtualMachine") - public void break_vm_death(RmiTraceObject obj) { + @TraceMethod(display = "Break on VM death") + public void break_vm_death(@Param(schema = "VirtualMachine", name = "vm") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); VMDeathRequest brkReq = vm.eventRequestManager() .createVMDeathRequest(); @@ -986,35 +911,26 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_enter", - display = "Break on method enter", - schema = "EventContainer") - public void break_enter_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on method enter") + public void break_enter_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_enter(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_enter", - display = "Break on method enter", - schema = "ReferenceType") - public void break_enter_reftype(RmiTraceObject obj) { + @TraceMethod(display = "Break on method enter") + public void break_enter_reftype( + @Param(schema = "ReferenceType", name = "class") RmiTraceObject obj) { break_enter(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_enter_instance", - display = "Break on method enter", - schema = "ObjectReference") - public void break_enter_instance(RmiTraceObject obj) { + @TraceMethod(display = "Break on method enter") + public void break_enter_instance( + @Param(schema = "ObjectReference", name = "instance") RmiTraceObject obj) { break_enter(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_enter_thread", - display = "Break on method enter", - schema = "Thread") - public void break_enter_thread(RmiTraceObject obj) { + @TraceMethod(display = "Break on method enter") + public void break_enter_thread(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { break_enter(obj); } @@ -1039,35 +955,26 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_exit", - display = "Break on method exit", - schema = "EventContainer") - public void break_exit_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on method exit") + public void break_exit_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_exit(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_exit", - display = "Break on method exit", - schema = "ReferenceType") - public void break_exit_reftype(RmiTraceObject obj) { + @TraceMethod(display = "Break on method exit") + public void break_exit_reftype( + @Param(schema = "ReferenceType", name = "class") RmiTraceObject obj) { break_exit(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_exit", - display = "Break on method exit", - schema = "ObjectReference") - public void break_exit_instance(RmiTraceObject obj) { + @TraceMethod(display = "Break on method exit") + public void break_exit_instance( + @Param(schema = "ObjectReference", name = "instance") RmiTraceObject obj) { break_exit(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_exit", - display = "Break on method exit", - schema = "Thread") - public void break_exit_thread(RmiTraceObject obj) { + @TraceMethod(display = "Break on method exit") + public void break_exit_thread(@Param(schema = "Thread", name = "thread") RmiTraceObject obj) { break_exit(obj); } @@ -1084,19 +991,15 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_load", - display = "Break on class load", - schema = "EventContainer") - public void break_load_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on class load") + public void break_load_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_load(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_load", - display = "Break on class load", - schema = "ReferenceType") - public void break_load_reftype(RmiTraceObject obj) { + @TraceMethod(display = "Break on class load") + public void break_load_reftype( + @Param(schema = "ReferenceType", name = "class") RmiTraceObject obj) { break_load(obj); } @@ -1108,11 +1011,9 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_unload", - display = "Break on class unload", - schema = "EventContainer") - public void break_unload_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on class unload") + public void break_unload_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_unload(obj); } @@ -1137,35 +1038,27 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_enter_contention", - display = "Break on monitor contended enter", - schema = "EventContainer") - public void break_mon_enter_contention_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor contended enter") + public void break_mon_enter_contention_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_mon_enter_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_enter_contention", - display = "Break on monitor contended enter", - schema = "ReferenceType") - public void break_mon_enter_contention_reftype(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor contended enter") + public void break_mon_enter_contention_reftype( + @Param(schema = "ReferenceType", name = "class") RmiTraceObject obj) { break_mon_enter_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_enter_contention", - display = "Break on monitor contended enter", - schema = "ObjectReference") - public void break_mon_enter_contention_instance(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor contended enter") + public void break_mon_enter_contention_instance( + @Param(schema = "ObjectReference", name = "instance") RmiTraceObject obj) { break_mon_enter_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_enter_contention", - display = "Break on monitor contended enter", - schema = "Thread") - public void break_mon_enter_contention_thread(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor contended enter") + public void break_mon_enter_contention_thread( + @Param(schema = "Thread", name = "thread") RmiTraceObject obj) { break_mon_enter_contention(obj); } @@ -1190,35 +1083,27 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_entered_contention", - display = "Break on monitor contented entered", - schema = "EventContainer") - public void break_mon_entered_contention_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor contented entered") + public void break_mon_entered_contention_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_mon_entered_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_entered_contention", - display = "Break on monitor contented entered", - schema = "ReferenceType") - public void break_mon_entered_contention_reftype(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor contented entered") + public void break_mon_entered_contention_reftype( + @Param(schema = "ReferenceType", name = "class") RmiTraceObject obj) { break_mon_entered_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_entered_contention", - display = "Break on monitor contented entered", - schema = "ObjectReference") - public void break_mon_entered_contention_instance(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor contented entered") + public void break_mon_entered_contention_instance( + @Param(schema = "ObjectReference", name = "instance") RmiTraceObject obj) { break_mon_entered_contention(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_entered_contention", - display = "Break on monitor contented entered", - schema = "Thread") - public void break_mon_entered_contention_thread(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor contented entered") + public void break_mon_entered_contention_thread( + @Param(schema = "Thread", name = "thread") RmiTraceObject obj) { break_mon_entered_contention(obj); } @@ -1243,35 +1128,27 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_wait", - display = "Break on monitor wait", - schema = "EventContainer") - public void break_mon_wait_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor wait") + public void break_mon_wait_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_mon_wait(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_wait", - display = "Break on monitor wait", - schema = "ReferenceType") - public void break_mon_wait_reftype(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor wait") + public void break_mon_wait_reftype( + @Param(schema = "ReferenceType", name = "class") RmiTraceObject obj) { break_mon_wait(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_wait", - display = "Break on monitor wait", - schema = "ObjectReference") - public void break_mon_wait_instance(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor wait") + public void break_mon_wait_instance( + @Param(schema = "ObjectReference", name = "instance") RmiTraceObject obj) { break_mon_wait(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_wait", - display = "Break on monitor wait", - schema = "Thread") - public void break_mon_wait_thread(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor wait") + public void break_mon_wait_thread( + @Param(schema = "Thread", name = "thread") RmiTraceObject obj) { break_mon_wait(obj); } @@ -1296,47 +1173,41 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_waited", - display = "Break on monitor waited", - schema = "EventContainer") - public void break_mon_waited_container(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor waited") + public void break_mon_waited_container( + @Param(schema = "EventContainer", name = "container") RmiTraceObject obj) { break_mon_waited(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_waited", - display = "Break on monitor waited", - schema = "ReferenceType") - public void break_mon_waited_reftype(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor waited") + public void break_mon_waited_reftype( + @Param(schema = "ReferenceType", name = "class") RmiTraceObject obj) { break_mon_waited(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_waited", - display = "Break on monitor waited", - schema = "ObjectReference") - public void break_mon_waited_instance(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor waited") + public void break_mon_waited_instance( + @Param(schema = "ObjectReference", name = "instance") RmiTraceObject obj) { break_mon_waited(obj); } - @RmiMethodRegistry.TraceMethod( - action = "break_mon_waited", - display = "Break on monitor waited", - schema = "Thread") - public void break_mon_waited_thread(RmiTraceObject obj) { + @TraceMethod(display = "Break on monitor waited") + public void break_mon_waited_thread( + @Param(schema = "Thread", name = "thread") RmiTraceObject obj) { break_mon_waited(obj); } - @RmiMethodRegistry.TraceMethod( - action = "add_count_filter", - display = "Add count filter", - schema = "Event") - public void add_count_filter(RmiTraceObject obj, - @TargetMethod.Param( + @TraceMethod(display = "Add count filter") + public void add_count_filter( + @Param( + schema = "Event", + description = "Event", + display = "Event", + name = "event") RmiTraceObject obj, + @Param( description = "Count", display = "MaxCount", - name = "count") Integer count) { + name = "count") int count) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof EventRequest req) { req.disable(); @@ -1347,128 +1218,132 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "set_class_filter", - display = "Set class filter", - schema = "Event") - public void set_class_filter(RmiTraceObject obj, - @TargetMethod.Param( + @TraceMethod(display = "Set class filter") + public void set_class_filter( + @Param( + schema = "Event", + description = "Event", + display = "Event", + name = "event") RmiTraceObject obj, + @Param( description = "Filter Pattern", display = "Filter", name = "filter") String filter, - @TargetMethod.Param( + @Param( description = "Exclude", display = "Exclude", - name = "exclude") String exclude) { + name = "exclude") boolean exclude) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof MethodEntryRequest req) { req.disable(); - if (exclude.equals("true")) { + if (exclude) { req.addClassExclusionFilter(filter); - cmds.setValue(obj.getPath(), "Exclude", filter); + cmds.setValue(obj.getPath(), ATTR_EXCLUDE, filter); } else { req.addClassFilter(filter); - cmds.setValue(obj.getPath(), "Include", filter); + cmds.setValue(obj.getPath(), ATTR_INCLUDE, filter); } req.enable(); } if (ctxt instanceof MethodExitRequest req) { req.disable(); - if (exclude.equals("true")) { + if (exclude) { req.addClassExclusionFilter(filter); - cmds.setValue(obj.getPath(), "Exclude", filter); + cmds.setValue(obj.getPath(), ATTR_EXCLUDE, filter); } else { req.addClassFilter(filter); - cmds.setValue(obj.getPath(), "Include", filter); + cmds.setValue(obj.getPath(), ATTR_INCLUDE, filter); } req.enable(); } if (ctxt instanceof ClassPrepareRequest req) { req.disable(); - if (exclude.equals("true")) { + if (exclude) { req.addClassExclusionFilter(filter); - cmds.setValue(obj.getPath(), "Exclude", filter); + cmds.setValue(obj.getPath(), ATTR_EXCLUDE, filter); } else { req.addClassFilter(filter); - cmds.setValue(obj.getPath(), "Include", filter); + cmds.setValue(obj.getPath(), ATTR_INCLUDE, filter); } req.enable(); } if (ctxt instanceof ClassUnloadRequest req) { req.disable(); - if (exclude.equals("true")) { + if (exclude) { req.addClassExclusionFilter(filter); - cmds.setValue(obj.getPath(), "Exclude", filter); + cmds.setValue(obj.getPath(), ATTR_EXCLUDE, filter); } else { req.addClassFilter(filter); - cmds.setValue(obj.getPath(), "Include", filter); + cmds.setValue(obj.getPath(), ATTR_INCLUDE, filter); } req.enable(); } if (ctxt instanceof MonitorContendedEnterRequest req) { req.disable(); - if (exclude.equals("true")) { + if (exclude) { req.addClassExclusionFilter(filter); - cmds.setValue(obj.getPath(), "Exclude", filter); + cmds.setValue(obj.getPath(), ATTR_EXCLUDE, filter); } else { req.addClassFilter(filter); - cmds.setValue(obj.getPath(), "Include", filter); + cmds.setValue(obj.getPath(), ATTR_INCLUDE, filter); } req.enable(); } if (ctxt instanceof MonitorContendedEnteredRequest req) { req.disable(); - if (exclude.equals("true")) { + if (exclude) { req.addClassExclusionFilter(filter); - cmds.setValue(obj.getPath(), "Exclude", filter); + cmds.setValue(obj.getPath(), ATTR_EXCLUDE, filter); } else { req.addClassFilter(filter); - cmds.setValue(obj.getPath(), "Include", filter); + cmds.setValue(obj.getPath(), ATTR_INCLUDE, filter); } req.enable(); } if (ctxt instanceof MonitorWaitRequest req) { req.disable(); - if (exclude.equals("true")) { + if (exclude) { req.addClassExclusionFilter(filter); - cmds.setValue(obj.getPath(), "Exclude", filter); + cmds.setValue(obj.getPath(), ATTR_EXCLUDE, filter); } else { req.addClassFilter(filter); - cmds.setValue(obj.getPath(), "Include", filter); + cmds.setValue(obj.getPath(), ATTR_INCLUDE, filter); } req.enable(); } if (ctxt instanceof MonitorWaitedRequest req) { req.disable(); - if (exclude.equals("true")) { + if (exclude) { req.addClassExclusionFilter(filter); - cmds.setValue(obj.getPath(), "Exclude", filter); + cmds.setValue(obj.getPath(), ATTR_EXCLUDE, filter); } else { req.addClassFilter(filter); - cmds.setValue(obj.getPath(), "Include", filter); + cmds.setValue(obj.getPath(), ATTR_INCLUDE, filter); } req.enable(); } cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "set_source_filter", - display = "Set source filter", - schema = "Event") - public void set_source_filter(RmiTraceObject obj, - @TargetMethod.Param( + @TraceMethod(display = "Set source filter") + public void set_source_filter( + @Param( + schema = "Event", + description = "Event", + display = "Event", + name = "event") RmiTraceObject obj, + @Param( description = "Source Name Pattern", display = "SourceName", - name = "srcname") String srcname) { + name = "source_name") String srcname) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ClassPrepareRequest req) { req.disable(); @@ -1479,32 +1354,27 @@ public class TraceJdiMethods implements RmiMethods { cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "set_platform_filter", - display = "Set platform filter", - schema = "Event") - public void set_platform_filter(RmiTraceObject obj) { + @TraceMethod(display = "Set platform filter") + public void set_platform_filter(@Param(schema = "Event", name = "event") RmiTraceObject obj) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof ThreadStartRequest req) { req.disable(); req.addPlatformThreadsOnlyFilter(); - cmds.setValue(obj.getPath(), "PlatformOnly", true); + cmds.setValue(obj.getPath(), ATTR_PLATFORM_ONLY, true); req.enable(); } if (ctxt instanceof ThreadDeathRequest req) { req.disable(); req.addPlatformThreadsOnlyFilter(); - cmds.setValue(obj.getPath(), "PlatformOnly", true); + cmds.setValue(obj.getPath(), ATTR_PLATFORM_ONLY, true); req.enable(); } cmds.putEvents(); } - @RmiMethodRegistry.TraceMethod( - action = "toggle_breakpoint", - display = "Toggle breakpoint", - schema = "BreakpointSpec") - public void toggle_breakpoint(RmiTraceObject obj) { + @TraceMethod(action = "toggle", display = "Toggle breakpoint") + public void toggle_breakpoint( + @Param(schema = "BreakpointSpec", name = "breakpoint") RmiTraceObject obj) { VirtualMachine vm = manager.getJdi().getCurrentVM(); Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof Field field) { @@ -1523,11 +1393,19 @@ public class TraceJdiMethods implements RmiMethods { cmds.putBreakpoints(); } - @RmiMethodRegistry.TraceMethod( - action = "toggle_event", - display = "Toggle event", - schema = "Event") - public void toggle_event(RmiTraceObject obj) { + @TraceMethod(action = "delete", display = "Delete breakpoint") + public void delete_breakpoint( + @Param(schema = "BreakpointSpec", name = "breakpoint") RmiTraceObject obj) { + VirtualMachine vm = manager.getJdi().getCurrentVM(); + Object ctxt = getObjectFromPath(obj.getPath()); + if (ctxt instanceof EventRequest req) { + vm.eventRequestManager().deleteEventRequest(req); + } + cmds.putBreakpoints(); + } + + @TraceMethod(action = "toggle", display = "Toggle event") + public void toggle_event(@Param(schema = "Event", name = "event") RmiTraceObject obj) { Object ctxt = getObjectFromPath(obj.getPath()); if (ctxt instanceof EventRequest req) { if (req.isEnabled()) { @@ -1540,22 +1418,28 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod( - action = "toggle_scope", - display = "Toggle scope", - schema = "MethodContainer") - public void toggle_scope_methods(RmiTraceObject obj) { + @TraceMethod(action = "delete", display = "Delete Event") + public void delete_event(@Param(schema = "Event", name = "event") RmiTraceObject obj) { + VirtualMachine vm = manager.getJdi().getCurrentVM(); + Object ctxt = getObjectFromPath(obj.getPath()); + if (ctxt instanceof EventRequest req) { + vm.eventRequestManager().deleteEventRequest(req); + } + cmds.putEvents(); + } + + @TraceMethod(action = "toggle", display = "Toggle scope") + public void toggle_scope_methods( + @Param(schema = "MethodContainer", name = "container") RmiTraceObject obj) { String ppath = cmds.getParentPath(obj.getPath()); Object parent = getObjectFromPath(ppath); manager.toggleScope(parent); refresh_methods(obj); } - @RmiMethodRegistry.TraceMethod( - action = "toggle_scope", - display = "Toggle scope", - schema = "FieldContainer") - public void toggle_scope_fields(RmiTraceObject obj) { + @TraceMethod(action = "toggle", display = "Toggle scope") + public void toggle_scope_fields( + @Param(schema = "FieldContainer", name = "container") RmiTraceObject obj) { String ppath = cmds.getParentPath(obj.getPath()); Object parent = getObjectFromPath(ppath); manager.toggleScope(parent); @@ -1567,8 +1451,17 @@ public class TraceJdiMethods implements RmiMethods { } } - @RmiMethodRegistry.TraceMethod(action = "read_mem", display = "", schema = "VirtualMachine") - public long read_mem(RmiTraceObject obj, AddressRange range) { + @TraceMethod(action = "read_mem", display = "Read Memory") + public long read_mem( + @Param( + schema = "VirtualMachine", + description = "VirtualMachine", + display = "VirtualMachine", + name = "vm") RmiTraceObject obj, + @Param( + description = "Range", + display = "Range", + name = "range") AddressRange range) { VirtualMachine vm = manager.getJdi().getCurrentVM(); MemoryMapper mapper = cmds.state.trace.memoryMapper; Address start = mapper.mapBack(range.getMinAddress()); @@ -1582,6 +1475,127 @@ public class TraceJdiMethods implements RmiMethods { return range.getLength(); } + @TraceMethod(display = "Invoke method (no args)") + public void execute_on_instance( + @Param( + schema = "ObjectReference", + description = "Object Reference", + display = "Object", + name = "object") RmiTraceObject obj, + @Param( + description = "Thread Name", + display = "ThreadName", + name = "thread_name") String threadName, + @Param( + description = "Method Name", + display = "MethodName", + name = "method_name") String methodName) { + VirtualMachine vm = manager.getJdi().getCurrentVM(); + ObjectReference ref = (ObjectReference) getObjectFromPath(obj.getPath()); + List methods = ref.referenceType().methodsByName(methodName); + if (methods.size() > 1) { + Msg.warn(this, "Method " + methodName + " is not unique - using first variant"); + } + for (ThreadReference thread : vm.allThreads()) { + if (thread.name().equals(threadName)) { + cmds.execute(ref, thread, methods.get(0), new ArrayList(), 0); + } + } + } + + @TraceMethod(display = "Invoke static method (no args)") + public void execute_on_class( + @Param( + schema = "ReferenceType", + description = "Class", + display = "Class", + name = "class") RmiTraceObject obj, + @Param( + description = "Thread Name", + display = "ThreadName", + name = "thread_name") String threadName, + @Param( + description = "Method Name", + display = "MethodName", + name = "method_name") String methodName) { + VirtualMachine vm = manager.getJdi().getCurrentVM(); + ReferenceType reftype = (ReferenceType) getObjectFromPath(obj.getPath()); + if (reftype instanceof ClassType cls) { + List methods = cls.methodsByName(methodName); + if (methods.size() > 1) { + Msg.warn(this, "Method " + methodName + " is not unique - using first variant"); + } + if (!methods.get(0).isStatic()) { + Msg.error(this, "Method " + methodName + " is not static"); + return; + } + for (ThreadReference thread : vm.allThreads()) { + if (thread.name().equals(threadName)) { + cmds.execute(cls, thread, methods.get(0), new ArrayList(), 0); + } + } + } + } + + @TraceMethod(display = "Invoke method (no args)") + public void execute_method( + @Param( + schema = "Method", + description = "Method", + display = "Method", + name = "method") RmiTraceObject obj, + @Param( + description = "Instance Pattern", + display = "InstancePattern", + name = "instance_pattern") String instancePattern, + @Param( + description = "Thread Name", + display = "ThreadName", + name = "thread_name") String threadName) { + VirtualMachine vm = manager.getJdi().getCurrentVM(); + String path = obj.getPath(); + Method method = (Method) getObjectFromPath(path); + ReferenceType declaringType = method.declaringType(); + List instances = declaringType.instances(0); + for (ObjectReference ref : instances) { + if (ref.toString().contains(instancePattern)) { + for (ThreadReference thread : vm.allThreads()) { + if (thread.name().equals(threadName)) { + cmds.execute(ref, thread, method, new ArrayList(), 0); + } + } + } + } + } + + @TraceMethod(display = "Invoke static method (no args)") + public void execute_static_method( + @Param( + schema = "Method", + description = "Method", + display = "Method", + name = "method") RmiTraceObject obj, + @Param( + description = "Thread Name", + display = "ThreadName", + name = "thread_name") String threadName) { + VirtualMachine vm = manager.getJdi().getCurrentVM(); + String path = obj.getPath(); + Method method = (Method) getObjectFromPath(path); + if (!method.isStatic()) { + Msg.error(this, "Method " + method.name() + " is not static"); + return; + } + ReferenceType reftype = method.declaringType(); + if (reftype instanceof ClassType ct) { + for (ThreadReference thread : vm.allThreads()) { + if (thread.name().equals(threadName)) { + cmds.execute(ct, thread, method, new ArrayList(), 0); + } + } + } + } + private List getThreadsFromValue(RmiTraceObject obj) { Object object = getObjectFromPath(obj.getPath()); if (object instanceof VirtualMachine vm) { diff --git a/Ghidra/Debug/Debugger-jpda/src/main/resources/ghidra/app/plugin/core/debug/client/tracermi/jdi_schema.xml b/Ghidra/Debug/Debugger-jpda/src/main/resources/ghidra/app/plugin/core/debug/client/tracermi/jdi_schema.xml index 14b0cee484..b17969eb38 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/resources/ghidra/app/plugin/core/debug/client/tracermi/jdi_schema.xml +++ b/Ghidra/Debug/Debugger-jpda/src/main/resources/ghidra/app/plugin/core/debug/client/tracermi/jdi_schema.xml @@ -162,7 +162,7 @@ - + @@ -361,7 +361,7 @@