mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-23 12:49:45 +00:00
GP-4760: Implement TraceRmi for JPDA (Java/Dalvik targets)
This commit is contained in:
parent
62819ff301
commit
6d39e7231c
@ -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')
|
||||
|
@ -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|
|
||||
|
28
Ghidra/Debug/Debugger-jpda/data/debugger-launchers/attach-java.jsh
Executable file
28
Ghidra/Debug/Debugger-jpda/data/debugger-launchers/attach-java.jsh
Executable 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();
|
27
Ghidra/Debug/Debugger-jpda/data/debugger-launchers/bypid-java.jsh
Executable file
27
Ghidra/Debug/Debugger-jpda/data/debugger-launchers/bypid-java.jsh
Executable 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();
|
29
Ghidra/Debug/Debugger-jpda/data/debugger-launchers/local-java.jsh
Executable file
29
Ghidra/Debug/Debugger-jpda/data/debugger-launchers/local-java.jsh
Executable 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();
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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>
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user