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

时间:2017-12-22 10:13:43

标签: java debugging jdi

我希望使用java debug interface构建调试器。 我的目标是设置断点并获取变量的值。
我发现this答案接近我要找的内容,我知道我必须使用以下界面: - VirtualMachineManagerLaunchingConnectorClassPrepareEventClassPrepareRequest。 但我无法弄清楚,如何在特定行设置断点并获取变量的值或接口的使用顺序。

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

import java.io.*;

class Hello {

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

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

2 个答案:

答案 0 :(得分:3)

发现此article有用。 这也是一个很好的example,可以帮助你。

或者,您可以查看以下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>");

答案 1 :(得分:0)

要回答有关接口的问题,这里是快速解释。

虚拟机(VM)-运行调试目标程序的JVM。

连接器-将调试器程序连接到调试目标的JVM。 LaunchingConnector将启动JVM并连接到它。还有连接到现有正在运行的JVM的AttachingConnector。

事件-VM在调试模式下运行调试目标程序时,它将触发多个事件,以便调试程序可以根据需要采取措施。调试器程序还可以请求VM触发默认情况下不会触发的某些特殊事件。

要回答问题的断点,这是一个片段。

Location location = classType.locationsOfLine(lineNumberToPutBreakpoint).get(0);
                    BreakpointRequest bpReq = vm.eventRequestManager().createBreakpointRequest(location);
                    bpReq.enable();

This article具有完整的简单Hello World示例,并有进一步的说明。可能对入门的基本了解很有帮助。