GP-4760: Implement TraceRmi for JPDA (Java/Dalvik targets)

This commit is contained in:
Dan 2024-09-09 11:57:14 -04:00
parent 62819ff301
commit 6d39e7231c
50 changed files with 8324 additions and 440 deletions

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -22,6 +22,8 @@ apply plugin: 'eclipse'
eclipse.project.name = 'Debug Debugger-jpda'
dependencies {
api project(':Debugger-api')
api project(':Debugger-rmi-trace')
api project(':Framework-AsyncComm')
api project(':Framework-Debugging')
api project(':ProposedUtils')

View File

@ -1,2 +1,6 @@
##VERSION: 2.0
Module.manifest||GHIDRA||||END|
data/debugger-launchers/attach-java.jsh||GHIDRA||||END|
data/debugger-launchers/bypid-java.jsh||GHIDRA||||END|
data/debugger-launchers/local-java.jsh||GHIDRA||||END|
src/main/resources/ghidra/app/plugin/core/debug/client/tracermi/jdi_schema.xml||GHIDRA||||END|

View File

@ -0,0 +1,28 @@
//@title java attach port
//@timeout 20000
//@desc <html><body width="300px">
//@desc <h3>Attach with <tt>java</tt></h3>
//@desc <p>
//@desc This will attach to the target at HOST:PORT.
//@desc For setup instructions, press <b>F1</b>.
//@desc </p>
//@desc </body></html>
//@menu-group attach
//@icon icon.debugger
//@help TraceRmiLauncherServicePlugin#java_attach
//@enum Arch:str JVM Dalvik
//@env OPT_ARCH:Arch="JVM" "Arch" "Target architecture"
//@env OPT_HOST:str="localhost" "Host" "The hostname of the target"
//@env OPT_PORT:str="54321" "Port" "The host's listening port"
//@env OPT_TIMEOUT:str="0" "Timeout" "Connection timeout"
//@env OPT_JSHELL_PATH:file="" "JShell cmd (if desired)" "The full path to jshell."
import ghidra.dbg.jdi.rmi.jpda.*;
// NB. The jshell code here is user modifiable; however, the user must provide OPT_JSHEL_PATH when
// prompted in Ghidra' UI, or else this script is completely bypassed. Without a jshell, Ghidra
// calls new JdiClientThread(env).start() directly.
GhidraJdiInit.initApp()
JdiClientThread thread = new JdiClientThread(System.getenv());
thread.start();

View File

@ -0,0 +1,27 @@
//@title java attach PID
//@timeout 20000
//@desc <html><body width="300px">
//@desc <h3>Attach with <tt>java</tt></h3>
//@desc <p>
//@desc This will attach to the target with a specified PID.
//@desc For setup instructions, press <b>F1</b>.
//@desc </p>
//@desc </body></html>
//@menu-group attach
//@icon icon.debugger
//@help TraceRmiLauncherServicePlugin#java_bypid
//@enum Arch:str JVM Dalvik
//@env OPT_ARCH:Arch="JVM" "Arch" "Target architecture"
//@env OPT_PID:str="" "Pid" "The target process id"
//@env OPT_TIMEOUT:str="0" "Timeout" "Connection timeout"
//@env OPT_JSHELL_PATH:file="" "JShell cmd (if desired)" "The full path to jshell."
import ghidra.dbg.jdi.rmi.jpda.*;
// NB. The jshell code here is user modifiable; however, the user must provide OPT_JSHEL_PATH when
// prompted in Ghidra' UI, or else this script is completely bypassed. Without a jshell, Ghidra
// calls new JdiClientThread(env).start() directly.
GhidraJdiInit.initApp()
JdiClientThread thread = new JdiClientThread(System.getenv());
thread.start();

View File

@ -0,0 +1,29 @@
//@title java launch
//@timeout 20000
//@desc <html><body width="300px">
//@desc <h3>Launch with <tt>java</tt></h3>
//@desc <p>
//@desc This will launch the target on the local machine using <tt>java</tt>.
//@desc For setup instructions, press <b>F1</b>.
//@desc </p>
//@desc </body></html>
//@menu-group local
//@icon icon.debugger
//@help TraceRmiLauncherServicePlugin#java
//@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."
import ghidra.dbg.jdi.rmi.jpda.*;
// NB. The jshell code here is user modifiable; however, the user must provide OPT_JSHEL_PATH when
// prompted in Ghidra' UI, or else this script is completely bypassed. Without a jshell, Ghidra
// calls new JdiClientThread(env).start() directly.
GhidraJdiInit.initApp()
JdiClientThread thread = new JdiClientThread(System.getenv());
thread.start();

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,6 +15,8 @@
*/
package ghidra.dbg.jdi.manager;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.*;
import org.apache.commons.lang3.exception.ExceptionUtils;
@ -26,7 +28,6 @@ import com.sun.jdi.request.EventRequest;
import ghidra.async.AsyncReference;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
import ghidra.util.Msg;
import ghidra.util.datastruct.ListenerSet;
public class JdiEventHandler implements Runnable {
@ -35,16 +36,16 @@ public class JdiEventHandler implements Runnable {
String shutdownMessageKey;
private VirtualMachine vm;
private Thread thread;
private Thread handlerThread;
private JdiEventHandler global;
protected final AsyncReference<Integer, JdiCause> state =
new AsyncReference<>(ThreadReference.THREAD_STATUS_NOT_STARTED);
public final ListenerSet<JdiEventsListener> listenersEvent =
new ListenerSet<>(JdiEventsListener.class, true);
public final Set<JdiEventsListener> listenersEvent = new HashSet<>();
protected final ExecutorService eventThread = Executors.newSingleThreadExecutor();
public JdiEventHandler() {
// Nothing to do here
}
public JdiEventHandler(VirtualMachine vm, JdiEventHandler global) {
@ -54,18 +55,19 @@ public class JdiEventHandler implements Runnable {
}
public void start() {
this.thread = new Thread(this, "event-handler");
thread.start();
this.handlerThread = new Thread(this, "event-handler");
handlerThread.start();
}
synchronized void shutdown() {
connected = false; // force run() loop termination
thread.interrupt();
handlerThread.interrupt();
while (!completed) {
try {
wait();
}
catch (InterruptedException exc) {
// IGNORE
}
}
}
@ -115,9 +117,9 @@ public class JdiEventHandler implements Runnable {
}
else if (eventSet.suspendPolicy() == EventRequest.SUSPEND_ALL) {
setCurrentThread(eventSet);
event(
() -> listenersEvent.invoke().processStop(eventSet, JdiCause.Causes.UNCLAIMED),
"processStopped");
for (JdiEventsListener listener : listenersEvent) {
listener.processStop(eventSet, JdiCause.Causes.UNCLAIMED);
}
}
}
catch (InterruptedException exc) {
@ -135,68 +137,33 @@ public class JdiEventHandler implements Runnable {
}
private DebugStatus processEvent(Event event) {
System.err.println(event + ":" + vm);
if (event instanceof ExceptionEvent) {
return processException((ExceptionEvent) event);
}
else if (event instanceof BreakpointEvent) {
return processBreakpoint((BreakpointEvent) event);
}
else if (event instanceof WatchpointEvent) {
return processWatchpoint((WatchpointEvent) event);
}
else if (event instanceof AccessWatchpointEvent) {
return processAccessWatchpoint((AccessWatchpointEvent) event);
}
else if (event instanceof ModificationWatchpointEvent) {
return processWatchpointModification((ModificationWatchpointEvent) event);
}
else if (event instanceof StepEvent) {
return processStep((StepEvent) event);
}
else if (event instanceof MethodEntryEvent) {
return processMethodEntry((MethodEntryEvent) event);
}
else if (event instanceof MethodExitEvent) {
return processMethodExit((MethodExitEvent) event);
}
else if (event instanceof MonitorContendedEnteredEvent) {
return processMCEntered((MonitorContendedEnteredEvent) event);
}
else if (event instanceof MonitorContendedEnterEvent) {
return processMCEnter((MonitorContendedEnterEvent) event);
}
else if (event instanceof MonitorWaitedEvent) {
return processMonitorWaited((MonitorWaitedEvent) event);
}
else if (event instanceof MonitorWaitEvent) {
return processMonitorWait((MonitorWaitEvent) event);
}
else if (event instanceof ClassPrepareEvent) {
return processClassPrepare((ClassPrepareEvent) event);
}
else if (event instanceof ClassUnloadEvent) {
return processClassUnload((ClassUnloadEvent) event);
}
else if (event instanceof ThreadStartEvent) {
return processThreadStart((ThreadStartEvent) event);
}
else if (event instanceof ThreadDeathEvent) {
return processThreadDeath((ThreadDeathEvent) event);
}
else if (event instanceof VMStartEvent) {
return processVMStart((VMStartEvent) event);
}
else if (event instanceof VMDisconnectEvent) {
return processVMDisconnect((VMDisconnectEvent) event);
}
else if (event instanceof VMDeathEvent) {
return processVMDeath((VMDeathEvent) event);
}
else {
System.err.println("Unknown event: " + event);
return null;
}
//System.err.println(event + ":" + vm);
return switch (event) {
case ExceptionEvent ev -> processException(ev);
case AccessWatchpointEvent ev -> processAccessWatchpoint(ev);
case ModificationWatchpointEvent ev -> processWatchpointModification(ev);
case WatchpointEvent ev -> processWatchpoint(ev);
case StepEvent ev -> processStep(ev);
case MethodEntryEvent ev -> processMethodEntry(ev);
case MethodExitEvent ev -> processMethodExit(ev);
case MonitorContendedEnteredEvent ev -> processMCEntered(ev);
case MonitorContendedEnterEvent ev -> processMCEnter(ev);
case MonitorWaitedEvent ev -> processMonitorWaited(ev);
case MonitorWaitEvent ev -> processMonitorWait(ev);
case ClassPrepareEvent ev -> processClassPrepare(ev);
case ClassUnloadEvent ev -> processClassUnload(ev);
case ThreadStartEvent ev -> processThreadStart(ev);
case ThreadDeathEvent ev -> processThreadDeath(ev);
case VMStartEvent ev -> processVMStart(ev);
case VMDisconnectEvent ev -> processVMDisconnect(ev);
case VMDeathEvent ev -> processVMDeath(ev);
default -> processUnknown(event);
};
}
private DebugStatus processUnknown(Event event) {
System.err.println("Unknown event: " + event);
return null;
}
private boolean vmDied = false;
@ -214,9 +181,11 @@ public class JdiEventHandler implements Runnable {
/*
* Inform jdb command line processor that jdb is being shutdown. JDK-8154144.
*/
event(() -> listenersEvent.invoke().processShutdown(event, JdiCause.Causes.UNCLAIMED),
"processStopped");
return null; ///false;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.processShutdown(event, JdiCause.Causes.UNCLAIMED));
}
return status;
}
else {
throw new InternalError();
@ -293,257 +262,300 @@ public class JdiEventHandler implements Runnable {
JdiThreadInfo.setCurrentThread(thread);
}
private DebugStatus update(DebugStatus status, DebugStatus update) {
if (update == null) {
update = DebugStatus.BREAK;
}
return update.equals(DebugStatus.NO_CHANGE) ? status : update;
}
/**
* Handler for breakpoint events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processBreakpoint(BreakpointEvent evt) {
event(() -> listenersEvent.invoke().breakpointHit(evt, JdiCause.Causes.UNCLAIMED),
"breakpointHit");
return DebugStatus.BREAK;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.breakpointHit(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for exception events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processException(ExceptionEvent evt) {
event(() -> listenersEvent.invoke().exceptionHit(evt, JdiCause.Causes.UNCLAIMED),
"exceptionHit");
return DebugStatus.BREAK;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.exceptionHit(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for method entry events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processMethodEntry(MethodEntryEvent evt) {
event(() -> listenersEvent.invoke().methodEntry(evt, JdiCause.Causes.UNCLAIMED), "methodEntry");
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.methodEntry(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for method exit events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processMethodExit(MethodExitEvent evt) {
event(() -> listenersEvent.invoke().methodExit(evt, JdiCause.Causes.UNCLAIMED), "methodExit");
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.methodExit(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for class prepared events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processClassPrepare(ClassPrepareEvent evt) {
event(() -> listenersEvent.invoke().classPrepare(evt, JdiCause.Causes.UNCLAIMED),
"classPrepare");
/*
if (!Env.specList.resolve(cle)) {
MessageOutput.lnprint("Stopping due to deferred breakpoint errors.");
return true;
} else {
return false;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.classPrepare(evt, JdiCause.Causes.UNCLAIMED));
}
*/
return DebugStatus.GO;
return status;
}
/**
* Handler for class unload events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processClassUnload(ClassUnloadEvent evt) {
event(() -> listenersEvent.invoke().classUnload(evt, JdiCause.Causes.UNCLAIMED), "classUnload");
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.classUnload(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for monitor contended entered events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processMCEntered(MonitorContendedEnteredEvent evt) {
event(() -> listenersEvent.invoke().monitorContendedEntered(evt, JdiCause.Causes.UNCLAIMED),
"monitorContendedEntered");
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status =
update(status, listener.monitorContendedEntered(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for monitor contended enter events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processMCEnter(MonitorContendedEnterEvent evt) {
event(() -> listenersEvent.invoke().monitorContendedEnter(evt, JdiCause.Causes.UNCLAIMED),
"monitorContendedEnter");
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.monitorContendedEnter(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for monitor waited events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processMonitorWaited(MonitorWaitedEvent evt) {
event(() -> listenersEvent.invoke().monitorWaited(evt, JdiCause.Causes.UNCLAIMED),
"monitorWaited");
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.monitorWaited(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for monitor waited events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processMonitorWait(MonitorWaitEvent evt) {
event(() -> listenersEvent.invoke().monitorWait(evt, JdiCause.Causes.UNCLAIMED), "monitorWait");
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.monitorWait(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for step events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processStep(StepEvent evt) {
evt.request().disable();
event(() -> listenersEvent.invoke().stepComplete(evt, JdiCause.Causes.UNCLAIMED), "step");
return DebugStatus.STEP_INTO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.stepComplete(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for watchpoint events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processWatchpoint(WatchpointEvent evt) {
event(() -> listenersEvent.invoke().watchpointHit(evt, JdiCause.Causes.UNCLAIMED),
"watchpointHit");
return DebugStatus.BREAK;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.watchpointHit(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for access watchpoint events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processAccessWatchpoint(AccessWatchpointEvent evt) {
event(() -> listenersEvent.invoke().accessWatchpointHit(evt, JdiCause.Causes.UNCLAIMED),
"accessWatchpointHit");
return DebugStatus.BREAK;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.accessWatchpointHit(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for watchpoint modified events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processWatchpointModification(ModificationWatchpointEvent evt) {
event(() -> listenersEvent.invoke().watchpointModified(evt, JdiCause.Causes.UNCLAIMED),
"watchpointModified");
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.watchpointModified(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for thread death events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processThreadDeath(ThreadDeathEvent evt) {
event(() -> listenersEvent.invoke().threadExited(evt, JdiCause.Causes.UNCLAIMED),
"threadExited");
JdiThreadInfo.removeThread(evt.thread());
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.threadExited(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for vm start events
*
* @param thread eventThread
* @param threadState state
* @param reason reason
* @return status
*/
public DebugStatus processThreadStateChanged(ThreadReference thread, int threadState,
JdiReason reason) {
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.threadStateChanged(thread, threadState,
JdiCause.Causes.UNCLAIMED, reason));
}
return status;
}
/**
* Handler for thread start events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processThreadStart(ThreadStartEvent evt) {
JdiThreadInfo.addThread(evt.thread());
event(() -> listenersEvent.invoke().threadStarted(evt, JdiCause.Causes.UNCLAIMED),
"threadStarted");
return DebugStatus.GO;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.threadStarted(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for vm death events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processVMDeath(VMDeathEvent evt) {
shutdownMessageKey = "The application exited";
event(() -> listenersEvent.invoke().vmDied(evt, JdiCause.Causes.UNCLAIMED), "vmDied");
return DebugStatus.BREAK;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.vmDied(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for vm disconnect events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processVMDisconnect(VMDisconnectEvent evt) {
shutdownMessageKey = "The application has been disconnected";
event(() -> listenersEvent.invoke().vmDisconnected(evt, JdiCause.Causes.UNCLAIMED),
"vmDisconnected");
return DebugStatus.BREAK;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.vmDisconnected(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
/**
* Handler for vm start events
*
* @param evt the event
* @param v nothing
* @return
* @return status
*/
protected DebugStatus processVMStart(VMStartEvent evt) {
event(() -> listenersEvent.invoke().vmStarted(evt, JdiCause.Causes.UNCLAIMED), "vmStarted");
return DebugStatus.BREAK;
DebugStatus status = DebugStatus.NO_CHANGE;
for (JdiEventsListener listener : listenersEvent) {
status = update(status, listener.vmStarted(evt, JdiCause.Causes.UNCLAIMED));
}
return status;
}
public Integer getState() {
@ -560,4 +572,5 @@ public class JdiEventHandler implements Runnable {
}
return set;
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -19,6 +19,7 @@ import com.sun.jdi.*;
import com.sun.jdi.event.*;
import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
/**
* A listener for events related to objects known to the manager
@ -30,8 +31,9 @@ public interface JdiEventsListener {
*
* @param vm a handle to the selected vm
* @param cause the cause of this event
* @return status
*/
void vmSelected(VirtualMachine vm, JdiCause cause);
DebugStatus vmSelected(VirtualMachine vm, JdiCause cause);
/**
* A different thread has been selected (gained focus)
@ -39,8 +41,9 @@ public interface JdiEventsListener {
* @param thread a handle to the selected thread
* @param frame a handle to the current frame
* @param cause the cause of this event
* @return status
*/
void threadSelected(ThreadReference thread, StackFrame frame, JdiCause cause);
DebugStatus threadSelected(ThreadReference thread, StackFrame frame, JdiCause cause);
/**
* A library has been loaded by an vm
@ -48,8 +51,9 @@ public interface JdiEventsListener {
* @param vm a handle to the vm which loaded the library
* @param name the name of the library on the target
* @param cause the cause of this event
* @return status
*/
void libraryLoaded(VirtualMachine vm, String name, JdiCause cause);
DebugStatus classLoaded(VirtualMachine vm, String name, JdiCause cause);
/**
* A library has been unloaded from an vm
@ -57,16 +61,18 @@ public interface JdiEventsListener {
* @param vm a handle to the vm which unloaded the library
* @param name the name of the library on the target
* @param cause the cause of this event
* @return status
*/
void libraryUnloaded(VirtualMachine vm, String name, JdiCause cause);
DebugStatus classUnloaded(VirtualMachine vm, String name, JdiCause cause);
/**
* A breakpoint has been created in the session
*
* @param info information about the new breakpoint
* @param cause the cause of this event
* @return status
*/
void breakpointCreated(JdiBreakpointInfo info, JdiCause cause);
DebugStatus breakpointCreated(JdiBreakpointInfo info, JdiCause cause);
/**
* A breakpoint in the session has been modified
@ -74,16 +80,19 @@ public interface JdiEventsListener {
* @param newInfo new information about the modified breakpoint
* @param oldInfo old information about the modified breakpoint
* @param cause the cause of this event
* @return status
*/
void breakpointModified(JdiBreakpointInfo newInfo, JdiBreakpointInfo oldInfo, JdiCause cause);
DebugStatus breakpointModified(JdiBreakpointInfo newInfo, JdiBreakpointInfo oldInfo,
JdiCause cause);
/**
* A breakpoint has been deleted from the session
*
* @param info information about the now-deleted breakpoint
* @param cause the cause of this event
* @return status
*/
void breakpointDeleted(JdiBreakpointInfo info, JdiCause cause);
DebugStatus breakpointDeleted(JdiBreakpointInfo info, JdiCause cause);
/**
* TODO: This is not yet implemented
@ -95,145 +104,165 @@ public interface JdiEventsListener {
* @param addr the address of the change
* @param len the length, with the address, bounding the region of change
* @param cause the cause of this event
* @return status
*/
void memoryChanged(VirtualMachine vm, long addr, int len, JdiCause cause);
DebugStatus memoryChanged(VirtualMachine vm, long addr, int len, JdiCause cause);
void vmInterrupted();
DebugStatus vmInterrupted();
/**
* A breakpoint has been hit
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void breakpointHit(BreakpointEvent evt, JdiCause cause);
DebugStatus breakpointHit(BreakpointEvent evt, JdiCause cause);
/**
* An exception has been hit
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void exceptionHit(ExceptionEvent evt, JdiCause cause);
DebugStatus exceptionHit(ExceptionEvent evt, JdiCause cause);
/**
* A method has been invoked
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void methodEntry(MethodEntryEvent evt, JdiCause cause);
DebugStatus methodEntry(MethodEntryEvent evt, JdiCause cause);
/**
* A method is about to finish
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void methodExit(MethodExitEvent evt, JdiCause cause);
DebugStatus methodExit(MethodExitEvent evt, JdiCause cause);
/**
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void classPrepare(ClassPrepareEvent evt, JdiCause cause);
DebugStatus classPrepare(ClassPrepareEvent evt, JdiCause cause);
/**
* A calls is being unloaded
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void classUnload(ClassUnloadEvent evt, JdiCause cause);
DebugStatus classUnload(ClassUnloadEvent evt, JdiCause cause);
/**
* A thread has entered a monitor after release from another thread
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause);
DebugStatus monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause);
/**
* A thread is attempting to enter monitor acquired by another thread
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause);
DebugStatus monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause);
/**
* A vm has finished waiting on a monitor object
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void monitorWaited(MonitorWaitedEvent evt, JdiCause cause);
DebugStatus monitorWaited(MonitorWaitedEvent evt, JdiCause cause);
/**
* A vm is about to wait on a monitor object
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void monitorWait(MonitorWaitEvent evt, JdiCause cause);
DebugStatus monitorWait(MonitorWaitEvent evt, JdiCause cause);
/**
* A step has completed
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void stepComplete(StepEvent evt, JdiCause cause);
DebugStatus stepComplete(StepEvent evt, JdiCause cause);
/**
* A watchpoint has been hit
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void watchpointHit(WatchpointEvent evt, JdiCause cause);
DebugStatus watchpointHit(WatchpointEvent evt, JdiCause cause);
/**
* A field has been accessed
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause);
DebugStatus accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause);
/**
* A field has been modified
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void watchpointModified(ModificationWatchpointEvent evt, JdiCause cause);
DebugStatus watchpointModified(ModificationWatchpointEvent evt, JdiCause cause);
/**
* A thread has exited
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void threadExited(ThreadDeathEvent evt, JdiCause cause);
DebugStatus threadExited(ThreadDeathEvent evt, JdiCause cause);
/**
* A thread has started
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void threadStarted(ThreadStartEvent evt, JdiCause cause);
DebugStatus threadStarted(ThreadStartEvent evt, JdiCause cause);
/**
* A thread has changed state
*
* @param evt the triggering event
* @param thread thread
* @param state state
* @param cause the cause of this event
* @param reason reason
* @return status
*/
void threadStateChanged(ThreadReference thread, Integer state, JdiCause cause,
DebugStatus threadStateChanged(ThreadReference thread, Integer state, JdiCause cause,
JdiReason reason);
/**
@ -241,27 +270,30 @@ public interface JdiEventsListener {
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void vmDied(VMDeathEvent evt, JdiCause cause);
DebugStatus vmDied(VMDeathEvent evt, JdiCause cause);
/**
* A vm has been disconnected
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void vmDisconnected(VMDisconnectEvent evt, JdiCause cause);
DebugStatus vmDisconnected(VMDisconnectEvent evt, JdiCause cause);
/**
* A vm has started
*
* @param evt the triggering event
* @param cause the cause of this event
* @return status
*/
void vmStarted(VMStartEvent evt, JdiCause cause);
DebugStatus vmStarted(VMStartEvent evt, JdiCause cause);
void processStop(EventSet eventSet, JdiCause cause);
DebugStatus processStop(EventSet eventSet, JdiCause cause);
void processShutdown(Event event, JdiCause cause);
DebugStatus processShutdown(Event event, JdiCause cause);
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,12 +15,11 @@
*/
package ghidra.dbg.jdi.manager;
import java.util.Collection;
import com.sun.jdi.*;
import com.sun.jdi.event.*;
import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
/**
* An adapter for {@link JdiEventsListener}
@ -30,128 +29,159 @@ import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo;
public interface JdiEventsListenerAdapter extends JdiEventsListener {
@Override
default void vmSelected(VirtualMachine vm, JdiCause cause) {
default DebugStatus vmSelected(VirtualMachine vm, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void threadSelected(ThreadReference thread, StackFrame frame, JdiCause cause) {
default DebugStatus threadSelected(ThreadReference thread, StackFrame frame, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void threadStateChanged(ThreadReference thread, Integer state, JdiCause cause,
default DebugStatus threadStateChanged(ThreadReference thread, Integer state, JdiCause cause,
JdiReason reason) {
return DebugStatus.NO_CHANGE;
}
@Override
default void libraryLoaded(VirtualMachine vm, String name, JdiCause cause) {
default DebugStatus classLoaded(VirtualMachine vm, String name, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void libraryUnloaded(VirtualMachine vm, String name, JdiCause cause) {
default DebugStatus classUnloaded(VirtualMachine vm, String name, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void breakpointCreated(JdiBreakpointInfo info, JdiCause cause) {
default DebugStatus breakpointCreated(JdiBreakpointInfo info, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void breakpointModified(JdiBreakpointInfo newInfo, JdiBreakpointInfo oldInfo,
default DebugStatus breakpointModified(JdiBreakpointInfo newInfo, JdiBreakpointInfo oldInfo,
JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void breakpointDeleted(JdiBreakpointInfo info, JdiCause cause) {
default DebugStatus breakpointDeleted(JdiBreakpointInfo info, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void memoryChanged(VirtualMachine vm, long addr, int len, JdiCause cause) {
default DebugStatus memoryChanged(VirtualMachine vm, long addr, int len, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void vmInterrupted() {
default DebugStatus vmInterrupted() {
return DebugStatus.BREAK;
}
@Override
default void breakpointHit(BreakpointEvent evt, JdiCause cause) {
default DebugStatus breakpointHit(BreakpointEvent evt, JdiCause cause) {
return DebugStatus.BREAK;
}
@Override
default void exceptionHit(ExceptionEvent evt, JdiCause cause) {
default DebugStatus exceptionHit(ExceptionEvent evt, JdiCause cause) {
return DebugStatus.BREAK;
}
@Override
default void methodEntry(MethodEntryEvent evt, JdiCause cause) {
default DebugStatus methodEntry(MethodEntryEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void methodExit(MethodExitEvent evt, JdiCause cause) {
default DebugStatus methodExit(MethodExitEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void classPrepare(ClassPrepareEvent evt, JdiCause cause) {
default DebugStatus classPrepare(ClassPrepareEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void classUnload(ClassUnloadEvent evt, JdiCause cause) {
default DebugStatus classUnload(ClassUnloadEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause) {
default DebugStatus monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause) {
default DebugStatus monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void monitorWaited(MonitorWaitedEvent evt, JdiCause cause) {
default DebugStatus monitorWaited(MonitorWaitedEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void monitorWait(MonitorWaitEvent evt, JdiCause cause) {
default DebugStatus monitorWait(MonitorWaitEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void stepComplete(StepEvent evt, JdiCause cause) {
default DebugStatus stepComplete(StepEvent evt, JdiCause cause) {
return DebugStatus.STEP_INTO;
}
@Override
default void watchpointHit(WatchpointEvent evt, JdiCause cause) {
default DebugStatus watchpointHit(WatchpointEvent evt, JdiCause cause) {
return DebugStatus.BREAK;
}
@Override
default void accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause) {
default DebugStatus accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause) {
return DebugStatus.BREAK;
}
@Override
default void watchpointModified(ModificationWatchpointEvent evt, JdiCause cause) {
default DebugStatus watchpointModified(ModificationWatchpointEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void threadExited(ThreadDeathEvent evt, JdiCause cause) {
default DebugStatus threadExited(ThreadDeathEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void threadStarted(ThreadStartEvent evt, JdiCause cause) {
default DebugStatus threadStarted(ThreadStartEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void vmDied(VMDeathEvent evt, JdiCause cause) {
default DebugStatus vmDied(VMDeathEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void vmDisconnected(VMDisconnectEvent evt, JdiCause cause) {
default DebugStatus vmDisconnected(VMDisconnectEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void vmStarted(VMStartEvent evt, JdiCause cause) {
default DebugStatus vmStarted(VMStartEvent evt, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
@Override
default void processStop(EventSet eventSet, JdiCause cause) {
default DebugStatus processStop(EventSet eventSet, JdiCause cause) {
return DebugStatus.BREAK;
}
@Override
default void processShutdown(Event event, JdiCause cause) {
default DebugStatus processShutdown(Event event, JdiCause cause) {
return DebugStatus.NO_CHANGE;
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -63,7 +63,6 @@ public interface JdiManager extends AutoCloseable {
/**
* Add a listener for JDI's state
*
* @see #getState()
* @param vm the virtual machine
* @param listener the listener to add
*/
@ -72,7 +71,6 @@ public interface JdiManager extends AutoCloseable {
/**
* Remove a listener for JDI's state
*
* @see #getState()
* @param vm the virtual machine
* @param listener the listener to remove
*/
@ -105,7 +103,7 @@ public interface JdiManager extends AutoCloseable {
* Remove a listener for target output
*
* @see #addTargetOutputListener(JdiTargetOutputListener)
* @param listener
* @param listener for output
*/
void removeTargetOutputListener(JdiTargetOutputListener listener);
@ -119,7 +117,7 @@ public interface JdiManager extends AutoCloseable {
/**
* Remove a listener for console output
*
* @param listener
* @param listener for output
*/
void removeConsoleOutputListener(JdiConsoleOutputListener listener);
@ -127,9 +125,8 @@ public interface JdiManager extends AutoCloseable {
* Get an inferior by its JDI-assigned ID
*
* JDI numbers virtual machines incrementally. All vms and created and destroyed by the user.
* See {@link #getVM()}.
*
* @param iid the inferior ID
* @param id the inferior ID
* @return a handle to the inferior, if it exists
*/
VirtualMachine getVM(String id);
@ -150,7 +147,6 @@ public interface JdiManager extends AutoCloseable {
* This may be useful if the manager's command queue is stalled because an inferior is running.
*
* @throws IOException if an I/O error occurs
* @throws InterruptedException
*/
void sendInterruptNow() throws IOException;
@ -162,9 +158,9 @@ public interface JdiManager extends AutoCloseable {
*
* @return a future which completes with the handle to the new vm
*/
CompletableFuture<VirtualMachine> addVM(Connector cx, List<String> args);
VirtualMachine addVM(Connector cx, List<String> args);
CompletableFuture<VirtualMachine> addVM(Connector cx, Map<String, Argument> args);
VirtualMachine addVM(Connector cx, Map<String, Argument> args);
/**
* Remove a vm
@ -178,7 +174,6 @@ public interface JdiManager extends AutoCloseable {
* Execute an arbitrary CLI command, printing output to the CLI console
*
* Note: to ensure a certain thread or inferior has focus for a console command, see
* {@link JdiThread#console(String)} and {@link JdiVM#console(String)}.
*
* @param command the command to execute
* @return a future that completes when JDI has executed the command
@ -189,8 +184,7 @@ public interface JdiManager extends AutoCloseable {
* Execute an arbitrary CLI command, capturing its console output
*
* The output will not be printed to the CLI console. To ensure a certain thread or inferior has
* focus for a console command, see {@link JdiThread#consoleCapture(String)} and
* {@link JdiVM#consoleCapture(String)}.
* focus for a console command
*
* @param command the command to execute
* @return a future that completes with the captured output when JDI has executed the command

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,7 +15,7 @@
*/
package ghidra.dbg.jdi.manager.breakpoint;
import java.util.*;
import java.util.Objects;
import com.sun.jdi.*;
import com.sun.jdi.request.*;
@ -69,14 +69,12 @@ public class JdiBreakpointInfo {
@Override
public boolean equals(Object obj) {
if (!((obj instanceof JdiBreakpointInfo))) {
return false;
if (obj instanceof JdiBreakpointInfo that) {
if (this.request == that.request) {
return true;
}
}
JdiBreakpointInfo that = (JdiBreakpointInfo) obj;
if (this.request != that.request) {
return false;
}
return true;
return false;
}
/**
@ -130,11 +128,11 @@ public class JdiBreakpointInfo {
}
public boolean isEnabled() {
if (request instanceof BreakpointRequest) {
return ((BreakpointRequest) request).isEnabled();
if (request instanceof BreakpointRequest breakreq) {
return breakreq.isEnabled();
}
if (request instanceof WatchpointRequest) {
return ((WatchpointRequest) request).isEnabled();
if (request instanceof WatchpointRequest watchReq) {
return watchReq.isEnabled();
}
return false;
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,29 +15,33 @@
*/
package ghidra.dbg.jdi.manager.impl;
import static ghidra.lifecycle.Unfinished.*;
import static ghidra.lifecycle.Unfinished.TODO;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.sun.jdi.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.event.Event;
import ghidra.dbg.jdi.manager.*;
import ghidra.dbg.jdi.manager.JdiCause.Causes;
import ghidra.util.Msg;
import ghidra.util.datastruct.ListenerSet;
public class JdiManagerImpl implements JdiManager {
public DebugStatus status;
private VirtualMachineManager virtualMachineManager;
private final Map<String, VirtualMachine> vms = new LinkedHashMap<>();
private VirtualMachine curVM = null;
private ThreadReference curThread = null;
private StackFrame curFrame = null;
private Location curLocation = null;
private Event curEvent = null;
private final Map<String, VirtualMachine> unmodifiableVMs = Collections.unmodifiableMap(vms);
protected final ListenerSet<JdiTargetOutputListener> listenersTargetOutput =
@ -77,7 +81,7 @@ public class JdiManagerImpl implements JdiManager {
@Override
public void terminate() {
/**
* NB: can use manager.connectedVMs, because technically, other things could be using the
* NB: can't use manager.connectedVMs, because technically, other things could be using the
* JDI outside of this manager.
*/
for (VirtualMachine vm : vms.values()) {
@ -174,7 +178,7 @@ public class JdiManagerImpl implements JdiManager {
}
@Override
public CompletableFuture<VirtualMachine> addVM(Connector cx, List<String> args) {
public VirtualMachine addVM(Connector cx, List<String> args) {
Map<String, Connector.Argument> arguments = cx.defaultArguments();
if (cx instanceof LaunchingConnector) {
if (arguments.containsKey("command")) {
@ -206,33 +210,39 @@ public class JdiManagerImpl implements JdiManager {
}
@Override
public CompletableFuture<VirtualMachine> addVM(Connector cx,
Map<String, Connector.Argument> args) {
return CompletableFuture.supplyAsync(() -> {
try {
curVM = connectVM(cx, args);
JdiEventHandler eventHandler = new JdiEventHandler(curVM, globalEventHandler);
eventHandler.start();
eventHandler.setState(ThreadReference.THREAD_STATUS_NOT_STARTED, Causes.UNCLAIMED);
eventHandlers.put(curVM, eventHandler);
vms.put(curVM.name(), curVM);
connectors.put(curVM, cx);
}
catch (VMDisconnectedException e) {
System.out.println("Virtual Machine is disconnected.");
return ExceptionUtils.rethrow(e);
}
catch (Exception e) {
return ExceptionUtils.rethrow(e);
}
return curVM;
});
public VirtualMachine addVM(Connector cx, Map<String, Connector.Argument> args) {
try {
setCurrentVM(connectVM(cx, args));
JdiEventHandler eventHandler = new JdiEventHandler(getCurrentVM(), globalEventHandler);
eventHandler.start();
eventHandler.setState(ThreadReference.THREAD_STATUS_NOT_STARTED, Causes.UNCLAIMED);
eventHandlers.put(getCurrentVM(), eventHandler);
vms.put(getCurrentVM().name(), getCurrentVM());
connectors.put(getCurrentVM(), cx);
}
catch (VMDisconnectedException e) {
Msg.error(this, "Virtual Machine is disconnected.");
return null;
}
catch (Exception e) {
Msg.error(this, "Could not connect Virtual Machine", e);
return null;
}
return getCurrentVM();
}
public void addVM(VirtualMachine vm) {
JdiEventHandler eventHandler = new JdiEventHandler(vm, globalEventHandler);
eventHandler.start();
eventHandler.setState(ThreadReference.THREAD_STATUS_NOT_STARTED, Causes.UNCLAIMED);
eventHandlers.put(getCurrentVM(), eventHandler);
vms.put(getCurrentVM().name(), getCurrentVM());
}
@Override
public CompletableFuture<Void> removeVM(VirtualMachine vm) {
if (curVM == vm) {
curVM = null;
if (getCurrentVM() == vm) {
setCurrentVM(null);
}
vms.remove(vm.name());
connectors.remove(vm);
@ -275,4 +285,50 @@ public class JdiManagerImpl implements JdiManager {
return eventHandlers.get(vm);
}
public VirtualMachine getCurrentVM() {
return curVM;
}
public void setCurrentVM(VirtualMachine vm) {
this.curVM = vm;
if (!vms.containsValue(vm)) {
addVM(vm);
}
}
public ThreadReference getCurrentThread() {
if (curThread == null) {
List<ThreadReference> threads = curVM.allThreads();
curThread = threads.getFirst();
}
return curThread;
}
public void setCurrentThread(ThreadReference thread) {
this.curThread = thread;
}
public StackFrame getCurrentFrame() {
return curFrame;
}
public void setCurrentFrame(StackFrame frame) {
this.curFrame = frame;
}
public void setCurrentLocation(Location location) {
this.curLocation = location;
}
public Location getCurrentLocation() {
return curLocation;
}
public void setCurrentEvent(Event event) {
this.curEvent = event;
}
public Event getCurrentEvent() {
return curEvent;
}
}

View File

@ -25,6 +25,7 @@ import ghidra.dbg.DebuggerObjectModel.RefreshBehavior;
import ghidra.dbg.jdi.manager.JdiCause;
import ghidra.dbg.jdi.manager.JdiEventsListenerAdapter;
import ghidra.dbg.jdi.manager.breakpoint.JdiBreakpointInfo;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject;
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
import ghidra.dbg.target.TargetBreakpointSpecContainer;
@ -62,22 +63,25 @@ public class JdiModelTargetBreakpointContainer extends JdiModelTargetObjectImpl
}
@Override
public void breakpointCreated(JdiBreakpointInfo info, JdiCause cause) {
public DebugStatus breakpointCreated(JdiBreakpointInfo info, JdiCause cause) {
changeElements(List.of(), List.of(getTargetBreakpointSpec(info)), Map.of(), "Created");
return DebugStatus.NO_CHANGE;
}
@Override
public void breakpointModified(JdiBreakpointInfo newInfo, JdiBreakpointInfo oldInfo,
public DebugStatus breakpointModified(JdiBreakpointInfo newInfo, JdiBreakpointInfo oldInfo,
JdiCause cause) {
getTargetBreakpointSpec(oldInfo).updateInfo(oldInfo, newInfo, "Modified");
return DebugStatus.NO_CHANGE;
}
@Override
public void breakpointDeleted(JdiBreakpointInfo info, JdiCause cause) {
public DebugStatus breakpointDeleted(JdiBreakpointInfo info, JdiCause cause) {
synchronized (this) {
specsByInfo.remove(info);
}
changeElements(List.of(info.toString()), List.of(), Map.of(), "Deleted");
return DebugStatus.NO_CHANGE;
}
@Override

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -29,10 +29,17 @@ import ghidra.dbg.target.schema.TargetAttributeType;
import ghidra.dbg.target.schema.TargetObjectSchemaInfo;
import ghidra.util.datastruct.ListenerSet;
@TargetObjectSchemaInfo(name = "BreakpointSpec", attributes = {
@TargetAttributeType(name = TargetBreakpointSpec.CONTAINER_ATTRIBUTE_NAME, type = JdiModelTargetBreakpointContainer.class),
@TargetAttributeType(name = TargetBreakpointLocation.SPEC_ATTRIBUTE_NAME, type = JdiModelTargetBreakpointSpec.class),
@TargetAttributeType(type = Void.class) }, canonicalContainer = true)
@TargetObjectSchemaInfo(
name = "BreakpointSpec",
attributes = {
@TargetAttributeType(
name = TargetBreakpointSpec.CONTAINER_ATTRIBUTE_NAME,
type = JdiModelTargetBreakpointContainer.class),
@TargetAttributeType(
name = TargetBreakpointLocation.SPEC_ATTRIBUTE_NAME,
type = JdiModelTargetBreakpointSpec.class),
@TargetAttributeType(type = Void.class) },
canonicalContainer = true)
public class JdiModelTargetBreakpointSpec extends JdiModelTargetObjectImpl
implements TargetBreakpointSpec, JdiModelTargetDeletable {

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -107,6 +107,7 @@ public class JdiModelTargetConnector extends JdiModelTargetObjectImpl
public CompletableFuture<Void> launch(Map<String, ?> args) {
Map<String, Argument> jdiArgs =
JdiModelTargetLauncher.getArguments(cx.defaultArguments(), paramDescs, args);
return getManager().addVM(cx, jdiArgs).thenApply(__ -> null);
getManager().addVM(cx, jdiArgs);
return CompletableFuture.completedFuture(null);
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -27,6 +27,7 @@ import ghidra.async.AsyncUtils;
import ghidra.dbg.DebugModelConventions;
import ghidra.dbg.agent.DefaultTargetModelRoot;
import ghidra.dbg.jdi.manager.*;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
import ghidra.dbg.jdi.model.iface1.*;
import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject;
import ghidra.dbg.target.*;
@ -61,7 +62,7 @@ import ghidra.util.Msg;
required = true,
fixed = true),
@TargetAttributeType(
name = "VirtualMachines",
name = "VMs",
type = JdiModelTargetVMContainer.class,
required = true,
fixed = true),
@ -153,24 +154,26 @@ public class JdiModelTargetRoot extends DefaultTargetModelRoot implements //
*/
@Override
public void vmSelected(VirtualMachine vm, JdiCause cause) {
public DebugStatus vmSelected(VirtualMachine vm, JdiCause cause) {
if (vm.allThreads().isEmpty()) {
JdiModelTargetVM targetVM = vms.getTargetVM(vm);
setFocus(targetVM);
}
// Otherwise, we'll presumably get the =thread-selected event
return DebugStatus.NO_CHANGE;
}
@Override
public void threadSelected(ThreadReference thread, StackFrame frame, JdiCause cause) {
public DebugStatus threadSelected(ThreadReference thread, StackFrame frame, JdiCause cause) {
JdiModelTargetVM vm = vms.getTargetVM(thread.threadGroup().virtualMachine());
JdiModelTargetThread t = vm.threads.getTargetThread(thread);
if (frame == null) {
setFocus(t);
return;
return DebugStatus.NO_CHANGE;
}
JdiModelTargetStackFrame f = t.stack.getTargetFrame(frame);
setFocus(f);
return DebugStatus.NO_CHANGE;
}
public void setAccessible(boolean accessible) {
@ -199,7 +202,8 @@ public class JdiModelTargetRoot extends DefaultTargetModelRoot implements //
Map<String, Argument> defaultArguments = cx.defaultArguments();
Map<String, Argument> jdiArgs = JdiModelTargetLauncher.getArguments(defaultArguments,
JdiModelTargetLauncher.getParameters(defaultArguments), args);
return getManager().addVM(cx, jdiArgs).thenApply(__ -> null);
getManager().addVM(cx, jdiArgs);
return CompletableFuture.completedFuture(null);
}
/**

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -24,6 +24,7 @@ import com.sun.jdi.*;
import ghidra.dbg.DebuggerObjectModel.RefreshBehavior;
import ghidra.dbg.jdi.manager.JdiCause;
import ghidra.dbg.jdi.manager.JdiEventsListenerAdapter;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
import ghidra.dbg.jdi.model.iface1.JdiModelSelectableObject;
import ghidra.dbg.jdi.model.iface1.JdiModelTargetFocusScope;
import ghidra.dbg.target.TargetFocusScope;
@ -32,8 +33,11 @@ import ghidra.dbg.target.schema.*;
import ghidra.program.model.address.Address;
import ghidra.util.Msg;
@TargetObjectSchemaInfo(name = "StackFrame", elements = {
@TargetElementType(type = Void.class) }, attributes = {
@TargetObjectSchemaInfo(
name = "StackFrame",
elements = {
@TargetElementType(type = Void.class) },
attributes = {
@TargetAttributeType(type = Object.class) })
public class JdiModelTargetStackFrame extends JdiModelTargetObjectImpl implements TargetStackFrame, //
//TargetRegisterBank, //
@ -120,10 +124,12 @@ public class JdiModelTargetStackFrame extends JdiModelTargetObjectImpl implement
}
@Override
public void threadSelected(ThreadReference eventThread, StackFrame eventFrame, JdiCause cause) {
public DebugStatus threadSelected(ThreadReference eventThread, StackFrame eventFrame,
JdiCause cause) {
if (eventThread.equals(thread.thread) && eventFrame.equals(frame)) {
((JdiModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this);
}
return DebugStatus.NO_CHANGE;
}
@Override

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -26,6 +26,7 @@ import com.sun.jdi.request.StepRequest;
import ghidra.async.AsyncFence;
import ghidra.dbg.DebuggerObjectModel.RefreshBehavior;
import ghidra.dbg.jdi.manager.*;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
import ghidra.dbg.jdi.model.iface1.*;
import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject;
import ghidra.dbg.target.TargetFocusScope;
@ -34,15 +35,27 @@ import ghidra.dbg.target.schema.*;
import ghidra.lifecycle.Internal;
import ghidra.util.Msg;
@TargetObjectSchemaInfo(name = "Thread", elements = { //
@TargetElementType(type = Void.class) }, attributes = {
@TargetObjectSchemaInfo(
name = "Thread",
elements = { //
@TargetElementType(type = Void.class) },
attributes = {
@TargetAttributeType(name = "Attributes", type = JdiModelTargetAttributesContainer.class),
@TargetAttributeType(name = "Registers", type = JdiModelTargetRegisterContainer.class, required = true, fixed = true),
@TargetAttributeType(name = "Stack", type = JdiModelTargetStack.class, required = true, fixed = true),
@TargetAttributeType(
name = "Registers",
type = JdiModelTargetRegisterContainer.class,
required = true,
fixed = true),
@TargetAttributeType(
name = "Stack",
type = JdiModelTargetStack.class,
required = true,
fixed = true),
@TargetAttributeType(name = "Status", type = Integer.class),
@TargetAttributeType(name = "UID", type = Long.class, fixed = true),
@TargetAttributeType(type = Object.class) //
}, canonicalContainer = true)
},
canonicalContainer = true)
public class JdiModelTargetThread extends JdiModelTargetObjectReference implements //
TargetThread, //
JdiModelTargetAccessConditioned, //
@ -214,7 +227,7 @@ public class JdiModelTargetThread extends JdiModelTargetObjectReference implemen
}
@Override
public void stepComplete(StepEvent evt, JdiCause cause) {
public DebugStatus stepComplete(StepEvent evt, JdiCause cause) {
if (evt.thread().equals(thread)) {
setLocation(evt.location());
changeAttributes(List.of(), List.of(), Map.of( //
@ -222,10 +235,11 @@ public class JdiModelTargetThread extends JdiModelTargetObjectReference implemen
), "Refreshed");
stateChanged(thread.status(), JdiReason.Reasons.STEP);
}
return DebugStatus.BREAK;
}
@Override
public void breakpointHit(BreakpointEvent evt, JdiCause cause) {
public DebugStatus breakpointHit(BreakpointEvent evt, JdiCause cause) {
if (evt.thread().equals(thread)) {
setLocation(evt.location());
changeAttributes(List.of(), List.of(), Map.of( //
@ -233,12 +247,13 @@ public class JdiModelTargetThread extends JdiModelTargetObjectReference implemen
), "Refreshed");
stateChanged(thread.status(), JdiReason.Reasons.BREAKPOINT_HIT);
}
return DebugStatus.BREAK;
}
// Which of these is actually going to fire, i.e. are separate events generated for subclasses?
@Override
public void watchpointHit(WatchpointEvent evt, JdiCause cause) {
public DebugStatus watchpointHit(WatchpointEvent evt, JdiCause cause) {
if (evt.thread().equals(thread)) {
setLocation(evt.location());
changeAttributes(List.of(), List.of(), Map.of( //
@ -246,10 +261,11 @@ public class JdiModelTargetThread extends JdiModelTargetObjectReference implemen
), "Refreshed");
stateChanged(thread.status(), JdiReason.Reasons.WATCHPOINT_HIT);
}
return DebugStatus.BREAK;
}
@Override
public void accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause) {
public DebugStatus accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause) {
if (evt.thread().equals(thread)) {
setLocation(evt.location());
changeAttributes(List.of(), List.of(), Map.of( //
@ -257,13 +273,16 @@ public class JdiModelTargetThread extends JdiModelTargetObjectReference implemen
), "Refreshed");
stateChanged(thread.status(), JdiReason.Reasons.ACCESS_WATCHPOINT_HIT);
}
return DebugStatus.BREAK;
}
@Override
public void threadSelected(ThreadReference eventThread, StackFrame frame, JdiCause cause) {
public DebugStatus threadSelected(ThreadReference eventThread, StackFrame frame,
JdiCause cause) {
if (eventThread.equals(thread) && frame == null) {
((JdiModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this);
}
return DebugStatus.NO_CHANGE;
}
private void stateChanged(int state, JdiReason reason) {
@ -274,8 +293,7 @@ public class JdiModelTargetThread extends JdiModelTargetObjectReference implemen
}
targetVM.vmStateChanged(targetState, reason);
JdiEventHandler eventHandler = getManager().getEventHandler(targetVM.vm);
eventHandler.listenersEvent.invoke().threadStateChanged(thread, state,
JdiCause.Causes.UNCLAIMED, reason);
eventHandler.processThreadStateChanged(thread, state, reason);
}
public void threadStateChanged(TargetExecutionState targetState) {
@ -385,8 +403,9 @@ public class JdiModelTargetThread extends JdiModelTargetObjectReference implemen
}
@Override
public void threadStarted(ThreadStartEvent evt, JdiCause cause) {
public DebugStatus threadStarted(ThreadStartEvent evt, JdiCause cause) {
threadSelected(evt.thread(), null, JdiCause.Causes.UNCLAIMED);
return DebugStatus.NO_CHANGE;
}
@Override

View File

@ -25,6 +25,7 @@ import com.sun.jdi.ThreadReference;
import ghidra.async.AsyncFence;
import ghidra.dbg.DebuggerObjectModel.RefreshBehavior;
import ghidra.dbg.jdi.manager.*;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
import ghidra.dbg.jdi.model.iface1.JdiModelTargetEventScope;
import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
@ -72,7 +73,7 @@ public class JdiModelTargetThreadContainer extends JdiModelTargetObjectImpl
}
@Override
public void threadStateChanged(ThreadReference thread, Integer state, JdiCause cause,
public DebugStatus threadStateChanged(ThreadReference thread, Integer state, JdiCause cause,
JdiReason reason) {
JdiModelTargetThread targetThread = getTargetThread(thread);
TargetExecutionState targetState = targetThread.convertState(state);
@ -80,6 +81,7 @@ public class JdiModelTargetThreadContainer extends JdiModelTargetObjectImpl
TargetEventType eventType = getEventType(reason);
broadcast().event(this, targetThread, eventType,
"Thread " + targetThread.getName() + " state changed", List.of(targetThread));
return DebugStatus.NO_CHANGE;
}
private TargetEventType getEventType(JdiReason reason) {

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -29,6 +29,7 @@ import com.sun.jdi.request.*;
import ghidra.async.AsyncFence;
import ghidra.dbg.DebuggerObjectModel.RefreshBehavior;
import ghidra.dbg.jdi.manager.*;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
import ghidra.dbg.jdi.manager.impl.JdiManagerImpl;
import ghidra.dbg.jdi.model.iface1.*;
import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject;
@ -43,15 +44,35 @@ import ghidra.lifecycle.Internal;
* TODO: Implementing {@link TargetLauncher} here doesn't seem right. While it's convenient from a
* UI perspective, it doesn't make sense semantically.
*/
@TargetObjectSchemaInfo(name = "VM", elements = {
@TargetElementType(type = Void.class) }, attributes = {
@TargetObjectSchemaInfo(
name = "VM",
elements = {
@TargetElementType(type = Void.class) },
attributes = {
@TargetAttributeType(name = "Attributes", type = JdiModelTargetAttributesContainer.class),
@TargetAttributeType(name = "Breakpoints", type = JdiModelTargetBreakpointContainer.class, fixed = true),
@TargetAttributeType(name = "Classes", type = JdiModelTargetClassContainer.class, fixed = true),
@TargetAttributeType(name = "Modules", type = JdiModelTargetModuleContainer.class, fixed = true),
@TargetAttributeType(name = "Threads", type = JdiModelTargetThreadContainer.class, required = true, fixed = true),
@TargetAttributeType(name = "ThreadGroups", type = JdiModelTargetThreadGroupContainer.class, fixed = true),
@TargetAttributeType(type = Object.class) }, canonicalContainer = true)
@TargetAttributeType(
name = "Breakpoints",
type = JdiModelTargetBreakpointContainer.class,
fixed = true),
@TargetAttributeType(
name = "Classes",
type = JdiModelTargetClassContainer.class,
fixed = true),
@TargetAttributeType(
name = "Modules",
type = JdiModelTargetModuleContainer.class,
fixed = true),
@TargetAttributeType(
name = "Threads",
type = JdiModelTargetThreadContainer.class,
required = true,
fixed = true),
@TargetAttributeType(
name = "ThreadGroups",
type = JdiModelTargetThreadGroupContainer.class,
fixed = true),
@TargetAttributeType(type = Object.class) },
canonicalContainer = true)
public class JdiModelTargetVM extends JdiModelTargetObjectImpl implements //
TargetProcess, //
TargetAggregate, //
@ -156,7 +177,6 @@ public class JdiModelTargetVM extends JdiModelTargetObjectImpl implements //
Map<String, Object> attrs = new HashMap<>();
attrs.put("version", vm.version());
attrs.put("description", vm.description());
attrs.put("canAddMethods", Boolean.valueOf(vm.canAddMethod()));
attrs.put("canBeModified", Boolean.valueOf(vm.canBeModified()));
attrs.put("canForceEarlyReturn", Boolean.valueOf(vm.canForceEarlyReturn()));
attrs.put("canGetBytecodes", Boolean.valueOf(vm.canGetBytecodes()));
@ -176,8 +196,6 @@ public class JdiModelTargetVM extends JdiModelTargetObjectImpl implements //
attrs.put("canRedefineClasses", Boolean.valueOf(vm.canRedefineClasses()));
attrs.put("canRequestMonitorEvents", Boolean.valueOf(vm.canRequestMonitorEvents()));
attrs.put("canRequestVMDeathEvent", Boolean.valueOf(vm.canRequestVMDeathEvent()));
attrs.put("canUnrestrictedlyRedefineClasses",
Boolean.valueOf(vm.canUnrestrictedlyRedefineClasses()));
attrs.put("canUseInstanceFilters", Boolean.valueOf(vm.canUseInstanceFilters()));
attrs.put("canUseSourceNameFilters", Boolean.valueOf(vm.canUseSourceNameFilters()));
attrs.put("canWatchFieldAccess", Boolean.valueOf(vm.canWatchFieldAccess()));
@ -219,7 +237,8 @@ public class JdiModelTargetVM extends JdiModelTargetObjectImpl implements //
Map<String, Argument> defaultArguments = cx.defaultArguments();
Map<String, Argument> jdiArgs = JdiModelTargetLauncher.getArguments(defaultArguments,
JdiModelTargetLauncher.getParameters(defaultArguments), args);
return getManager().addVM(cx, jdiArgs).thenApply(__ -> null);
getManager().addVM(cx, jdiArgs);
return CompletableFuture.completedFuture(null);
}
@Override
@ -293,36 +312,42 @@ public class JdiModelTargetVM extends JdiModelTargetObjectImpl implements //
}
@Override
public void vmSelected(VirtualMachine eventVM, JdiCause cause) {
public DebugStatus vmSelected(VirtualMachine eventVM, JdiCause cause) {
if (eventVM.equals(vm)) {
((JdiModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this);
}
return DebugStatus.NO_CHANGE;
}
public void vmStateChanged(TargetExecutionState targetState, JdiReason reason) {
public DebugStatus vmStateChanged(TargetExecutionState targetState, JdiReason reason) {
changeAttributes(List.of(), List.of(), Map.of( //
STATE_ATTRIBUTE_NAME, targetState //
), reason.desc());
return DebugStatus.NO_CHANGE;
}
@Override
public void monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause) {
public DebugStatus monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause) {
System.err.println(this + ":" + evt);
return DebugStatus.NO_CHANGE;
}
@Override
public void monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause) {
public DebugStatus monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause) {
System.err.println(this + ":" + evt);
return DebugStatus.NO_CHANGE;
}
@Override
public void monitorWaited(MonitorWaitedEvent evt, JdiCause cause) {
public DebugStatus monitorWaited(MonitorWaitedEvent evt, JdiCause cause) {
System.err.println(this + ":" + evt);
return DebugStatus.NO_CHANGE;
}
@Override
public void monitorWait(MonitorWaitEvent evt, JdiCause cause) {
public DebugStatus monitorWait(MonitorWaitEvent evt, JdiCause cause) {
System.err.println(this + ":" + evt);
return DebugStatus.NO_CHANGE;
}
protected void updateDisplayAttribute() {

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -27,6 +27,7 @@ import ghidra.async.AsyncUtils;
import ghidra.dbg.DebuggerObjectModel.RefreshBehavior;
import ghidra.dbg.jdi.manager.JdiCause;
import ghidra.dbg.jdi.manager.JdiEventsListenerAdapter;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
import ghidra.dbg.target.TargetEventScope.TargetEventType;
import ghidra.dbg.target.schema.*;
import ghidra.util.Msg;
@ -48,14 +49,14 @@ public class JdiModelTargetVMContainer extends JdiModelTargetObjectImpl
protected final Map<String, JdiModelTargetVM> vmsById = new WeakValueHashMap<>();
public JdiModelTargetVMContainer(JdiModelTargetRoot session) {
super(session, "VirtualMachines");
super(session, "VMs");
this.session = session;
impl.getManager().addEventsListener(null, this);
}
@Override
public void vmStarted(VMStartEvent event, JdiCause cause) {
public DebugStatus vmStarted(VMStartEvent event, JdiCause cause) {
VirtualMachine vm = event.virtualMachine();
JdiModelTargetVM target = getTargetVM(vm);
// TODO: Move PROCESS_CREATED here to restore proper order of event reporting
@ -69,10 +70,11 @@ public class JdiModelTargetVMContainer extends JdiModelTargetObjectImpl
});
changeElements(List.of(), List.of(target), Map.of(), "Added");
return DebugStatus.NO_CHANGE;
}
@Override
public void vmDied(VMDeathEvent event, JdiCause cause) {
public DebugStatus vmDied(VMDeathEvent event, JdiCause cause) {
VirtualMachine vm = event.virtualMachine();
JdiModelTargetVM tgtVM = vmsById.get(vm.name());
broadcast().event(session, null, TargetEventType.PROCESS_EXITED, "VM " + vm.name(),
@ -83,6 +85,7 @@ public class JdiModelTargetVMContainer extends JdiModelTargetObjectImpl
getManager().removeVM(vm);
}
changeElements(List.of(vm.name()), List.of(), Map.of(), "Removed");
return DebugStatus.NO_CHANGE;
}
protected void gatherThreads(List<? super JdiModelTargetThread> into, JdiModelTargetVM vm,
@ -96,30 +99,32 @@ public class JdiModelTargetVMContainer extends JdiModelTargetObjectImpl
}
@Override
public void threadStarted(ThreadStartEvent event, JdiCause cause) {
public DebugStatus threadStarted(ThreadStartEvent event, JdiCause cause) {
ThreadReference thread = event.thread();
JdiModelTargetVM vm = getTargetVM(thread.threadGroup().virtualMachine());
if (!vmsById.containsValue(vm)) {
Msg.info(this, event + " ignored as vm may have exited");
return;
return DebugStatus.NO_CHANGE;
}
JdiModelTargetThread targetThread = vm.threads.threadCreated(thread);
broadcast().event(session, targetThread, TargetEventType.THREAD_CREATED,
"Thread " + thread.name() + " started", List.of(targetThread));
return DebugStatus.NO_CHANGE;
}
@Override
public void threadExited(ThreadDeathEvent event, JdiCause cause) {
public DebugStatus threadExited(ThreadDeathEvent event, JdiCause cause) {
ThreadReference thread = event.thread();
JdiModelTargetVM tgtVM = vmsById.get(thread.virtualMachine().name());
JdiModelTargetThread targetThread = tgtVM.threads.threadsById.get(thread.name());
broadcast().event(session, targetThread, TargetEventType.THREAD_EXITED,
"Thread " + thread.name() + " exited", List.of(targetThread));
tgtVM.threads.threadExited(thread);
return DebugStatus.NO_CHANGE;
}
@Override
public void libraryLoaded(VirtualMachine vm, String name, JdiCause cause) {
public DebugStatus classLoaded(VirtualMachine vm, String name, JdiCause cause) {
/*
JdiModelTargetVM vm = getTargetInferior(inf);
JdiModelTargetModule module = vm.modules.libraryLoaded(name);
@ -128,10 +133,11 @@ public class JdiModelTargetVMContainer extends JdiModelTargetObjectImpl
.event(parent, null, TargetEventType.MODULE_LOADED,
"Library " + name + " loaded", List.of(module));
*/
return DebugStatus.NO_CHANGE;
}
@Override
public void libraryUnloaded(VirtualMachine vm, String name, JdiCause cause) {
public DebugStatus classUnloaded(VirtualMachine vm, String name, JdiCause cause) {
/*
JdiModelTargetVM vm = getTargetInferior(inf);
JdiModelTargetModule module = vm.modules.getTargetModuleIfPresent(name);
@ -141,6 +147,7 @@ public class JdiModelTargetVMContainer extends JdiModelTargetObjectImpl
"Library " + name + " unloaded", List.of(module));
vm.modules.libraryUnloaded(name);
*/
return DebugStatus.NO_CHANGE;
}
private void updateUsingVMs(Map<String, VirtualMachine> byName) {

View File

@ -0,0 +1,130 @@
/* ###
* 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;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.SocketAddress;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ghidra.app.plugin.core.debug.gui.tracermi.launcher.*;
import ghidra.app.plugin.core.debug.gui.tracermi.launcher.ScriptAttributesParser.ScriptAttributes;
import ghidra.app.plugin.core.debug.gui.tracermi.launcher.ScriptAttributesParser.TtyCondition;
import ghidra.dbg.jdi.rmi.jpda.JdiClientThread;
import ghidra.debug.api.ValStr;
import ghidra.debug.api.tracermi.TerminalSession;
import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor;
/**
* A launcher implemented by a simple UNIX shell script.
*
* <p>
* The script must start with an attributes header in a comment block. See
* {@link ScriptAttributesParser}.
*/
public class JavaTraceRmiLaunchOffer extends AbstractScriptTraceRmiLaunchOffer {
public static final String REM = "//";
public static final int REM_LEN = REM.length();
/**
* Create a launch offer from the given shell script.
*
* @param plugin the launcher service plugin
* @param program the current program, usually the target image. In general, this should be used
* for at least two purposes. 1) To populate the default command line. 2) To ensure
* the target image is mapped in the resulting target trace.
* @param script the script file that implements this offer
* @return the offer
* @throws FileNotFoundException if the script file does not exist
*/
public static JavaTraceRmiLaunchOffer create(TraceRmiLauncherServicePlugin plugin,
Program program, File script) throws FileNotFoundException {
ScriptAttributesParser parser = new ScriptAttributesParser() {
@Override
protected boolean ignoreLine(int lineNo, String line) {
return line.isBlank();
}
@Override
protected String removeDelimiter(String line) {
String stripped = line.stripLeading();
if (!stripped.startsWith(REM)) {
return null;
}
return stripped.substring(REM_LEN);
}
};
ScriptAttributes attrs = parser.parseFile(script);
return new JavaTraceRmiLaunchOffer(plugin, program, script,
"JAVA:" + script.getName(), attrs);
}
private JavaTraceRmiLaunchOffer(TraceRmiLauncherServicePlugin plugin,
Program program, File script, String configName, ScriptAttributes attrs) {
super(plugin, program, script, configName, attrs);
}
boolean hasKeyReally(Map<String, String> env, String key) {
String val = env.get(key);
return val != null && !val.isBlank();
}
@Override
protected void launchBackEnd(TaskMonitor monitor, Map<String, TerminalSession> sessions,
Map<String, ValStr<?>> args, SocketAddress address) throws Exception {
List<String> commandLine = new ArrayList<>();
Map<String, String> env = new HashMap<>(System.getenv());
prepareSubprocess(commandLine, env, args, address);
for (Map.Entry<String, TtyCondition> ent : attrs.extraTtys().entrySet()) {
if (!ent.getValue().isActive(args)) {
continue;
}
NullPtyTerminalSession ns = nullPtyTerminal();
env.put(ent.getKey(), ns.name());
sessions.put(ns.name(), ns);
}
if (hasKeyReally(env, "OPT_JSHELL_PATH")) {
String classPath = computeClassPath(env);
commandLine.add(0, "--startup");
commandLine.add(0, "--class-path=" + classPath);
commandLine.add(0, env.get("OPT_JSHELL_PATH"));
sessions.put("Shell",
runInTerminal(commandLine, env, script.getParentFile(), sessions.values()));
}
else {
JdiClientThread thread = new JdiClientThread(env);
thread.start();
}
}
private String computeClassPath(Map<String, String> env) {
String sep = File.pathSeparator;
return Stream.of(System.getProperty("java.class.path").split(sep))
.filter(p -> new File(p).exists())
.collect(Collectors.joining(sep));
}
@Override
public boolean requiresImage() {
return false;
}
}

View File

@ -0,0 +1,51 @@
/* ###
* 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;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.debug.gui.tracermi.launcher.AbstractTraceRmiLaunchOpinion;
import ghidra.app.plugin.core.debug.gui.tracermi.launcher.TraceRmiLauncherServicePlugin;
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
public class JavaTraceRmiLaunchOpinion extends AbstractTraceRmiLaunchOpinion {
@Override
public Collection<TraceRmiLaunchOffer> getOffers(TraceRmiLauncherServicePlugin plugin,
Program program) {
return getScriptPaths(plugin.getTool())
.flatMap(rf -> Stream.of(rf.listFiles(crf -> crf.getName().endsWith(".jsh"))))
.flatMap(sf -> createOffer(plugin, program, sf))
.collect(Collectors.toList());
}
protected Stream<TraceRmiLaunchOffer> createOffer(TraceRmiLauncherServicePlugin plugin,
Program program, ResourceFile scriptFile) {
try {
return Stream.of(JavaTraceRmiLaunchOffer.create(plugin, program,
scriptFile.getFile(false)));
}
catch (Exception e) {
Msg.error(this, "Could not offer " + scriptFile + ": " + e.getMessage(), e);
return Stream.of();
}
}
}

View File

@ -0,0 +1,56 @@
/* ###
* 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.io.IOException;
import ghidra.GhidraApplicationLayout;
import ghidra.framework.*;
/**
* This is a convenience class for preparing the JDI Client in a stand-alone jshell.
*
* <p>
* For any of the Ghidra stuff to work (including logging), the application needs to be initialized.
* If we're in Ghidra's JVM, then we do not need this. Do not call it! If we're in a separate
* subprocess, e.g., a stand-alone jshell, then we need to call this. Putting it all here lets the
* scripts/user avoid tons of imports.
*/
public class GhidraJdiInit {
/**
* Initialize the Ghidra application using all the defaults.
*
* @throws IOException if the file system can't be read
*/
public static void initApp() throws IOException {
GhidraApplicationLayout layout = new GhidraApplicationLayout();
GhidraApplicationConfiguration config = new GhidraApplicationConfiguration();
config.setShowSplashScreen(false);
Application.initializeApplication(layout, config);
}
/**
* Initialize the Ghidra application in headless mode.
*
* @throws IOException if the file system can't be read
*/
public static void initHeadless() throws IOException {
GhidraApplicationLayout layout = new GhidraApplicationLayout();
HeadlessGhidraApplicationConfiguration config =
new HeadlessGhidraApplicationConfiguration();
Application.initializeApplication(layout, config);
}
}

View File

@ -0,0 +1,129 @@
/* ###
* 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.connect.AttachingConnector;
import com.sun.jdi.connect.Connector;
import com.sun.jdi.connect.Connector.Argument;
import ghidra.dbg.jdi.manager.JdiCause.Causes;
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<String, String> env;
private final Mode mode;
private JdiManagerImpl manager;
private TraceJdiManager traceJdiManager;
public JdiClientThread(Map<String, String> env) {
this.env = env;
this.mode = computeMode();
}
/**
* Compute/detect the launch mode using the environment map.
*
* <p>
* 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();
}
@Override
public void run() {
try {
manager = new JdiManagerImpl();
traceJdiManager = new TraceJdiManager(manager, env);
Connector cx = switch (mode) {
case ATTACH_PORT -> findConnectorByArgKey("port");
case ATTACH_PID -> findConnectorByArgKey("pid");
case LAUNCH -> manager.getVirtualMachineManager().defaultConnector();
};
Map<String, Argument> args = cx.defaultArguments();
putArguments(args);
if (manager.addVM(cx, args) != null) {
traceJdiManager.getCommands().ghidraTraceSyncEnable();
traceJdiManager.getHooks().vmStarted(null, Causes.UNCLAIMED);
}
else {
// Nothing. addVM should already have reported the error.
}
}
catch (Exception e) {
Msg.error(this, "Could not start the JDI client", e);
}
}
protected void putArguments(Map<String, Argument> 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 TraceJdiManager mgr() {
return traceJdiManager;
}
public TraceJdiCommands cmds() {
return traceJdiManager.getCommands();
}
}

View File

@ -0,0 +1,91 @@
/* ###
* 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.HashMap;
import java.util.Map;
import ghidra.app.plugin.core.debug.client.tracermi.DefaultMemoryMapper;
import ghidra.app.plugin.core.debug.client.tracermi.DefaultRegisterMapper;
import ghidra.program.model.lang.*;
import ghidra.program.util.DefaultLanguageService;
public class TraceJdiArch {
private LanguageID langID;
private Language language;
private final LanguageService languageService = DefaultLanguageService.getLanguageService();
public String getArch() {
Map<String, String> env = new HashMap<>(System.getenv());
String arch = "JVM";
if (env.containsKey("OPT_ARCH")) {
arch = env.get("OPT_ARCH");
}
return arch.equals("Dalvik") ? "Dalvik" : "JVM";
}
public String getEndian() {
return "big";
}
public String getOSABI() {
Map<String, String> env = new HashMap<>(System.getenv());
String arch = "JVM";
if (env.containsKey("OPT_ARCH")) {
arch = env.get("OPT_ARCH");
}
return arch.equals("Dalvik") ? "Dalvik:LE:32:default" : "JVM:BE:32:default";
}
public LanguageID computeGhidraLanguage() {
return new LanguageID(getOSABI());
}
public CompilerSpecID computeGhidraCompiler(LanguageID id) {
return new CompilerSpecID("default");
}
public void computeGhidraLcsp() {
langID = computeGhidraLanguage();
try {
language = languageService.getLanguage(langID);
}
catch (LanguageNotFoundException e) {
throw new RuntimeException(e);
}
}
public DefaultMemoryMapper computeMemoryMapper() {
if (langID == null) {
computeGhidraLcsp();
}
return new DefaultMemoryMapper(langID);
}
public DefaultRegisterMapper computeRegisterMapper() {
if (langID == null) {
computeGhidraLcsp();
}
return new DefaultRegisterMapper(langID);
}
public Language getLanguage() {
return language;
}
}

View File

@ -0,0 +1,486 @@
/* ###
* 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.*;
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.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;
public HookState(TraceJdiCommands cmds) {
this.cmds = cmds;
this.batch = null;
}
public void ensureBatch() {
if (batch == null) {
batch = 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 boolean firstPass;
boolean classes;
boolean modules;
boolean regions;
boolean threads;
boolean breaks;
boolean events;
Set<Object> visited;
public VmState(TraceJdiManager manager) {
this.manager = manager;
this.cmds = manager.getCommands();
this.firstPass = true;
this.classes = false;
this.modules = false;
this.regions = false;
this.threads = false;
this.breaks = false;
this.events = false;
this.visited = new HashSet<>();
}
public void recordState(String description) {
boolean first = this.firstPass;
if (description != null) {
cmds.state.trace.snapshot(description, "", null);
}
this.firstPass = false;
if (first) {
cmds.putProcesses();
}
VirtualMachine vm = manager.getJdi().getCurrentVM();
cmds.putVM("VMs", vm);
setState(vm);
if (first || threads) {
String path = cmds.getPath(vm);
cmds.putThreadContainer(path, vm.allThreads(), false);
cmds.putThreadGroupContainer(path, vm.topLevelThreadGroups());
threads = false;
}
cmds.putCurrentLocation();
ThreadReference thread = manager.getJdi().getCurrentThread();
if (thread != null) {
cmds.createLink(vm, "_event_thread", thread);
if (first || !visited.contains(thread)) {
cmds.putFrames();
visited.add(thread);
}
StackFrame frame = manager.getJdi().getCurrentFrame();
if (frame != null) {
if (first || !visited.contains(frame)) {
cmds.putReg(frame);
visited.add(frame);
}
}
}
if (classes) {
classes = false;
cmds.putReferenceTypeContainer(cmds.getPath(vm) + ".Classes", vm.allClasses());
}
if (first || modules) {
modules = false;
cmds.putModuleReferenceContainer();
}
if (first || breaks) {
breaks = false;
cmds.putBreakpoints();
}
if (first || events) {
events = false;
cmds.putEvents();
}
}
public void setState(VirtualMachine vm) {
boolean stopped = false;
for (ThreadReference thread : vm.allThreads()) {
stopped |= cmds.setStatus(thread, stopped);
}
cmds.setStatus(vm, stopped);
Process process = vm.process();
if (process != null) {
cmds.setStatus(process, stopped);
}
if (stopped) {
breaks = true;
events = true;
}
}
public void recordStateContinued(VirtualMachine vm) {
Process proc = vm.process();
String path = cmds.getPath(proc);
if (path != null) {
cmds.setValue(path, "Alive", proc.isAlive());
}
setState(vm);
}
public void recordStateExited(VirtualMachine eventVM, String description) {
VirtualMachine vm = manager.getJdi().getCurrentVM();
String path = cmds.getPath(vm);
int exitCode = -1;
try {
Process process = eventVM.process();
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");
}
}
catch (IllegalThreadStateException e) {
// IGNORE
}
if (description != null) {
cmds.state.trace.snapshot(description, "", null);
}
cmds.setValue(path, "ExitCode", exitCode);
cmds.setValue(path, TraceJdiManager.STATE_ATTRIBUTE_NAME, "TERMINATED");
}
}
public class TraceJdiHooks implements JdiEventsListenerAdapter {
private TraceJdiManager manager;
private TraceJdiCommands cmds;
private HookState hookState;
private Map<VirtualMachine, VmState> vmStates = new HashMap<>();
public TraceJdiHooks(TraceJdiManager manager) {
this.manager = manager;
this.cmds = manager.getCommands();
}
private void setCommands(TraceJdiCommands commands) {
this.cmds = commands;
hookState = new HookState(commands);
}
@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);
cmds.putVMs();
enableCurrentVM();
}
hookState.endBatch();
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus vmDied(VMDeathEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("VMDeathEvent");
}
@Override
public DebugStatus vmDisconnected(VMDisconnectEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
VirtualMachine eventVM = evt.virtualMachine();
VmState state = vmStates.get(eventVM);
try (RmiTransaction tx = trace.openTx("VM disconnected: " + eventVM.description())) {
state.recordStateExited(eventVM, "VM disconnected");
}
disableCurrentVM();
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus threadStarted(ThreadStartEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("ThreadStartEvent");
}
@Override
public DebugStatus threadExited(ThreadDeathEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("ThreadDeathEvent");
}
@Override
public DebugStatus stepComplete(StepEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return DebugStatus.BREAK;
}
@Override
public DebugStatus breakpointHit(BreakpointEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return DebugStatus.BREAK;
}
@Override
public DebugStatus accessWatchpointHit(AccessWatchpointEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return DebugStatus.BREAK;
}
@Override
public DebugStatus watchpointHit(WatchpointEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return DebugStatus.BREAK;
}
@Override
public DebugStatus watchpointModified(ModificationWatchpointEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return DebugStatus.BREAK;
}
@Override
public DebugStatus exceptionHit(ExceptionEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("ExceptionEvent");
}
@Override
public DebugStatus methodEntry(MethodEntryEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("MethodEntryEvent");
}
@Override
public DebugStatus methodExit(MethodExitEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("MethodExitEvent");
}
@Override
public DebugStatus classPrepare(ClassPrepareEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("ClassPrepareEvent");
}
@Override
public DebugStatus classUnload(ClassUnloadEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("ClassUnloadEvent");
}
@Override
public DebugStatus monitorContendedEnter(MonitorContendedEnterEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("MonitorContendedEnterEvent");
}
@Override
public DebugStatus monitorContendedEntered(MonitorContendedEnteredEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("MonitorContendedEnteredEvent");
}
@Override
public DebugStatus monitorWait(MonitorWaitEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("MonitorWaitEvent");
}
@Override
public DebugStatus monitorWaited(MonitorWaitedEvent evt, JdiCause cause) {
RmiTrace trace = cmds.state.trace;
if (trace == null) {
return DebugStatus.NO_CHANGE;
}
onStop(evt, trace);
return manager.getReturnStatus("MonitorWaitedEvent");
}
@Override
public DebugStatus threadStateChanged(ThreadReference thread, Integer state, JdiCause cause,
JdiReason reason) {
return DebugStatus.NO_CHANGE;
}
void onStop(Event evt, RmiTrace trace) {
VirtualMachine vm = manager.getJdi().getCurrentVM();
if (evt != null) {
setCurrent(evt);
vm = evt.virtualMachine();
}
VmState state = vmStates.get(vm);
state.visited.clear();
hookState.ensureBatch();
try (RmiTransaction tx = trace.openTx("Stopped")) {
state.recordState("Stopped");
cmds.activate(null);
}
hookState.endBatch();
}
private void setCurrent(Event event) {
VirtualMachine eventVM = event.virtualMachine();
JdiManagerImpl jdi = manager.getJdi();
jdi.setCurrentEvent(event);
jdi.setCurrentVM(eventVM);
if (event instanceof LocatableEvent locEvt) {
jdi.setCurrentLocation(locEvt.location());
ThreadReference eventThread = locEvt.thread();
jdi.setCurrentThread(eventThread);
try {
jdi.setCurrentFrame(eventThread.frame(0));
}
catch (IncompatibleThreadStateException e) {
// IGNORE
}
}
}
void onContinue() {
VirtualMachine currentVM = manager.getJdi().getCurrentVM();
VmState state = vmStates.get(currentVM);
hookState.ensureBatch();
try (RmiTransaction tx = cmds.state.trace.openTx("Continue")) {
state.recordStateContinued(currentVM);
cmds.activate(null);
}
hookState.endBatch();
}
public void installHooks() {
manager.getJdi().addEventsListener(null, this);
}
public void removeHooks() {
manager.getJdi().removeEventsListener(null, this);
}
public void enableCurrentVM() {
VirtualMachine vm = manager.getJdi().getCurrentVM();
VmState state = new VmState(manager);
vmStates.put(vm, state);
state.recordState("VM started");
cmds.activate(null);
}
public void disableCurrentVM() {
VirtualMachine vm = manager.getJdi().getCurrentVM();
VmState state = vmStates.get(vm);
state.visited.clear();
vmStates.remove(vm);
vm.dispose();
}
public void setState(VirtualMachine vm) {
VmState state = vmStates.get(vm);
state.setState(vm);
}
}

View File

@ -0,0 +1,368 @@
/* ###
* 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.HashMap;
import java.util.Map;
import com.sun.jdi.*;
import ghidra.app.plugin.core.debug.client.tracermi.*;
import ghidra.dbg.jdi.manager.impl.DebugStatus;
import ghidra.dbg.jdi.manager.impl.JdiManagerImpl;
import ghidra.dbg.target.schema.EnumerableTargetObjectSchema;
import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.program.model.address.*;
import ghidra.util.Msg;
public class TraceJdiManager {
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";
private JdiManagerImpl manager;
private TraceJdiArch arch;
private TraceJdiHooks hooks;
private TraceJdiMethods methods;
private TraceJdiCommands commands;
Map<String, Object> objectRegistry = new HashMap<>();
Map<Object, String> reverseRegistry = new HashMap<>();
RmiMethodRegistry remoteMethodRegistry = new RmiMethodRegistry();
Map<Object, Boolean> scopeRegistry = new HashMap<>();
protected final AddressSpace ram = new GenericAddressSpace("ram", 64, AddressSpace.TYPE_RAM, 0);
protected Long ramIndex = Long.valueOf(BLOCK_SIZE);
protected final AddressSpace pool =
new GenericAddressSpace("constantPool", 64, AddressSpace.TYPE_RAM, 0);
protected Long poolIndex = Long.valueOf(0x0L);
public AddressRangeImpl defaultRange;
private Map<String, AddressRange> addressRangeByMethod = new HashMap<>();
private Map<String, Method> methodsByKey = new HashMap<>();
private Map<ReferenceType, AddressRange> addressRangeByClass = new HashMap<>();
private Map<ReferenceType, AddressRange> cpAddressRangeByClass = new HashMap<>();
private Map<String, DebugStatus> returnStatusMap = new HashMap<>();
TargetObjectSchema rootSchema;
public TraceJdiManager(JdiManagerImpl manager, Map<String, String> 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) {
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);
hooks.installHooks();
}
public JdiManagerImpl getJdi() {
return manager;
}
public TraceJdiArch getArch() {
return arch;
}
public TraceJdiCommands getCommands() {
return commands;
}
public TraceJdiMethods getMethods() {
return methods;
}
public TraceJdiHooks getHooks() {
return hooks;
}
public RmiClient getClient() {
return commands.state.client;
}
public void registerRemoteMethod(TraceJdiMethods 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);
if (annot == null) {
return;
}
action = annot.action();
if (annot.display() != null) {
display = annot.display();
}
if (annot.description() != null) {
description = annot.description();
}
int pcount = m.getParameterCount();
if (pcount < 1) {
return;
}
TargetObjectSchema schema = EnumerableTargetObjectSchema.ANY;
RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, action, display,
description, schema, methods, m);
remoteMethodRegistry.putMethod(name, method);
}
public AddressSpace getAddressSpace(String name) {
switch (name) {
case "ram":
return ram;
case "constantPool":
return pool;
default:
return null;
}
}
public AddressRange putAddressRange(ReferenceType cls, AddressSet set) {
if (set.isEmpty()) {
addressRangeByClass.put(cls, defaultRange);
return defaultRange;
}
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);
}
public ReferenceType getReferenceTypeForAddress(Address address) {
for (ReferenceType ref : addressRangeByClass.keySet()) {
AddressRange range = addressRangeByClass.get(ref);
if (range.contains(address)) {
return ref;
}
}
return null;
}
public AddressRange getPoolAddressRange(ReferenceType cls, int sz) {
if (cls == null) {
return defaultRange;
}
AddressRange range = cpAddressRangeByClass.get(cls);
if (range == null) {
registerConstantPool(cls, sz);
range = cpAddressRangeByClass.get(cls);
}
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) {
Address start = pool.getAddress(poolIndex);
AddressRangeImpl range =
new AddressRangeImpl(start, start.add(length - 1));
if (!cpAddressRangeByClass.containsKey(declaringType)) {
cpAddressRangeByClass.put(declaringType, range);
poolIndex += length; //bytecodes.length;
}
}
}
}
}
public ReferenceType getReferenceTypeForPoolAddress(Address address) {
for (ReferenceType ref : cpAddressRangeByClass.keySet()) {
AddressRange range = cpAddressRangeByClass.get(ref);
if (range.contains(address)) {
return ref;
}
}
return null;
}
public AddressRange getAddressRange(Method method) {
if (method == null) {
return defaultRange;
}
AddressRange range = addressRangeByMethod.get(methodToKey(method));
if (range == null) {
return defaultRange;
}
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);
}
}
return null;
}
public Address getAddressFromLocation(Location location) {
AddressRange addressRange = getAddressRange(location.method());
if (addressRange == null) {
return getAddressSpace("ram").getAddress(-1L);
}
long codeIndex = location.codeIndex();
return addressRange.getMinAddress().add(codeIndex < 0 ? 0 : codeIndex);
}
public Location getLocation(Address address) {
Method method = getMethodForAddress(address);
long codeIndex = address.subtract(getAddressRange(method).getMinAddress());
return method.locationOfCodeIndex(codeIndex);
}
public static String methodToKey(Method method) {
return method.toString();
}
public AddressRange registerAddressesForMethod(Method method) {
byte[] bytecodes = method.bytecodes();
if (bytecodes == null) {
return null;
}
int length = bytecodes.length;
if (length <= 0) {
return null;
}
synchronized (addressRangeByMethod) {
Address start = ram.getAddress(ramIndex);
AddressRangeImpl range =
new AddressRangeImpl(start, start.add(length - 1));
String key = methodToKey(method);
//System.err.println(Long.toHexString(ramIndex)+":"+key+":"+bytecodes.length+" "+Long.toHexString(bytecodes[0]));
if (!methodsByKey.containsKey(key)) {
methodsByKey.put(key, method);
}
if (!addressRangeByMethod.containsKey(key)) {
addressRangeByMethod.put(key, range);
ramIndex = range.getMaxAddress().getUnsignedOffset() + STATIC_METHOD_SEPARATION;
return range;
}
return addressRangeByMethod.get(key);
}
}
public String recordPath(Object obj, String path, String key) {
if (path.endsWith("]")) {
path += "." + key;
}
else {
path += key(key);
}
objectRegistry.put(path, obj);
if (!reverseRegistry.containsKey(obj)) {
reverseRegistry.put(obj, path);
}
return path;
}
public Object objForPath(String path) {
return objectRegistry.get(path);
}
public String pathForObj(Object obj) {
if (!reverseRegistry.containsKey(obj)) {
if (objectRegistry.containsValue(obj)) {
Msg.error(this, "MISSING path for " + obj);
}
return null;
}
return reverseRegistry.get(obj);
}
public boolean getScope(Object ctxt) {
Boolean scope = scopeRegistry.get(ctxt);
if (scope == null) {
scope = true;
}
return scope;
}
public void toggleScope(Object ctxt) {
Boolean scope = scopeRegistry.get(ctxt);
if (scope == null) {
scope = true;
}
scopeRegistry.put(ctxt, !scope);
}
private String sanitize(String name) {
if (name == null) {
return name;
}
name = name.replace("[", "");
name = name.replace("]", "");
return name;
}
public String key(String key) {
return "[" + sanitize(key) + "]";
}
public DebugStatus getReturnStatus(String eventName) {
return returnStatusMap.get(eventName);
}
public void setReturnStatus(String eventName, String statusString) {
DebugStatus status = DebugStatus.BREAK;
try {
status = DebugStatus.valueOf(statusString.toUpperCase());
}
catch (IllegalArgumentException e) {
// IGNORE
}
returnStatusMap.put(eventName, status);
}
public void bumpRamIndex() {
synchronized (addressRangeByMethod) {
if (ramIndex % BLOCK_SIZE != 0) {
ramIndex = (ramIndex / BLOCK_SIZE + 1) * BLOCK_SIZE;
}
}
}
}

View File

@ -0,0 +1,787 @@
<?xml version="1.0"?>
<context>
<schema name="Debugger" elementResync="NEVER" attributeResync="ALWAYS">
<interface name="Access" />
<interface name="Attacher" />
<interface name="EventScope" />
<interface name="Launcher" />
<interface name="ActiveScope" />
<interface name="FocusScope" />
<interface name="Aggregate" />
<element schema="VOID" />
<attribute name="_accessible" schema="BOOL" required="yes" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="_event_process" schema="OBJECT" hidden="yes" />
<attribute name="_event_thread" schema="OBJECT" hidden="yes" />
<attribute name="_parameters" schema="MAP_PARAMETERS" required="yes" hidden="yes" />
<attribute name="_focus" schema="OBJECT" required="no" hidden="yes" />
<attribute name="_system" schema="OBJECT" hidden="no" />
<attribute name="Available" schema="AvailableContainer" fixed="yes" />
<attribute name="Connectors" schema="ConnectorContainer" fixed="yes" />
<attribute name="VMs" schema="VMContainer" required="yes" fixed="yes" />
<attribute name="Settings" schema="OBJECT" />
<attribute name="State" schema="OBJECT" />
<attribute name="Utility" schema="OBJECT" />
<attribute schema="VOID" />
</schema>
<schema name="VMContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="VirtualMachine" />
<interface name="Aggregate" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="ConnectorContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="OBJECT" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="Launch process" schema="ProcessLaunchConnector" required="yes" fixed="yes" />
<attribute name="Attach to process" schema="ProcessAttachConnector" required="yes" fixed="yes" />
<attribute name="Load trace/dump" schema="TraceOrDumpConnector" required="yes" fixed="yes" />
<attribute name="Attach to kernel" schema="KernelConnector" required="yes" fixed="yes" />
<attribute schema="VOID" />
</schema>
<schema name="AvailableContainer" canonical="yes" elementResync="ALWAYS" attributeResync="NEVER">
<element schema="Available" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="KernelConnector" elementResync="NEVER" attributeResync="NEVER">
<interface name="Launcher" />
<element schema="VOID" />
<attribute name="_parameters" schema="MAP_PARAMETERS" required="yes" fixed="yes" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="ProcessAttachConnector" elementResync="NEVER" attributeResync="NEVER">
<interface name="Launcher" />
<element schema="VOID" />
<attribute name="_parameters" schema="MAP_PARAMETERS" required="yes" fixed="yes" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="ProcessLaunchConnector" elementResync="NEVER" attributeResync="NEVER">
<interface name="Launcher" />
<element schema="VOID" />
<attribute name="_parameters" schema="MAP_PARAMETERS" required="yes" fixed="yes" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="TraceOrDumpConnector" elementResync="NEVER" attributeResync="NEVER">
<interface name="Launcher" />
<element schema="VOID" />
<attribute name="_parameters" schema="MAP_PARAMETERS" required="yes" fixed="yes" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="VirtualMachine" elementResync="NEVER" attributeResync="ONCE">
<interface name="Process" />
<interface name="Aggregate" />
<interface name="Access" />
<interface name="Activatable" />
<interface name="ExecutionStateful" />
<interface name="Interpreter" />
<interface name="Interruptible" />
<interface name="Deletable" />
<interface name="Detachable" />
<interface name="Killable" />
<interface name="Resumable" />
<interface name="Steppable" />
<interface name="Interruptible" />
<element schema="VOID" />
<attribute name="_accessible" schema="BOOL" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="_state" schema="EXECUTION_STATE" required="no" hidden="yes" />
<attribute name="_prompt" schema="STRING" required="yes" hidden="yes" />
<attribute name="Attributes" schema="SessionAttributes" fixed="yes" />
<attribute name="Devices" schema="OBJECT" />
<attribute name="Process" schema="ProcessRef" required="yes" fixed="yes" />
<attribute name="Processes" schema="ProcessContainer" required="yes" fixed="yes" />
<attribute name="ThreadGroups" schema="ThreadGroupReferenceContainer" required="yes" fixed="yes" />
<attribute name="Threads" schema="ThreadContainer" required="yes" fixed="yes" />
<attribute name="Breakpoints" schema="BreakpointContainer" required="yes" fixed="yes" />
<attribute name="Events" schema="EventContainer" required="yes" fixed="yes" />
<attribute name="Classes" schema="ReferenceTypeContainer" required="yes" fixed="yes" />
<attribute name="Memory" schema="Memory" required="yes" fixed="yes" />
<attribute name="ModuleRefs" schema="ModuleReferenceContainer" required="yes" fixed="yes" />
<attribute schema="ANY" />
</schema>
<schema name="Available" elementResync="NEVER" attributeResync="NEVER">
<interface name="Attachable" />
<element schema="VOID" />
<attribute name="_pid" schema="LONG" hidden="yes" required="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="SessionAttributes" elementResync="NEVER" attributeResync="ONCE">
<interface name="Environment" />
<element schema="VOID" />
<attribute name="_os" schema="STRING" hidden="yes" />
<attribute name="_debugger" schema="STRING" hidden="yes" />
<!-- attribute name="_endian" schema="STRING" hidden="yes" /-->
<attribute name="_arch" schema="STRING" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="Machine" schema="SessionAttributesMachine" fixed="yes" />
<attribute name="Target" schema="OBJECT" />
<attribute schema="ANY" />
</schema>
<schema name="SessionAttributesMachine" elementResync="NEVER" attributeResync="NEVER">
<element schema="VOID" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="Arch" schema="STRING" />
<attribute name="Debugger" schema="STRING" />
<attribute name="OS" schema="STRING" />
<attribute name="Mode" schema="STRING" />
<attribute schema="ANY" />
</schema>
<schema name="ProcessContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Aggregate" />
<interface name="EventScope" />
<element schema="ProcessRef" />
<attribute name="_event_process" schema="STRING" hidden="yes" />
<attribute name="_event_thread" schema="STRING" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="ProcessRef" elementResync="NEVER" attributeResync="ONCE">
<interface name="Activatable" />
<interface name="Aggregate" />
<interface name="ExecutionStateful" />
<interface name="Access" />
<interface name="Attacher" />
<interface name="Attachable" />
<!-- interface name="Launcher" / -->
<interface name="Deletable" />
<interface name="Detachable" />
<interface name="Killable" />
<interface name="Resumable" />
<interface name="Steppable" />
<interface name="Interruptible" />
<element schema="VOID" />
<attribute name="_pid" schema="LONG" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="_state" schema="EXECUTION_STATE" required="no" hidden="yes" />
<attribute name="_accessible" schema="BOOL" required="yes" hidden="yes" />
<attribute name="_supported_attach_kinds" schema="SET_ATTACH_KIND" required="yes" hidden="yes" />
<attribute name="_supported_step_kinds" schema="SET_STEP_KIND" required="yes" fixed="yes" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="Memory" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Memory" />
<element schema="MemoryRegion" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="MemoryRegion" elementResync="NEVER" attributeResync="NEVER">
<interface name="MemoryRegion" />
<element schema="VOID" />
<attribute name="_memory" schema="Memory" />
<attribute name="Name" schema="STRING" />
<attribute-alias from="_name" to="Name" />
<attribute name="Range" schema="RANGE" />
<attribute-alias from="_range" to="Range" />
<attribute name="_readable" schema="BOOL" hidden="yes" />
<attribute name="_writable" schema="BOOL" hidden="yes" />
<attribute name="_executable" schema="BOOL" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="DebugContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Aggregate" />
<element schema="OBJECT" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="Breakpoints" schema="BreakpointContainer" required="yes" fixed="no" />
<attribute schema="ANY" />
</schema>
<schema name="BreakpointContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="BreakpointLocationContainer" />
<interface name="BreakpointSpecContainer" />
<element schema="BreakpointSpec" />
<attribute name="_supported_breakpoint_kinds" schema="SET_BREAKPOINT_KIND" required="yes" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="BreakpointSpec" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="BreakpointSpec" />
<interface name="BreakpointLocation" />
<interface name="Deletable" />
<element schema="OBJECT" />
<attribute name="_expression" schema="STRING" required="yes" hidden="yes" />
<attribute name="_kinds" schema="SET_BREAKPOINT_KIND" required="yes" hidden="yes" />
<attribute name="_container" schema="BreakpointContainer" />
<attribute name="_affects" schema="LIST_OBJECT" hidden="yes" />
<attribute name="_spec" schema="BreakpointSpec" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="Range" schema="RANGE" hidden="yes" />
<attribute-alias from="_range" to="Range" />
<attribute name="Enabled" schema="BOOL" required="yes" hidden="yes" />
<attribute-alias from="_enabled" to="Enabled" />
<attribute schema="ANY" />
</schema>
<schema name="EventContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="Event" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="Event" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Deletable" />
<element schema="OBJECT" />
<attribute name="_enabled" schema="BOOL" required="yes" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="SymbolContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
<interface name="SymbolNamespace" />
<element schema="Symbol" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="Symbol" elementResync="NEVER" attributeResync="NEVER">
<interface name="Symbol" />
<element schema="VOID" />
<attribute name="_namespace" schema="SymbolContainer" />
<attribute name="_data_type" schema="DATA_TYPE" fixed="yes" hidden="yes" />
<attribute name="_size" schema="LONG" fixed="yes" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ADDRESS" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="RegisterContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="RegisterContainer" />
<interface name="RegisterBank" />
<element schema="VOID" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="RegisterDescriptor" elementResync="NEVER" attributeResync="NEVER">
<interface name="Register" />
<element schema="VOID" />
<attribute name="_length" schema="INT" fixed="yes" hidden="yes" />
<attribute name="_container" schema="RegisterContainer" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" required="yes" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="Stack" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Stack" />
<element schema="StackFrame" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="StackFrame" elementResync="NEVER" attributeResync="NEVER">
<interface name="StackFrame" />
<interface name="Activatable" />
<interface name="Aggregate" />
<element schema="VOID" />
<attribute name="Function" schema="MethodProxy" hidden="yes" />
<attribute-alias from="_function" to="Function" />
<attribute name="Location" schema="Location" />
<attribute name="Registers" schema="RegisterContainer" />
<attribute name="PC" schema="ADDRESS" hidden="yes" />
<attribute-alias from="_pc" to="PC" />
<attribute name="This" schema="ObjectReferenceProxy" />
<attribute name="Variables" schema="VariableContainer" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="Attributes" schema="StackFrameAttributes" />
<attribute schema="ANY" />
</schema>
<schema name="StackFrameAttributes" elementResync="NEVER" attributeResync="NEVER">
<element schema="VOID" />
<attribute name="_pc" schema="ADDRESS" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="FrameNumber" schema="OBJECT" />
<attribute name="FrameOffset" schema="OBJECT" />
<attribute name="FuncTableEntry" schema="OBJECT" />
<attribute name="InstructionOffset" schema="OBJECT" />
<attribute name="ReturnOffset" schema="OBJECT" />
<attribute name="StackOffset" schema="OBJECT" />
<attribute name="Virtual" schema="OBJECT" />
<attribute schema="ANY" />
</schema>
<schema name="ReferenceTypeContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="ReferenceType" />
<attribute schema="ANY" />
</schema>
<schema name="ReferenceTypeProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="ReferenceType" />
</schema>
<schema name="ReferenceType" elementResync="NEVER" attributeResync="ALWAYS">
<interface name="Module" />
<interface name="Aggregate" />
<element schema="ANY" />
<attribute name="Fields" schema="FieldContainer" fixed="yes" />
<attribute name="Instances" schema="ObjectReferenceContainer" fixed="yes" />
<attribute name="Locations" schema="LocationContainer" fixed="yes" />
<attribute name="Methods" schema="MethodContainer" required="no" fixed="yes" />
<attribute name="Relations" schema="ReferenceTypeRelations" required="no" fixed="yes" />
<attribute name="_module_name" schema="STRING" hidden="yes" />
<attribute name="Range" schema="RANGE" />
<attribute name="RangeCP" schema="RANGE" />
<attribute-alias from="_range" to="Range" />
<attribute schema="ANY" />
</schema>
<schema name="ReferenceTypeRelations" elementResync="NEVER" attributeResync="ALWAYS">
<element schema="ANY" />
<attribute name="ModuleRef" schema="ModuleReferenceProxy" fixed="yes" />
<attribute name="ComponentType" schema="Type" fixed="yes" />
<attribute name="AllInterfaces" schema="ReferenceTypeContainer" fixed="yes" />
<attribute name="Interfaces" schema="ReferenceTypeContainer" fixed="yes" />
<attribute name="IsEnum" schema="BOOL" fixed="yes" />
<attribute name="SubClasses" schema="ReferenceTypeContainer" fixed="yes" />
<attribute name="ClassType" schema="ReferenceTypeProxy" fixed="yes" />
<attribute name="Implementors" schema="ReferenceTypeContainer" fixed="yes" />
<attribute name="SubInterfaces" schema="ReferenceTypeContainer" fixed="yes" />
<attribute name="SuperInterfaces" schema="ReferenceTypeContainer" fixed="yes" />
<attribute schema="ANY" />
</schema>
<schema name="FieldContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="Field" />
<attribute schema="ANY" />
</schema>
<schema name="Field" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="Type" schema="Type" required="no" fixed="yes" />
<attribute name="Value" schema="Value" required="no" />
<attribute schema="ANY" />
</schema>
<schema name="MethodContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="SectionContainer" />
<element schema="Method" />
<attribute schema="ANY" />
</schema>
<schema name="MethodProxy" elementResync="NEVER" attributeResync="NEVER">
<interface name="SectionContainer" />
<element schema="Method" />
</schema>
<schema name="Method" elementResync="NEVER" attributeResync="NEVER">
<interface name="Section" />
<interface name="Aggregate" />
<element schema="ANY" />
<attribute name="Arguments" schema="ArgumentContainer" fixed="yes" />
<attribute name="DeclaringType" schema="ReferenceTypeProxy" fixed="yes" />
<attribute name="Locations" schema="LocationContainer" fixed="yes" />
<attribute name="Range" schema="RANGE" />
<attribute-alias from="_range" to="Range" />
<attribute name="Variables" schema="VariableContainer" fixed="yes" />
<attribute name="_display" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_return_type" schema="TYPE" fixed="yes" hidden="yes" />
<attribute name="_parameters" schema="MAP_PARAMETERS" fixed="yes" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="ArgumentContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="Type" />
<attribute schema="ANY" />
</schema>
<schema name="VariableContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="Variable" />
<attribute schema="ANY" />
</schema>
<schema name="Variable" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="Type" schema="Type" hidden="yes" />
<attribute name="Value" schema="Value" required="no" />
<attribute schema="ANY" />
</schema>
<schema name="ConstantPool" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute schema="ANY" />
</schema>
<schema name="LocationContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="Location" />
<attribute schema="ANY" />
</schema>
<schema name="Location" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="Addr" schema="ADDRESS" />
<attribute name="DeclaringType" schema="ReferenceTypeProxy" />
<attribute name="ModuleRef" schema="ModuleReferenceProxy" />
<attribute name="Method" schema="MethodProxy" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="Type" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute schema="ANY" />
</schema>
<schema name="ValueContainer" elementResync="NEVER" attributeResync="NEVER">
<element schema="Value" />
</schema>
<schema name="Value" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="Type" schema="Type" />
<attribute name="Value" schema="Value" />
<attribute name="Variables" schema="FieldContainer" />
<attribute name="ClassLoader" schema="ClassLoaderReferenceProxy" />
<attribute name="DefinedClasses" schema="ReferenceTypeContainer" />
<attribute name="VisibleClasses" schema="ReferenceTypeContainer" />
<attribute name="ReflectedType" schema="ReferenceTypeProxy" />
<attribute name="Relations" schema="ObjectRelations" required="no" fixed="yes" />
<attribute name="ThreadGroups" schema="ThreadGroupReferenceContainer" />
<attribute name="Threads" schema="ThreadReferenceContainer" />
<attribute name="Values" schema="ValueContainer" />
<attribute schema="ANY" />
</schema>
<schema name="PrimitiveValueContainer" elementResync="NEVER" attributeResync="NEVER">
<element schema="PrimitiveValue" />
</schema>
<schema name="PrimitiveValueProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="PrimitiveValue" />
</schema>
<schema name="PrimitiveValue" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="Type" schema="Type" />
<attribute name="Value" schema="Value" />
<attribute schema="ANY" />
</schema>
<schema name="ObjectReferenceContainer" elementResync="NEVER" attributeResync="NEVER">
<element schema="ObjectReference" />
</schema>
<schema name="ObjectReferenceProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="ObjectReference" />
</schema>
<schema name="ObjectReference" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="Type" schema="Type" />
<attribute name="Relations" schema="ObjectRelations" />
<attribute name="Variables" schema="FieldContainer" />
<attribute schema="ANY" />
</schema>
<schema name="ObjectRelations" elementResync="NEVER" attributeResync="ALWAYS">
<element schema="ANY" />
<attribute name="CurrentContendedMonitor" schema="ObjectReferenceProxy" />
<attribute name="OwnedMonitors" schema="ObjectReferenceContainer" />
<attribute name="OwnedMonitorsAndFrames" schema="MonitorInfoContainer" />
<attribute name="OwningThread" schema="ThreadReferenceProxy" />
<attribute name="ReferenceType" schema="ReferenceTypeProxy" />
<attribute name="ReferringObjects" schema="ObjectReferenceContainer" />
<attribute name="WaitingThreads" schema="ThreadReferenceContainer" />
<attribute name="ThreadGroup" schema="ThreadGroupReferenceProxy" />
<attribute schema="ANY" />
</schema>
<schema name="ArrayReferenceContainer" elementResync="NEVER" attributeResync="NEVER">
<element schema="ArrayReference" />
</schema>
<schema name="ArrayReferenceProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="ArrayReference" />
</schema>
<schema name="ArrayReference" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="Values" schema="ValueContainer" />
<attribute name="Relations" schema="ObjectRelations" required="no" fixed="yes" />
<attribute name="Type" schema="Type" />
<attribute schema="ANY" />
</schema>
<schema name="ClassLoaderReferenceContainer" elementResync="NEVER" attributeResync="NEVER">
<element schema="ClassLoaderReference" />
</schema>
<schema name="ClassLoaderReferenceProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="ClassLoaderReference" />
</schema>
<schema name="ClassLoaderReference" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="DefinedClasses" schema="ReferenceTypeContainer" />
<attribute name="VisibleClasses" schema="ReferenceTypeContainer" />
<attribute name="Relations" schema="ObjectRelations" required="no" fixed="yes" />
<attribute name="Type" schema="Type" />
<attribute schema="ANY" />
</schema>
<schema name="ClassObjecReferenceContainer" elementResync="NEVER" attributeResync="NEVER">
<element schema="ClassObjecReference" />
</schema>
<schema name="ClassObjecReferenceProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="ClassObjecReference" />
</schema>
<schema name="ClassObjecReference" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="ReflectedType" schema="ReferenceTypeProxy" />
<attribute name="Relations" schema="ObjectRelations" required="no" fixed="yes" />
<attribute name="Type" schema="Type" />
<attribute schema="ANY" />
</schema>
<schema name="ModuleReferenceContainer" elementResync="NEVER" attributeResync="NEVER">
<element schema="ModuleReference" />
</schema>
<schema name="ModuleReferenceProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="ModuleReference" />
</schema>
<schema name="ModuleReference" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="ClassLoader" schema="ClassLoaderReferenceProxy" />
<attribute name="Relations" schema="ObjectRelations" required="no" fixed="yes" />
<attribute name="Type" schema="Type" />
<attribute schema="ANY" />
</schema>
<schema name="StringReferenceContainer" elementResync="NEVER" attributeResync="NEVER">
<element schema="StringReference" />
</schema>
<schema name="StringReferenceProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="StringReference" />
</schema>
<schema name="StringReference" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="Value" schema="STRING" />
<attribute name="Relations" schema="ObjectRelations" required="no" fixed="yes" />
<attribute name="Type" schema="Type" />
<attribute schema="ANY" />
</schema>
<schema name="ThreadGroupReferenceContainer" elementResync="NEVER" attributeResync="NEVER">
<interface name="Aggregate" />
<element schema="ThreadGroupReference" />
</schema>
<schema name="ThreadGroupReferenceProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="ThreadGroupReference" />
</schema>
<schema name="ThreadGroupReference" elementResync="NEVER" attributeResync="NEVER">
<interface name="Aggregate" />
<interface name="ExecutionStateful" />
<interface name="Steppable" />
<element schema="ANY" />
<attribute name="Parent" schema="ThreadGroupReferenceProxy" />
<attribute name="Relations" schema="ObjectRelations" required="no" fixed="yes" />
<attribute name="ThreadGroups" schema="ThreadGroupReferenceContainer" />
<attribute name="Threads" schema="ThreadReferenceContainer" />
<attribute name="Type" schema="Type" />
<attribute schema="ANY" />
</schema>
<schema name="ThreadContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Aggregate" />
<interface name="EventScope" />
<element schema="Thread" />
</schema>
<schema name="ThreadReferenceContainer" elementResync="NEVER" attributeResync="NEVER">
<interface name="Aggregate" />
<interface name="EventScope" />
<element schema="Thread" />
</schema>
<schema name="ThreadReferenceProxy" elementResync="NEVER" attributeResync="NEVER">
<element schema="Thread" />
</schema>
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
<interface name="Thread" />
<interface name="Aggregate" />
<interface name="Access" />
<interface name="Activatable" />
<interface name="ExecutionStateful" />
<interface name="Resumable" />
<interface name="Steppable" />
<interface name="Interruptible" />
<element schema="VOID" />
<attribute name="_tid" schema="INT" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
<attribute name="_update_mode" schema="UPDATE_MODE" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="_accessible" schema="BOOL" required="yes" hidden="yes" />
<attribute name="_state" schema="EXECUTION_STATE" required="no" hidden="yes" />
<attribute name="_supported_step_kinds" schema="SET_STEP_KIND" required="yes" fixed="yes" hidden="yes" />
<attribute name="Stack" schema="Stack" required="yes" fixed="yes" />
<attribute name="Id" schema="OBJECT" />
<attribute name="Name" schema="OBJECT" />
<attribute name="Relations" schema="ObjectRelations" required="no" fixed="yes" />
<attribute name="Type" schema="Type" />
<attribute name="Variables" schema="FieldContainer" />
<attribute name="_arch" schema="STRING" />
<attribute name="Step to Address (pa)" schema="Method" />
<attribute name="Trace to Address (ta)" schema="Method" />
<attribute schema="ANY" />
</schema>
<schema name="MonitorInfoContainer" elementResync="NEVER" attributeResync="NEVER">
<interface name="Aggregate" />
<element schema="MonitorInfo" />
</schema>
<schema name="MonitorInfo" elementResync="NEVER" attributeResync="NEVER">
<element schema="ANY" />
<attribute name="Monitor" schema="ObjectReferenceProxy" />
<attribute name="Thread" schema="ThreadReferenceProxy" />
<attribute schema="ANY" />
</schema>
<schema name="UPDATE_MODE" elementResync="NEVER" attributeResync="NEVER"></schema>
</context>

View File

@ -5,7 +5,7 @@
* 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
* 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,
@ -66,7 +66,7 @@ public class JdiModelTest implements DebuggerModelTestUtils {
waitOn(launcher.launch(parameters));
TargetObject vm =
Unique.assertOne(waitOn(model.fetchObjectElements("VirtualMachines")).values());
Unique.assertOne(waitOn(model.fetchObjectElements("VMs")).values());
waitOn(vm.as(TargetKillable.class).kill());
}
}

View File

@ -733,94 +733,93 @@ python3 -m pip install --no-index -f Debugger-rmi-trace\pypkg\dist -f Debugger-a
Alternatively, if you are trying to quit, but typed "<TT>.exit</TT>", just type
"<TT>quit()</TT>" to terminate the session.</P>
<H3><A name="dbgeng_ext"></A>dbgeng-ext</H3>
<H3><A name="dbgeng_ext"></A>dbgeng-ext</H3>
<P>This launcher extends the base dbgeng launcher adding extra options (a la IDebugClient's CreateProcess2).</P>
<P>This launcher extends the base dbgeng launcher adding extra options (a la IDebugClient's
CreateProcess2).</P>
<H4>Options</H4>
<H4>Options</H4>
<UL>
<LI><B>Dir</B>: This is the starting directory for the process.</LI>
<UL>
<LI><B>Dir</B>: This is the starting directory for the process.</LI>
<LI><B>Env</B>: This is a composite string containg Environment Variable entries delineated
by '/0' separators. For example, you could redefine USERNAME and USERPROFILE with the entry
'USERNAME=SomeUser/0USERPROFILE=C:\Users\SomeUser'.</LI>
<LI><B>Env</B>: This is a composite string containg Environment Variable entries
delineated by '/0' separators. For example, you could redefine USERNAME and USERPROFILE
with the entry 'USERNAME=SomeUser/0USERPROFILE=C:\Users\SomeUser'.</LI>
<LI><B>CreateFlags</B>: Flags used when creating the process, typically either
DEBUG_PROCESS(1) or DEBUG_ONLY_THIS_PROCESS(2) if you do not wish to follow spawned
processes. Other possible values are defined by processes.h's
CreateProcessCreationFlags.</LI>
<LI><B>CreateFlags</B>: Flags used when creating the process, typically either DEBUG_PROCESS(1) or
DEBUG_ONLY_THIS_PROCESS(2) if you do not wish to follow spawned processes. Other possible values
are defined by processes.h's CreateProcessCreationFlags.</LI>
<LI><B>CreateFlags (Engine)</B>: Engine-specific flags used when creating the process
(defined in dbgeng.h). Typically, these are set to 0.</LI>
<LI><B>CreateFlags (Engine)</B>: Engine-specific flags used when creating the process (defined in dbgeng.h).
Typically, these are set to 0.</LI>
<LI><B>VerifierFlags (Engine)</B>: Flags used by the Application Verifier. Typically unused,
but, if desired, CreateEngineFlags must include
DEBUG_ECREATE_PROCESS_USE_VERIFIER_FLAGS(2).</LI>
</UL>
<LI><B>VerifierFlags (Engine)</B>: Flags used by the Application Verifier. Typically unused, but, if desired,
CreateEngineFlags must include DEBUG_ECREATE_PROCESS_USE_VERIFIER_FLAGS(2).</LI>
</UL>
<H3><A name="dbgeng_attach"></A>dbgeng-attach</H3>
<H3><A name="dbgeng_attach"></A>dbgeng-attach</H3>
<P>This launcher allows the user to attach to a local running process. Options are the same as
those for the base dbgeng, except for ProcessId and AttachFlags</P>
<P>This launcher allows the user to attach to a local running process. Options are the same as those for the base dbgeng, except for ProcessId and AttachFlags</P>
<H4>Options</H4>
<UL>
<LI><B>ProcessId</B>: The pid of the process you wish to attach to.</LI>
<H4>Options</H4>
<LI><B>AttachFlags</B>: Flags used when attaching to the target process, typically
DEBUG_ATTACH_PROCESS(0). Other possible values are defined in dbgeng.h and determine whether
the attach should be invasive or not and the status of the process after attaching.</LI>
</UL>
<UL>
<LI><B>ProcessId</B>: The pid of the process you wish to attach to.</LI>
<H3><A name="dbgeng_remote"></A>dbgeng-remote</H3>
<LI><B>AttachFlags</B>: Flags used when attaching to the target process, typically DEBUG_ATTACH_PROCESS(0). Other possible values
are defined in dbgeng.h and determine whether the attach should be invasive or not
and the status of the process after attaching.</LI>
<P>This launcher extends the base dbgeng launcher adding an option for connecting through a
remote process server.</P>
</UL>
<H4>Options</H4>
<H3><A name="dbgeng_remote"></A>dbgeng-remote</H3>
<P>This launcher extends the base dbgeng launcher adding an option for connecting through a remote process server.
</P>
<H4>Options</H4>
<UL>
<LI><B>Connection</B>: This is the connection string specifying the transport options for
communicating with the remote server. A typical example might be 'tcp:port=12345,server=192.168.0.2''
for a process server launched on the machine at 192.168.0.2 using:
<UL>
<LI>
<B>Connection</B>: This is the connection string specifying the transport options for
communicating with the remote server. A typical example might be
'tcp:port=12345,server=192.168.0.2'' for a process server launched on the machine at
192.168.0.2 using:
<PRE>
dbgsrv -t tcp:port=12345
</PRE>
</LI>
</UL>
</LI>
</UL>
<H3><A name="dbgeng_kernel"></A>dbgeng-kernel</H3>
<H3><A name="dbgeng_kernel"></A>dbgeng-kernel</H3>
<P>This version of the dbgeng should be used for kernel-debugging of a remote machine. Options are the
same as the base dbgeng, except for the connection-string arguments. For remote
debugging, the target machine should be booted with the appropriate options, set using BCDEDIT or the
equivalent, such as:
</P>
<UL style='list-style-type: none'><LI>
<P>This version of the dbgeng should be used for kernel-debugging of a remote machine. Options
are the same as the base dbgeng, except for the connection-string arguments. For remote
debugging, the target machine should be booted with the appropriate options, set using BCDEDIT
or the equivalent, such as:</P>
<UL style='list-style-type: none'>
<LI>
<PRE>
bcdedit /debug ON
bdcedit /dbgsettings NET HOSTIP:IP PORT:54321 KEY:1.1.1.1
</PRE>
</LI></UL>
<P>
where IP= the address of the machine runing Ghidra.
</P>
</LI>
</UL>
<H4>Options</H4>
<P>where IP= the address of the machine runing Ghidra.</P>
<UL>
<LI><B>Arguments</B>: This is the connection string specifying the transport options for
communicating with the remote target. A typical example might be 'net:port=54321,key=1.1.1.1'.'
</LI>
</UL>
<H4>Options</H4>
<UL>
<LI><B>Arguments</B>: This is the connection string specifying the transport options for
communicating with the remote target. A typical example might be
'net:port=54321,key=1.1.1.1'.'</LI>
</UL>
<H3><A name="dbgeng_ttd"></A>TTD (Time-Travel Debugging)</H3>
<P>This is a nascent extension to our launcher for the Windows Debugger. The launcher itself
@ -854,6 +853,88 @@ bdcedit /dbgsettings NET HOSTIP:IP PORT:54321 KEY:1.1.1.1
contain <TT>dbgmodel.dll</TT> and the scripts that implement TTD. These are most easily
obtained by installing WinDbg Preview or later.</P>
<H2>Stock Java Launchers</H2>
<P>The following launchers based on the Java Debugger are included out of the box:</P>
<H3><A name="java"></A>java launch</H3>
<P>This launcher uses the native Java Debug Interface (JDI) to launch the current
<TT>.class</TT> file.</P>
<H4><A name="java_setup"></A>Setup</H4>
<P>You must have Java installed on the local system. No additional setup is required.</P>
<H4>Options</H4>
<UL>
<LI><B>Arguments</B>: These are the command-line arguments to pass into the target.</LI>
<LI><B>Arch</B>: The architecture (currently, either "JVM" or "Dalvik").</LI>
<LI><B>Suspend</B>: Should the target be suspended on launch.</LI>
<LI><B>Include virtual threads</B>: As described.</LI>
<LI><B>JShell cmd</B>: If desired, the path to the jshell binary that will host
execution.</LI>
</UL>
<H3><A name="java_attach"></A>java attach port</H3>
<P>This launcher uses the native Java Debug Interface (JDI) to attach to a running java program
launched with an open Java Debug Wire Port (JDWP), e.g.:</P>
<UL style="list-style-type: none">
<LI>
<PRE>
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:54321 Target.class
</PRE>
</LI>
</UL>
<H4><A name="java_attach_setup"></A>Setup</H4>
<P>Identical to that for the java launcher.</P>
<H4>Options</H4>
<UL>
<LI><B>Arch</B>: The architecture (currently, either "JVM" or "Dalvik").</LI>
<LI><B>Host</B>: The host IP where the target is running.</LI>
<LI><B>Port</B>: The open JDWP port used by the target.</LI>
<LI><B>Timeout</B>: How long to wait for a connection attempt.</LI>
<LI><B>JShell cmd</B>: If desired, the path to the jshell binary that will host
execution.</LI>
</UL>
<H3><A name="java_by_pid"></A>java attach PID</H3>
<P>This launcher uses the native Java Debug Interface (JDI) to attach to a running java program
launched with a Java Debug Wire Port (JDWP) identified by process id.</P>
<H4><A name="java_bypid_setup"></A>Setup</H4>
<P>Identical to that for the java launcher.</P>
<H4>Options</H4>
<UL>
<LI><B>Arch</B>: The architecture (currently, either "JVM" or "Dalvik").</LI>
<LI><B>Pid</B>: The target process's ID.</LI>
<LI><B>Timeout</B>: How long to wait for a connection attempt.</LI>
<LI><B>JShell cmd</B>: If desired, the path to the jshell binary that will host
execution.</LI>
</UL>
<H2>Development and Diagnostic Launchers</H2>
<P>We currently provide one launcher for Trace RMI API exploration and development:</P>

View File

@ -0,0 +1,52 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.util.DefaultLanguageService;
public class DefaultMemoryMapper implements MemoryMapper {
private final AddressFactory factory;
public DefaultMemoryMapper(LanguageID id) {
LanguageService langServ = DefaultLanguageService.getLanguageService();
try {
Language lang = langServ.getLanguage(id);
this.factory = lang.getAddressFactory();
}
catch (LanguageNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public Address map(Address address) {
return address;
}
@Override
public Address mapBack(Address address) {
return address;
}
@Override
public Address genAddr(String space, long offset) {
return factory.getAddressSpace(space).getAddress(offset);
}
}

View File

@ -0,0 +1,48 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import ghidra.program.model.lang.LanguageID;
import ghidra.program.model.lang.RegisterValue;
public class DefaultRegisterMapper implements RegisterMapper {
public DefaultRegisterMapper(LanguageID id) {
// Nothing so far
}
@Override
public String mapName(String name) {
return name;
}
@Override
public String mapNameBack(String name) {
return name;
}
@Override
public RegisterValue mapValue(String name, RegisterValue rv) {
return rv;
}
@Override
public RegisterValue mapValueBack(String name, RegisterValue rv) {
return rv;
}
}

View File

@ -0,0 +1,30 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import com.sun.jdi.VirtualMachine;
import ghidra.program.model.address.Address;
public interface MemoryMapper {
public Address map(Address address);
public Address mapBack(Address address);
public Address genAddr(String space, long offset);
}

View File

@ -0,0 +1,80 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import com.google.protobuf.AbstractMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import ghidra.util.Msg;
public class ProtobufSocket<T extends AbstractMessage> {
public interface Decoder<T> {
T decode(ByteBuffer buf) throws InvalidProtocolBufferException;
}
private final ByteBuffer lenSend = ByteBuffer.allocate(4);
private final ByteBuffer lenRecv = ByteBuffer.allocate(4);
private final SocketChannel channel;
private final Decoder<T> decoder;
public ProtobufSocket(SocketChannel channel, Decoder<T> decoder) {
this.channel = channel;
this.decoder = decoder;
}
public void send(T msg) throws IOException {
synchronized (lenSend) {
lenSend.clear();
lenSend.putInt(msg.getSerializedSize());
lenSend.flip();
channel.write(lenSend);
for (ByteBuffer buf : msg.toByteString().asReadOnlyByteBufferList()) {
channel.write(buf);
}
}
}
public T recv() throws IOException {
synchronized (lenRecv) {
lenRecv.clear();
while (lenRecv.hasRemaining()) {
channel.read(lenRecv);
}
lenRecv.flip();
int len = lenRecv.getInt();
// This is just for testing, so littering on the heap is okay.
ByteBuffer buf = ByteBuffer.allocate(len);
while (buf.hasRemaining()) {
channel.read(buf);
}
buf.flip();
return decoder.decode(buf);
}
}
public void close() {
try {
channel.close();
}
catch (IOException e) {
Msg.error(this, "Unable to close ProtobufSocket");
}
}
}

View File

@ -0,0 +1,30 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import ghidra.program.model.lang.RegisterValue;
public interface RegisterMapper {
public String mapName(String name);
public String mapNameBack(String name);
public RegisterValue mapValue(String name, RegisterValue rv);
public RegisterValue mapValueBack(String name, RegisterValue rv);
}

View File

@ -0,0 +1,44 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import java.util.HashSet;
import java.util.Set;
public class RmiBatch {
private int refCount = 0;
private Set<Object> futures = new HashSet<>();
public void inc() {
refCount++;
}
public int dec() {
return --refCount;
}
public void append(Object f) {
futures.add(f);
}
public Object results() {
return null;
}
}

View File

@ -0,0 +1,731 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.nio.channels.SocketChannel;
import java.util.*;
import org.jdom.JDOMException;
import com.google.protobuf.ByteString;
import ghidra.app.plugin.core.debug.service.tracermi.TraceRmiHandler;
import ghidra.dbg.target.schema.*;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.rmi.trace.TraceRmi;
import ghidra.rmi.trace.TraceRmi.*;
import ghidra.rmi.trace.TraceRmi.Language;
import ghidra.rmi.trace.TraceRmi.Value.Builder;
import ghidra.trace.model.Lifespan;
import ghidra.util.Msg;
public class RmiClient {
private final ProtobufSocket<RootMessage> socket;
private final String description;
private int nextTraceId = 0;
private RmiBatch currentBatch = null;
Map<Integer, RmiTrace> traces = new HashMap<>();
private SchemaContext schemaContext;
private RmiMethodHandlerThread handler;
private static RmiMethodRegistry methodRegistry;
private Deque<RootMessage> requests = new LinkedList<>();
public static TargetObjectSchema loadSchema(String resourceName, String rootName) {
XmlSchemaContext schemaContext;
try {
InputStream resourceAsStream = RmiClient.class.getResourceAsStream(resourceName);
schemaContext = XmlSchemaContext
.deserialize(resourceAsStream);
return schemaContext.getSchema(schemaContext.name(rootName));
}
catch (JDOMException | IOException e) {
throw new AssertionError(e);
}
}
// public static TargetObjectSchema getSchema(String name) {
// try {
// return SCHEMA_CTX.getSchema(new SchemaName(name));
// } catch (NullPointerException e) {
// System.err.println("Possibly non-existent schema: "+name);
// return SCHEMA_CTX.getSchema(new SchemaName("OBJECT"));
// }
// }
public static enum TraceRmiResolution {
RES_ADJUST("adjust", Resolution.CR_ADJUST), //
RES_DENY("deny", Resolution.CR_DENY), //
RES_TRUNCATE("truncate", Resolution.CR_TRUNCATE), //
;
TraceRmiResolution(String val, TraceRmi.Resolution description) {
this.val = val;
this.description = description;
}
public final String val;
public final Resolution description;
}
public static enum TraceRmiValueKinds {
ATTRIBUTES("attributes", ValueKinds.VK_ATTRIBUTES), //
ELEMENTS("elements", ValueKinds.VK_ELEMENTS), //
BOTH("both", ValueKinds.VK_BOTH), //
;
TraceRmiValueKinds(String val, TraceRmi.ValueKinds description) {
this.val = val;
this.description = description;
}
public final String val;
public final ValueKinds description;
}
public RmiClient(SocketChannel channel, String description) {
this.socket = new ProtobufSocket<>(channel, RootMessage::parseFrom);
this.description = description;
this.handler = new RmiMethodHandlerThread(this, socket);
handler.start();
}
public ProtobufSocket<RootMessage> getSocket() {
return socket;
}
public String getDescription() {
return description;
}
public void close() {
handler.close();
socket.close();
}
private void send(RootMessage msg) {
try {
requests.push(msg);
socket.send(msg);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
public RmiTrace createTrace(String path, LanguageID language, CompilerSpecID compiler) {
if (compiler == null) {
compiler = new CompilerSpecID("default");
}
RmiTrace trace = new RmiTrace(this, nextTraceId);
traces.put(nextTraceId, trace);
send(RootMessage.newBuilder()
.setRequestCreateTrace(RequestCreateTrace.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(nextTraceId++))
.setLanguage(Language.newBuilder()
.setId(language.getIdAsString()))
.setCompiler(Compiler.newBuilder()
.setId(compiler.getIdAsString()))
.setPath(FilePath.newBuilder()
.setPath(path)))
.build());
return trace;
}
public void closeTrace(int id) {
send(RootMessage.newBuilder()
.setRequestCloseTrace(RequestCloseTrace.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(id)))
.build());
}
public void saveTrace(int id) {
send(RootMessage.newBuilder()
.setRequestSaveTrace(RequestSaveTrace.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(id)))
.build());
}
public void startTx(int traceId, String desc, boolean undoable, int txId) {
send(RootMessage.newBuilder()
.setRequestStartTx(RequestStartTx.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setTxid(TxId.newBuilder().setId(txId))
.setDescription(desc)
.setUndoable(undoable))
.build());
}
public void endTx(int traceId, int txId, boolean abort) {
send(RootMessage.newBuilder()
.setRequestEndTx(RequestEndTx.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setTxid(TxId.newBuilder().setId(txId))
.setAbort(abort))
.build());
}
public void snapshot(int traceId, String desc, String datetime, long snap) {
send(RootMessage.newBuilder()
.setRequestSnapshot(RequestSnapshot.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setSnap(Snap.newBuilder()
.setSnap(snap))
.setDatetime(datetime)
.setDescription(desc))
.build());
}
public void createOverlaySpace(int traceId, String base, String name) {
send(RootMessage.newBuilder()
.setRequestCreateOverlay(RequestCreateOverlaySpace.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setBaseSpace(base)
.setName(name))
.build());
}
public void putBytes(int traceId, long snap, Address start, byte[] data) {
send(RootMessage.newBuilder()
.setRequestPutBytes(RequestPutBytes.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setSnap(Snap.newBuilder().setSnap(snap))
.setStart(Addr.newBuilder()
.setSpace(start.getAddressSpace().getName())
.setOffset(start.getOffset()))
.setData(ByteString.copyFrom(data)))
.build());
}
public void setMemoryState(int traceId, long snap, AddressRange range, MemoryState state) {
send(RootMessage.newBuilder()
.setRequestSetMemoryState(RequestSetMemoryState.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setSnap(Snap.newBuilder().setSnap(snap))
.setRange(AddrRange.newBuilder()
.setSpace(range.getAddressSpace().getName())
.setOffset(range.getMinAddress().getOffset())
.setExtend(range.getLength() - 1))
.setState(state))
.build());
}
public void deleteBytes(int traceId, long snap, AddressRange range) {
send(RootMessage.newBuilder()
.setRequestDeleteBytes(RequestDeleteBytes.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setSnap(Snap.newBuilder().setSnap(snap))
.setRange(AddrRange.newBuilder()
.setSpace(range.getAddressSpace().getName())
.setOffset(range.getMinAddress().getOffset())
.setExtend(range.getLength())))
.build());
}
public void putRegisters(int traceId, long snap, String ppath, RegisterValue[] values) {
RequestPutRegisterValue.Builder builder = RequestPutRegisterValue.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setSnap(Snap.newBuilder().setSnap(snap))
.setSpace(ppath);
for (int i = 0; i < values.length; i++) {
RegisterValue rv = values[i];
ByteString val = ByteString.copyFrom(rv.toBytes());
rv.getUnsignedValue();
builder.addValues(i, RegVal.newBuilder()
.setName(rv.getRegister().getName())
.setValue(val));
}
send(RootMessage.newBuilder()
.setRequestPutRegisterValue(builder)
.build());
}
public void deleteRegisters(int traceId, long snap, String ppath, String[] names) {
RequestDeleteRegisterValue.Builder builder = RequestDeleteRegisterValue.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setSnap(Snap.newBuilder().setSnap(snap))
.setSpace(ppath);
for (int i = 0; i < names.length; i++) {
String name = names[i];
builder.setNames(i, name);
}
send(RootMessage.newBuilder()
.setRequestDeleteRegisterValue(builder)
.build());
}
public void createRootObject(int traceId, SchemaContext schemContext, String schema) {
this.schemaContext = schemContext;
String xmlCtx = XmlSchemaContext.serialize(schemContext);
send(RootMessage.newBuilder()
.setRequestCreateRootObject(RequestCreateRootObject.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setSchemaContext(xmlCtx)
.setRootSchema(schema))
.build());
}
public void createObject(int traceId, String path) {
//System.err.println("createObject:"+path);
send(RootMessage.newBuilder()
.setRequestCreateObject(RequestCreateObject.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setPath(ObjPath.newBuilder().setPath(path)))
.build());
}
public void insertObject(int traceId, String path, Lifespan span, Resolution r) {
send(RootMessage.newBuilder()
.setRequestInsertObject(RequestInsertObject.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setObject(ObjSpec.newBuilder()
.setPath(ObjPath.newBuilder()
.setPath(path)))
.setSpan(Span.newBuilder()
.setMin(span.lmin())
.setMax(span.lmax()))
.setResolution(r))
.build());
}
public void insertObject(int traceId, ObjSpec object, Lifespan span, Resolution r) {
send(RootMessage.newBuilder()
.setRequestInsertObject(RequestInsertObject.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setObject(object)
.setSpan(Span.newBuilder()
.setMin(span.lmin())
.setMax(span.lmax()))
.setResolution(r))
.build());
}
public void removeObject(int traceId, ObjSpec object, Lifespan span, boolean tree) {
send(RootMessage.newBuilder()
.setRequestRemoveObject(RequestRemoveObject.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setObject(object)
.setSpan(Span.newBuilder()
.setMin(span.lmin())
.setMax(span.lmax()))
.setTree(tree))
.build());
}
public void setValue(int traceId, String ppath, Lifespan span, String key, Object value,
String resolution) {
Resolution r = resolution == null ? Resolution.CR_ADJUST
: TraceRmiResolution.valueOf(resolution).description;
send(RootMessage.newBuilder()
.setRequestSetValue(RequestSetValue.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setValue(ValSpec.newBuilder()
.setSpan(Span.newBuilder()
.setMin(span.lmin())
.setMax(span.lmax()))
.setParent(ObjSpec.newBuilder()
.setPath(ObjPath.newBuilder()
.setPath(ppath)))
.setKey(key)
.setValue(buildValue(value)))
.setResolution(r))
.build());
}
public void retainValues(int traceId, String ppath, Lifespan span, ValueKinds kinds,
Set<String> keys) {
RequestRetainValues.Builder builder = RequestRetainValues.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setObject(ObjSpec.newBuilder()
.setPath(ObjPath.newBuilder()
.setPath(ppath)))
.setSpan(Span.newBuilder()
.setMin(span.lmin())
.setMax(span.lmax()))
.setKinds(kinds)
.addAllKeys(keys);
send(RootMessage.newBuilder()
.setRequestRetainValues(builder)
.build());
}
public void getObject(int traceId, String path) {
RequestGetObject.Builder builder = RequestGetObject.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setObject(ObjSpec.newBuilder()
.setPath(ObjPath.newBuilder()
.setPath(path)));
send(RootMessage.newBuilder()
.setRequestGetObject(builder)
.build());
}
public void getValues(int traceId, Lifespan span, String pattern) {
send(RootMessage.newBuilder()
.setRequestGetValues(RequestGetValues.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setSpan(Span.newBuilder()
.setMin(span.lmin())
.setMax(span.lmax()))
.setPattern(ObjPath.newBuilder().setPath(pattern)))
.build());
}
public void getValuesIntersecting(int traceId, Lifespan span, AddressRange range,
String key) {
send(RootMessage.newBuilder()
.setRequestGetValuesIntersecting(RequestGetValuesIntersecting.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setBox(Box.newBuilder()
.setSpan(Span.newBuilder()
.setMin(span.lmin())
.setMax(span.lmax()))
.setRange(AddrRange.newBuilder()
.setSpace(range.getAddressSpace().getName())
.setOffset(range.getMinAddress().getOffset())
.setExtend(range.getLength())))
.setKey(key))
.build());
}
public RmiTraceObject proxyObjectId(int traceId, Long id) {
return RmiTraceObject.fromId(traces.get(traceId), id);
}
public RmiTraceObject proxyObjectPath(int traceId, String path) {
return RmiTraceObject.fromPath(traces.get(traceId), path);
}
public RmiTraceObject proxyObjectPath(int traceId, Long id, String path) {
return new RmiTraceObject(traces.get(traceId), id, path);
}
@SuppressWarnings("unchecked")
private Builder buildValue(Object value) {
Builder builder = Value.newBuilder();
if (value instanceof String str) {
return builder.setStringValue(str);
}
if (value instanceof Boolean bval) {
return builder.setBoolValue(bval);
}
if (value instanceof Short sval) {
return builder.setShortValue(sval);
}
if (value instanceof Integer ival) {
return builder.setIntValue(ival);
}
if (value instanceof Long lval) {
return builder.setLongValue(lval);
}
if (value instanceof ByteString bstr) {
return builder.setBytesValue(bstr);
}
if (value instanceof Byte b) {
return builder.setByteValue(b);
}
if (value instanceof Character c) {
return builder.setCharValue(c);
}
if (value instanceof Address address) {
Addr.Builder addr = Addr.newBuilder()
.setSpace(address.getAddressSpace().getName())
.setOffset(address.getOffset());
return builder.setAddressValue(addr);
}
if (value instanceof AddressRange range) {
AddrRange.Builder rng = AddrRange.newBuilder()
.setSpace(range.getAddressSpace().getName())
.setOffset(range.getMinAddress().getOffset())
.setExtend(range.getLength() - 1);
return builder.setRangeValue(rng);
}
if (value instanceof RmiTraceObject obj) {
return builder.setChildSpec(ObjSpec.newBuilder()
.setPath(ObjPath.newBuilder()
.setPath(obj.getPath())));
}
if (value instanceof List<?> list) {
if (list.get(0) instanceof String) {
StringArr.Builder b = StringArr.newBuilder().addAllArr((List<String>) list);
return builder.setStringArrValue(b.build());
}
if (list.get(0) instanceof Boolean) {
BoolArr.Builder b = BoolArr.newBuilder().addAllArr((List<Boolean>) list);
return builder.setBoolArrValue(b.build());
}
if (list.get(0) instanceof Short) {
ShortArr.Builder b = ShortArr.newBuilder().addAllArr((List<Integer>) list);
return builder.setShortArrValue(b.build());
}
if (list.get(0) instanceof Integer) {
IntArr.Builder b = IntArr.newBuilder().addAllArr((List<Integer>) list);
return builder.setIntArrValue(b.build());
}
if (list.get(0) instanceof Long) {
LongArr.Builder b = LongArr.newBuilder().addAllArr((List<Long>) list);
return builder.setLongArrValue(b.build());
}
}
throw new RuntimeException("Unhandled type for buildValue: " + value);
}
public void activate(int traceId, String path) {
send(RootMessage.newBuilder()
.setRequestActivate(RequestActivate.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setObject(ObjSpec.newBuilder()
.setPath(ObjPath.newBuilder()
.setPath(path))))
.build());
}
public void disassemble(int traceId, long snap, Address start) {
send(RootMessage.newBuilder()
.setRequestDisassemble(RequestDisassemble.newBuilder()
.setOid(DomObjId.newBuilder()
.setId(traceId))
.setSnap(Snap.newBuilder().setSnap(snap))
.setStart(Addr.newBuilder()
.setSpace(start.getAddressSpace().getName())
.setOffset(start.getOffset())))
.build());
}
public void negotiate(String desc) {
RequestNegotiate.Builder builder = RequestNegotiate.newBuilder()
.setVersion(TraceRmiHandler.VERSION)
.setDescription(desc);
int i = 0;
for (RmiRemoteMethod m : methodRegistry.getMap().values()) {
Method method = buildMethod(m);
builder.addMethods(i++, method);
}
send(RootMessage.newBuilder()
.setRequestNegotiate(builder)
.build());
}
private Method buildMethod(RmiRemoteMethod method) {
Method.Builder builder = Method.newBuilder()
.setName(method.getName())
.setDescription(method.getDescription())
.setAction(method.getAction())
.setDisplay(method.getDisplay());
int i = 0;
for (RmiRemoteMethodParameter p : method.getParameters()) {
MethodParameter param = buildParameter(p);
builder.addParameters(i++, param);
}
return builder.build();
}
private MethodParameter buildParameter(RmiRemoteMethodParameter param) {
return MethodParameter.newBuilder()
.setName(param.getName())
.setDisplay(param.getDisplay())
.setDescription(param.getDescription())
.setType(param.getType())
.setDefaultValue(param.getDefaultValue())
.setRequired(param.isRequired())
.build();
}
public void handleInvokeMethod(int traceId, XRequestInvokeMethod req) {
RmiRemoteMethod rm = getMethod(req.getName());
Object[] arglist = new Object[req.getArgumentsCount()];
java.lang.reflect.Method m = rm.getMethod();
Map<String, MethodArgument> argmap = new HashMap<>();
for (int i = 0; i < req.getArgumentsCount(); i++) {
MethodArgument arg = req.getArguments(i);
argmap.put(arg.getName(), arg);
}
int i = 0;
for (Parameter p : m.getParameters()) {
MethodArgument arg = argmap.get(p.getName());
if (arg != null) {
Object obj = argToObject(traceId, arg);
arglist[i++] = obj;
}
}
try {
Object ret = m.invoke(rm.getContainer(), arglist);
if (ret != null) {
socket.send(RootMessage.newBuilder()
.setXreplyInvokeMethod(XReplyInvokeMethod.newBuilder()
.setReturnValue(buildValue(ret)))
.build());
}
}
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
| IOException e) {
String message = e.getMessage();
if (message != null) {
Msg.error(this, message);
try {
socket.send(RootMessage.newBuilder()
.setXreplyInvokeMethod(
XReplyInvokeMethod.newBuilder().setError(message))
.build());
}
catch (IOException e1) {
Msg.error(this, e1.getMessage());
}
}
}
}
private Object argToObject(int traceId, MethodArgument arg) {
if (arg == null) {
throw new RuntimeException("Null argument passed to argToObject");
}
Value value = arg.getValue();
if (value.hasStringValue()) {
return value.getStringValue();
}
if (value.hasStringArrValue()) {
return value.getStringArrValue();
}
if (value.hasBoolValue()) {
return value.getBoolValue();
}
if (value.hasBoolArrValue()) {
return value.getBoolArrValue();
}
if (value.hasCharValue()) {
return value.getCharValue();
}
if (value.hasCharArrValue()) {
return value.getCharArrValue();
}
if (value.hasShortValue()) {
return value.getShortValue();
}
if (value.hasShortArrValue()) {
return value.getShortArrValue();
}
if (value.hasIntValue()) {
return value.getIntValue();
}
if (value.hasIntArrValue()) {
return value.getIntArrValue();
}
if (value.hasLongValue()) {
return value.getLongValue();
}
if (value.hasLongArrValue()) {
return value.getLongArrValue();
}
if (value.hasAddressValue()) {
return decodeAddr(traceId, value.getAddressValue());
}
if (value.hasRangeValue()) {
return decodeRange(traceId, value.getRangeValue());
}
if (value.hasByteValue()) {
return value.getByteValue();
}
if (value.hasBytesValue()) {
return value.getBytesValue();
}
if (value.hasNullValue()) {
return value.getNullValue();
}
ObjDesc desc = value.getChildDesc();
String path = desc.getPath().getPath();
return proxyObjectPath(traceId, path);
}
private Address decodeAddr(int id, Addr addr) {
RmiTrace trace = traces.get(id);
return trace.memoryMapper.genAddr(addr.getSpace(), addr.getOffset());
}
private AddressRange decodeRange(int id, AddrRange rng) {
RmiTrace trace = traces.get(id);
Address start = trace.memoryMapper.genAddr(rng.getSpace(), rng.getOffset());
return new AddressRangeImpl(start, start.add(rng.getExtend()));
}
public void setRegistry(RmiMethodRegistry methodRegistry) {
RmiClient.methodRegistry = methodRegistry;
}
public RmiRemoteMethod getMethod(String name) {
return methodRegistry.getMap().get(name);
}
public Object startBatch() {
if (currentBatch == null) {
currentBatch = new RmiBatch();
}
currentBatch.inc();
return currentBatch;
}
public Object endBatch() {
RmiBatch cb = null;
if (0 == currentBatch.dec()) {
cb = currentBatch;
currentBatch = null;
}
if (cb != null) {
return cb.results();
}
return null;
}
public TargetObjectSchema getSchema(String schema) {
return schemaContext.getSchema(new SchemaName(schema));
}
public RootMessage getRequestsPoll() {
return requests.poll();
}
}

View File

@ -0,0 +1,75 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import ghidra.rmi.trace.TraceRmi.*;
import ghidra.util.Msg;
public class RmiMethodHandlerThread extends Thread {
private RmiClient client;
private ProtobufSocket<RootMessage> socket;
private boolean terminated = false;
public RmiMethodHandlerThread(RmiClient client, ProtobufSocket<RootMessage> socket) {
this.client = client;
this.socket = socket;
}
@Override
public void run() {
while (!terminated) {
try {
RootMessage msg = socket.recv();
if (msg.hasXrequestInvokeMethod()) {
try {
XRequestInvokeMethod req = msg.getXrequestInvokeMethod();
int id = req.getOid().getId();
RmiTrace trace = client.traces.get(id);
trace.handleInvokeMethod(req);
}
catch (Exception e) {
e.printStackTrace();
}
continue;
}
RootMessage request = client.getRequestsPoll();
if (msg.hasError()) {
Msg.error(this, msg);
}
else if (msg.hasReplyCreateObject()) {
ReplyCreateObject reply = msg.getReplyCreateObject();
RmiTrace trace = client.traces.get(request.getRequestCreateObject().getOid().getId());
trace.handleCreateObject(reply);
}
else if (msg.hasReplyCreateTrace()) {
ReplyCreateTrace reply = msg.getReplyCreateTrace();
RmiTrace trace = client.traces.get(request.getRequestCreateTrace().getOid().getId());
trace.handleCreateTrace(reply);
}
}
catch (Exception e) {
Msg.error(this, e.getMessage());
}
}
Msg.info(this, "Handler exiting");
}
public void close() {
terminated = true;
}
}

View File

@ -0,0 +1,50 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import java.lang.annotation.*;
import java.util.HashMap;
import java.util.Map;
public class RmiMethodRegistry {
/**
* An annotation for marking remote methods.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public static @interface TraceMethod {
String action();
String display() default "";
String description() default "";
String schema() default "ANY";
}
Map<String, RmiRemoteMethod> map = new HashMap<>();
public RmiRemoteMethod getMethod(String key) {
return map.get(key);
}
public void putMethod(String key, RmiRemoteMethod value) {
map.put(key, value);
}
public Map<String, RmiRemoteMethod> getMap() {
return map;
}
}

View File

@ -0,0 +1,22 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
public interface RmiMethods {
// This is a marker class - we need an instance of the methods container to invoke its methods
}

View File

@ -0,0 +1,129 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import ghidra.dbg.target.TargetMethod;
import ghidra.dbg.target.schema.*;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.rmi.trace.TraceRmi.Value;
public class RmiRemoteMethod {
private final SchemaContext schemaContext;
private String name;
private String action;
private String display;
private String description;
private RmiRemoteMethodParameter[] params;
private TargetObjectSchema schema;
private RmiMethods instance;
private Method m;
public RmiRemoteMethod(SchemaContext schemaContext, String name, String action, String display, String description, TargetObjectSchema schema, RmiMethods instance, Method m) {
this.schemaContext = schemaContext;
this.name = name;
this.action = action;
this.display = display;
this.description = description;
this.params = new RmiRemoteMethodParameter[m.getParameterCount()];
this.schema = schema;
this.instance = instance;
this.m = m;
int i = 0;
for (Parameter p : m.getParameters()) {
TargetObjectSchema pschema = getSchemaFromParameter(p);
String pname = p.getName(); // NB: don't change this unless yuou resolve the ordering issues
String pdesc = pname;
String pdisp = pname;
if (i == 0) {
RmiMethodRegistry.TraceMethod annot = m.getAnnotation(RmiMethodRegistry.TraceMethod.class);
if (annot != null) {
pschema = schemaContext.getSchema(new SchemaName(annot.schema()));
}
pdisp = "Object";
}
Value pdef = null;
TargetMethod.Param pannot = p.getAnnotation(TargetMethod.Param.class);
if (pannot != null) {
pdesc = pannot.description();
pdisp = pannot.display();
}
boolean required = i != 0;
params[i++] = new RmiRemoteMethodParameter(pname, pschema, required, pdef, pdisp, pdesc);
}
}
private TargetObjectSchema getSchemaFromParameter(Parameter p) {
if (p.getAnnotatedType().getType().equals(String.class)) {
return EnumerableTargetObjectSchema.STRING;
}
if (p.getAnnotatedType().getType().equals(Boolean.class)) {
return EnumerableTargetObjectSchema.BOOL;
}
if (p.getAnnotatedType().getType().equals(Integer.class)) {
return EnumerableTargetObjectSchema.INT;
}
if (p.getAnnotatedType().getType().equals(Long.class)) {
return EnumerableTargetObjectSchema.LONG;
}
if (p.getAnnotatedType().getType().equals(Address.class)) {
return EnumerableTargetObjectSchema.ADDRESS;
}
if (p.getAnnotatedType().getType().equals(AddressRange.class)) {
return EnumerableTargetObjectSchema.RANGE;
}
return EnumerableTargetObjectSchema.ANY;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getAction() {
return action;
}
public String getDisplay() {
return display;
}
public RmiRemoteMethodParameter[] getParameters() {
return params;
}
public Method getMethod() {
return m;
}
public TargetObjectSchema getSchema() {
return schema;
}
public RmiMethods getContainer() {
return instance;
}
}

View File

@ -0,0 +1,71 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.rmi.trace.TraceRmi.*;
public class RmiRemoteMethodParameter {
private final String name;
private final TargetObjectSchema schema;
private final boolean required;
private final Value defaultValue;
private final String display;
private final String description;
public RmiRemoteMethodParameter(String name, TargetObjectSchema schema, boolean required,
Value defaultValue, String display, String description) {
this.name = name;
this.schema = schema;
this.required = required;
this.defaultValue = defaultValue;
this.display = display;
this.description = description;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getDisplay() {
return display;
}
public ValueType getType() {
String schemaName = schema.getName().toString();
// if (schemaName.equals("ANY")) {
// return ValueType.newBuilder().setName("OBJECT").build();
// }
return ValueType.newBuilder().setName(schemaName).build();
}
public Value getDefaultValue() {
if (defaultValue != null) {
return defaultValue;
}
return Value.newBuilder().setNullValue(Null.newBuilder()).build();
}
public boolean isRequired() {
return required;
}
}

View File

@ -0,0 +1,216 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.lang.RegisterValue;
import ghidra.rmi.trace.TraceRmi.*;
import ghidra.trace.model.Lifespan;
import ghidra.util.LockHold;
import ghidra.util.Msg;
public class RmiTrace {
final RmiClient client;
private final int id;
private int nextTx = 0;
private Object txLock = new Object();
private ReadWriteLock snLock = new ReentrantReadWriteLock();
private Set<String> overlays = new HashSet<>();
private long currentSnap = -1;
private boolean closed = false;
public MemoryMapper memoryMapper;
public RegisterMapper registerMapper;
public RmiTrace(RmiClient client, int id) {
this.client = client;
this.id = id;
}
public void close() {
if (closed) {
return;
}
client.closeTrace(id);
}
public void save() {
client.saveTrace(id);
}
public RmiTransaction startTx(String description, boolean undoable) {
int txid;
synchronized(txLock) {
txid = nextTx++;
}
client.startTx(id, description, undoable, txid);
return new RmiTransaction(this, txid);
}
public RmiTransaction openTx(String description) {
return startTx(description, false);
}
public void endTx(int txid, boolean abort) {
client.endTx(id, txid, abort);
}
public long nextSnap() {
try (LockHold hold = LockHold.lock(snLock.writeLock())) {
return ++currentSnap;
}
}
public long snapshot(String description, String datatime, Long snap) {
if (snap == null) {
snap = nextSnap();
}
client.snapshot(id, description, datatime, snap.intValue());
return snap.longValue();
}
public long getSnap() {
try (LockHold hold = LockHold.lock(snLock.readLock())) {
return currentSnap;
}
}
public void setSnap(long snap) {
try (LockHold hold = LockHold.lock(snLock.writeLock())) {
this.currentSnap = snap;
}
}
public long snapOrCurrent(Long snap) {
try (LockHold hold = LockHold.lock(snLock.readLock())) {
return snap == null ? this.currentSnap : snap.longValue();
}
}
public void createOverlaySpace(String base, String name) {
if (overlays.contains(name)) {
return;
}
client.createOverlaySpace(id, base, name);
}
public void createOverlaySpace(Address repl, Address orig) {
createOverlaySpace(repl.getAddressSpace().getName(), orig.getAddressSpace().getName());
}
public void putBytes(Address addr, byte[] data, Long snap) {
client.putBytes(id, snapOrCurrent(snap), addr, data);
}
public void setMemoryState(AddressRange range, MemoryState state, Long snap) {
client.setMemoryState(id, snapOrCurrent(snap), range, state);
}
public void deleteBytes(AddressRange range, Long snap) {
client.deleteBytes(id, snapOrCurrent(snap), range);
}
public void putRegisters(String ppath, RegisterValue[] values, Long snap) {
client.putRegisters(id, snapOrCurrent(snap), ppath, values);
}
public void deleteRegisters(String ppath, String[] names, Long snap) {
client.deleteRegisters(id, snapOrCurrent(snap), ppath, names);
}
public void createRootObject(SchemaContext schemaContext, String schema) {
client.createRootObject(id, schemaContext, schema);
}
public void createObject(String path) {
client.createObject(id, path);
}
public void handleCreateObject(ReplyCreateObject reply) {
RmiTraceObject obj = new RmiTraceObject(this, reply.getObject());
try (RmiTransaction tx = startTx("CreateObject", false); LockHold hold = LockHold.lock(snLock.readLock())) {
obj.insert(currentSnap, null);
}
}
public void handleCreateTrace(ReplyCreateTrace reply) {
}
public void insertObject(String path) {
Lifespan span = getLifespan();
client.insertObject(id, path, span, Resolution.CR_ADJUST);
}
private Lifespan getLifespan() {
try (LockHold hold = LockHold.lock(snLock.readLock())) {
return Lifespan.nowOn(currentSnap);
}
}
public void setValue(String ppath, String key, Object value) {
Lifespan span = getLifespan();
client.setValue(id, ppath, span, key, value, null);
}
public void retainValues(String ppath, Set<String> keys, ValueKinds kinds) {
Lifespan span = getLifespan();
client.retainValues(id, ppath, span, kinds, keys);
}
public void activate(String path) {
if (path == null) {
Msg.error(this, "Attempt to activate null");
}
client.activate(id, path);
}
public void disassemble(Address start, Long snap) {
client.disassemble(id, snapOrCurrent(snap), start);
}
public void handleInvokeMethod(XRequestInvokeMethod req) {
try (RmiTransaction tx = startTx("InvokeMethod", false)) {
client.handleInvokeMethod(id, req);
}
}
public RmiTraceObject proxyObjectId(Long objectId) {
return client.proxyObjectId(id, objectId);
}
public RmiTraceObject proxyObjectPath(String path) {
return client.proxyObjectPath(id, path);
}
public RmiTraceObject proxyObjectPath(Long objectId, String path) {
return client.proxyObjectPath(id, objectId, path);
}
public int getId() {
return id;
}
}

View File

@ -0,0 +1,79 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import java.util.Set;
import ghidra.rmi.trace.TraceRmi.*;
import ghidra.trace.model.Lifespan;
public class RmiTraceObject {
private RmiTrace trace;
private ObjSpec spec;
private String path;
public RmiTraceObject(RmiTrace trace, ObjSpec spec) {
this.trace = trace;
this.spec = spec;
this.path = spec.getPath().getPath();
}
public RmiTraceObject(RmiTrace trace, Long id, String path) {
this.trace = trace;
this.path = path;
}
public static RmiTraceObject fromId(RmiTrace trace, long id) {
return new RmiTraceObject(trace, id, null);
}
public static RmiTraceObject fromPath(RmiTrace trace, String path) {
return new RmiTraceObject(trace, null, path);
}
public void insert(long snap, Resolution resolution) {
if (resolution == null) {
resolution = Resolution.CR_ADJUST;
}
Lifespan span = Lifespan.nowOn(snap);
trace.client.insertObject(trace.getId(), spec, span, resolution);
}
public void remove(long snap, boolean tree) {
Lifespan span = Lifespan.nowOn(snap);
trace.client.removeObject(trace.getId(), spec, span, tree);
}
public void setValue(String key, Object value, long snap, String resolution) {
Lifespan span = Lifespan.nowOn(snap);
trace.client.setValue(trace.getId(), path, span, key, value, resolution);
}
public void retainValues(Set<String> keys, long snap, ValueKinds kinds) {
Lifespan span = Lifespan.nowOn(snap);
trace.client.retainValues(trace.getId(), path, span, kinds, keys);
}
public void activate() {
trace.client.activate(trace.getId(), path);
}
public String getPath() {
return path;
}
}

View File

@ -0,0 +1,64 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.client.tracermi;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import ghidra.util.LockHold;
public class RmiTransaction implements AutoCloseable {
private RmiTrace trace;
private int id;
private ReadWriteLock lock = new ReentrantReadWriteLock();
private boolean closed = false;
public RmiTransaction(RmiTrace trace, int id) {
this.trace = trace;
this.id = id;
}
public void commit() {
try (LockHold hold = LockHold.lock(lock.writeLock())) {
if (closed) {
return;
}
closed = true;
}
trace.endTx(id, false);
}
public void abort() {
try (LockHold hold = LockHold.lock(lock.writeLock())) {
if (closed) {
return;
}
closed = true;
}
trace.endTx(id, true);
}
@Override
public void close() {
commit();
}
public RmiTransaction startTx(String description, boolean b) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -94,6 +94,13 @@ public class LaunchFailureDialog extends OptionDialog {
result.trace() != null;
}
protected static String shorten(String title) {
if (title.length() > 80) {
return title.substring(0, 77) + "...";
}
return title;
}
protected static String htmlContent(TerminalSession session) {
String content = session.content().trim();
List<String> lines = Arrays.asList(content.split("\n"));
@ -108,7 +115,7 @@ public class LaunchFailureDialog extends OptionDialog {
<div style="font:bold;">Title: %s</div>%s
<div style="background:black;color:white;">
<pre>%s</pre>""".formatted(
session.title(),
HTMLUtilities.escapeHTML(shorten(session.title())),
note,
content);
}

View File

@ -244,6 +244,9 @@ public class TraceRmiHandler implements TraceRmiConnection {
this.plugin = plugin;
plugin.addHandler(this);
this.socket = socket;
if (socket == null) {
throw new RuntimeException("Socket cannot be null");
}
this.in = socket.getInputStream();
this.out = socket.getOutputStream();

View File

@ -391,8 +391,9 @@ public class TraceRmiTarget extends AbstractTarget {
boolean allowSuitableObject) {
Map<String, ActionEntry> result = new HashMap<>();
for (RemoteMethod m : methods) {
result.put(m.name(), createEntry(m, context, allowContextObject, allowCoordsObject,
allowSuitableObject));
ActionEntry entry = createEntry(m, context, allowContextObject, allowCoordsObject,
allowSuitableObject);
result.put(m.name(), entry);
}
return result;
}