代码之家  ›  专栏  ›  技术社区  ›  Satnam Sandhu

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

  •  2
  • Satnam Sandhu  · 技术社区  · 7 年前

    我希望使用 java debug interface .
    我的目标是设置断点并获取变量的值。
    我发现 this 答案接近我所寻找的,我理解我必须使用以下接口:- 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

    2 回复  |  直到 7 年前
        1
  •  8
  •   isnvi23h4    7 年前

    找到这个 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>");
    
        2
  •  1
  •   Ravi K    5 年前

    为了回答有关接口的问题,这里有一个快速的解释。

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

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

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

    为了回答问题的转折点部分,这里有一个片段。

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

    This article 有完整的简单Hello-World示例和进一步的解释。开始了解基本知识可能会很有用。