使用 jdi 构建一个简单的调试器来设置断点并检索变量的值
build a simple debugger with jdi to set breakpoints and retrieve the value of a variable
我希望使用 java debug interface 构建调试器。
我的objective是设置断点,获取一个变量的值。
我找到了 接近我正在寻找的答案,我知道我必须使用以下界面:- VirtualMachineManager
、LaunchingConnector
、ClassPrepareEvent
, ClassPrepareRequest
。
但是我想不通,如何在特定行设置断点并获取变量的值,或者应该以什么顺序使用接口。
例如在下面的代码中,我如何使用 jdi
继续 运行 它以便我得到变量的值 S
import java.io.*;
class Hello {
public static void main(String args[]) {
String S = "Hello World";
int a = 12;
}
}
我正在考虑在 a = 12
行或方法 main
结束时设置调试点,以便我得到 S
的值
发现此 article 有用。
这里还有一个很好的 可以帮助你。
或者,您可以检查以下内容project
这里有一个示例代码供您继续使用。
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package jdidebugger;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.Bootstrap;
import com.sun.jdi.ClassType;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.VirtualMachineManager;
import com.sun.jdi.connect.Connector;
import com.sun.jdi.connect.IllegalConnectorArgumentsException;
import com.sun.jdi.connect.LaunchingConnector;
import com.sun.jdi.connect.VMStartException;
import com.sun.jdi.event.BreakpointEvent;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventIterator;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.request.BreakpointRequest;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.EventRequestManager;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author bonnie
*/
public class JdiDebugger {
/**
* @param options
* @param main
* @param classPattern
* @param methodName
* @param lineNumber
* @throws java.io.IOException
* @throws com.sun.jdi.connect.IllegalConnectorArgumentsException
* @throws com.sun.jdi.connect.VMStartException
* @throws java.lang.InterruptedException
* @throws com.sun.jdi.AbsentInformationException
* @throws com.sun.jdi.IncompatibleThreadStateException
*/
public static void onMethodExit(String options, String main, String classPattern, String methodName) throws IOException, IllegalConnectorArgumentsException, VMStartException, InterruptedException, AbsentInformationException, IncompatibleThreadStateException {
// create and launch a virtual machine
VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
LaunchingConnector lc = vmm.defaultConnector();
Map<String, Connector.Argument> env = lc.defaultArguments();
env.get("options").setValue(options);
env.get("main").setValue(main);
VirtualMachine vm = lc.launch(env);
// create a class prepare request
EventRequestManager erm = vm.eventRequestManager();
ClassPrepareRequest r = erm.createClassPrepareRequest();
r.addClassFilter(classPattern);
r.enable();
EventQueue queue = vm.eventQueue();
while (true) {
EventSet eventSet = queue.remove();
EventIterator it = eventSet.eventIterator();
while (it.hasNext()) {
Event event = it.nextEvent();
if (event instanceof ClassPrepareEvent) {
ClassPrepareEvent evt = (ClassPrepareEvent) event;
ClassType classType = (ClassType) evt.referenceType();
classType.methodsByName(methodName).forEach(new Consumer<Method>() {
@Override
public void accept(Method m) {
List<Location> locations = null;
try {
locations = m.allLineLocations();
} catch (AbsentInformationException ex) {
Logger.getLogger(JdiDebuggerOld.class.getName()).log(Level.SEVERE, null, ex);
}
// get the last line location of the function and enable the
// break point
Location location = locations.get(locations.size() - 1);
BreakpointRequest bpReq = erm.createBreakpointRequest(location);
bpReq.enable();
}
});
}
if (event instanceof BreakpointEvent) {
// disable the breakpoint event
event.request().disable();
ThreadReference thread = ((BreakpointEvent) event).thread();
StackFrame stackFrame = thread.frame(0);
// print all the visible variables with the respective values
Map<LocalVariable, Value> visibleVariables = (Map<LocalVariable, Value>) stackFrame.getValues(stackFrame.visibleVariables());
for (Map.Entry<LocalVariable, Value> entry : visibleVariables.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
vm.resume();
}
}
}
}
这就是调用方法的方式
new jdiDebugger().onMehtodeExit("-cp <whatever your class path is>", "<name of the class that contains the main method>", "<the name of the class that you wish to debug>", "<the name of the method that you want to debug>");
为了回答关于接口的问题,这里是快速解释。
虚拟机 (VM) - JVM,它是 运行 调试目标程序。
Connector - 这将调试器程序连接到调试目标的 JVM。 LaunchingConnector 将启动 JVM 并连接到它。还有连接到现有 运行 JVM 的 AttachingConnector。
Events - 当 VM 在调试模式下运行调试目标程序时,它会触发几个事件,以便调试程序可以根据需要采取行动。调试器程序还可以请求VM触发某些默认不触发的特殊事件。
为了回答问题的断点部分,这里有一个片段。
Location location = classType.locationsOfLine(lineNumberToPutBreakpoint).get(0);
BreakpointRequest bpReq = vm.eventRequestManager().createBreakpointRequest(location);
bpReq.enable();
This article 有完整的简单 Hello World 示例和进一步的解释。可能会发现对开始基本理解很有用。
我希望使用 java debug interface 构建调试器。
我的objective是设置断点,获取一个变量的值。
我找到了 VirtualMachineManager
、LaunchingConnector
、ClassPrepareEvent
, ClassPrepareRequest
。
但是我想不通,如何在特定行设置断点并获取变量的值,或者应该以什么顺序使用接口。
例如在下面的代码中,我如何使用 jdi
继续 运行 它以便我得到变量的值 S
import java.io.*;
class Hello {
public static void main(String args[]) {
String S = "Hello World";
int a = 12;
}
}
我正在考虑在 a = 12
行或方法 main
结束时设置调试点,以便我得到 S
的值
发现此 article 有用。
这里还有一个很好的
或者,您可以检查以下内容project
这里有一个示例代码供您继续使用。
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package jdidebugger;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.Bootstrap;
import com.sun.jdi.ClassType;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.VirtualMachineManager;
import com.sun.jdi.connect.Connector;
import com.sun.jdi.connect.IllegalConnectorArgumentsException;
import com.sun.jdi.connect.LaunchingConnector;
import com.sun.jdi.connect.VMStartException;
import com.sun.jdi.event.BreakpointEvent;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventIterator;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.request.BreakpointRequest;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.EventRequestManager;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author bonnie
*/
public class JdiDebugger {
/**
* @param options
* @param main
* @param classPattern
* @param methodName
* @param lineNumber
* @throws java.io.IOException
* @throws com.sun.jdi.connect.IllegalConnectorArgumentsException
* @throws com.sun.jdi.connect.VMStartException
* @throws java.lang.InterruptedException
* @throws com.sun.jdi.AbsentInformationException
* @throws com.sun.jdi.IncompatibleThreadStateException
*/
public static void onMethodExit(String options, String main, String classPattern, String methodName) throws IOException, IllegalConnectorArgumentsException, VMStartException, InterruptedException, AbsentInformationException, IncompatibleThreadStateException {
// create and launch a virtual machine
VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
LaunchingConnector lc = vmm.defaultConnector();
Map<String, Connector.Argument> env = lc.defaultArguments();
env.get("options").setValue(options);
env.get("main").setValue(main);
VirtualMachine vm = lc.launch(env);
// create a class prepare request
EventRequestManager erm = vm.eventRequestManager();
ClassPrepareRequest r = erm.createClassPrepareRequest();
r.addClassFilter(classPattern);
r.enable();
EventQueue queue = vm.eventQueue();
while (true) {
EventSet eventSet = queue.remove();
EventIterator it = eventSet.eventIterator();
while (it.hasNext()) {
Event event = it.nextEvent();
if (event instanceof ClassPrepareEvent) {
ClassPrepareEvent evt = (ClassPrepareEvent) event;
ClassType classType = (ClassType) evt.referenceType();
classType.methodsByName(methodName).forEach(new Consumer<Method>() {
@Override
public void accept(Method m) {
List<Location> locations = null;
try {
locations = m.allLineLocations();
} catch (AbsentInformationException ex) {
Logger.getLogger(JdiDebuggerOld.class.getName()).log(Level.SEVERE, null, ex);
}
// get the last line location of the function and enable the
// break point
Location location = locations.get(locations.size() - 1);
BreakpointRequest bpReq = erm.createBreakpointRequest(location);
bpReq.enable();
}
});
}
if (event instanceof BreakpointEvent) {
// disable the breakpoint event
event.request().disable();
ThreadReference thread = ((BreakpointEvent) event).thread();
StackFrame stackFrame = thread.frame(0);
// print all the visible variables with the respective values
Map<LocalVariable, Value> visibleVariables = (Map<LocalVariable, Value>) stackFrame.getValues(stackFrame.visibleVariables());
for (Map.Entry<LocalVariable, Value> entry : visibleVariables.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
vm.resume();
}
}
}
}
这就是调用方法的方式
new jdiDebugger().onMehtodeExit("-cp <whatever your class path is>", "<name of the class that contains the main method>", "<the name of the class that you wish to debug>", "<the name of the method that you want to debug>");
为了回答关于接口的问题,这里是快速解释。
虚拟机 (VM) - JVM,它是 运行 调试目标程序。
Connector - 这将调试器程序连接到调试目标的 JVM。 LaunchingConnector 将启动 JVM 并连接到它。还有连接到现有 运行 JVM 的 AttachingConnector。
Events - 当 VM 在调试模式下运行调试目标程序时,它会触发几个事件,以便调试程序可以根据需要采取行动。调试器程序还可以请求VM触发某些默认不触发的特殊事件。
为了回答问题的断点部分,这里有一个片段。
Location location = classType.locationsOfLine(lineNumberToPutBreakpoint).get(0);
BreakpointRequest bpReq = vm.eventRequestManager().createBreakpointRequest(location);
bpReq.enable();
This article 有完整的简单 Hello World 示例和进一步的解释。可能会发现对开始基本理解很有用。