使用 jdi 构建一个简单的调试器来设置断点并检索变量的值

Sat*_*dhu 2 java debugging jdi

我正在寻找使用java debug interface构建调试器。
我的目标是设置断点并获取变量的值。
我发现这个答案接近我正在寻找的答案,我知道我必须使用以下接口:- VirtualMachineManager, LaunchingConnector, ClassPrepareEvent, ClassPrepareRequest. 但我无法弄清楚,如何在特定行设置断点并获取变量的值或应以何种顺序使用接口。

例如在下面的代码中,我如何继续运行它以jdi获取变量的值S

import java.io.*;

class Hello {

  public static void main(String args[]) {
    String S = "Hello World";
    int a = 12;
  }
}
Run Code Online (Sandbox Code Playgroud)

我正在考虑在线a = 12或在方法结束时设置调试点, main以便我获得S

sou*_*der 6

发现这篇文章很有用。这里也是一个很好的例子,可以帮助你。

或者,您可以检查以下项目

这里有一个示例代码供您使用。

/*
 * 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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是你调用方法的方式

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>");
Run Code Online (Sandbox Code Playgroud)