java debug 體系-jdi

??????JDI屬于JPDA中最上層接口。定義了調(diào)試器(Debugger)所需要的一些調(diào)試接口?;谶@些接口,調(diào)試器可以及時(shí)地了解目標(biāo)虛擬機(jī)的狀態(tài),例如查看目標(biāo)虛擬機(jī)上有哪些類(lèi)和實(shí)例等。另外,調(diào)試者還可以控制目標(biāo)虛擬機(jī)的執(zhí)行,例如掛起和恢復(fù)目標(biāo)虛擬機(jī)上的線(xiàn)程,設(shè)置斷點(diǎn)等。

0、工作方式

??????首先,調(diào)試器(Debuuger)通過(guò) Bootstrap 獲取唯一的虛擬機(jī)管理器。
虛擬機(jī)管理器將在第一次被調(diào)用時(shí)初始化可用的鏈接器。一般地,調(diào)試器會(huì)默認(rèn)地采用啟動(dòng)型鏈接器進(jìn)行鏈接。
??????然后,調(diào)試器調(diào)用鏈接器的 launch () 來(lái)啟動(dòng)目標(biāo)程序,并完成調(diào)試器與目標(biāo)虛擬機(jī)的鏈接。
??????當(dāng)鏈接完成后,調(diào)試器與目標(biāo)虛擬機(jī)便可以進(jìn)行雙向通信了。調(diào)試器將用戶(hù)的操作轉(zhuǎn)化為調(diào)試命令,命令通過(guò)鏈接被發(fā)送到前端運(yùn)行目標(biāo)程序的虛擬機(jī)上;然后,目標(biāo)虛擬機(jī)根據(jù)接受的命令做出相應(yīng)的操作,將調(diào)試的結(jié)果發(fā)回給后端的調(diào)試器;最后,調(diào)試器可視化數(shù)據(jù)信息反饋給用戶(hù)。

1、模塊劃分

??????通過(guò)上面的描述,我們可以將jdi分成3部分: 數(shù)據(jù)模塊、連接模塊、事件處理模塊。

1.1 數(shù)據(jù)模塊

??????jdi的數(shù)據(jù)模塊,主要就是Mirror機(jī)制。Mirror 接口是JDI最底層的接口,JDI中幾乎所有其他接口都繼承于它。Mirror 機(jī)制是將目標(biāo)虛擬機(jī)上的所有數(shù)據(jù)、類(lèi)型、域、方法、事件、狀態(tài)和資源,以及調(diào)試器發(fā)向目標(biāo)虛擬機(jī)的事件請(qǐng)求等都映射成 Mirror 對(duì)象。

例如,在目標(biāo)虛擬機(jī)上,已裝載的類(lèi)被映射成 ReferenceType 鏡像,對(duì)象實(shí)例被映射成 ObjectReference 鏡像,基本類(lèi)型的值(如 float 等)被映射成 PrimitiveValue(如 FloatValue 等)。被調(diào)試的目標(biāo)程序的運(yùn)行狀態(tài)信息被映射到 StackFrame 鏡像中,在調(diào)試過(guò)程中所觸發(fā)的事件被映射成 Event 鏡像(如 StepEvent 等),調(diào)試器發(fā)出的事件請(qǐng)求被映射成 EventRequest 鏡像(如 StepRequest 等),被調(diào)試的目標(biāo)虛擬機(jī)則被映射成 VirtualMachine 鏡像。但是,JDI 并不保證目標(biāo)虛擬機(jī)上的每份信息和資源都只有唯一的鏡像與之對(duì)應(yīng),這是由 JDI 的具體實(shí)現(xiàn)所決定的。例如,目標(biāo)虛擬機(jī)上的某個(gè)事件有可能存在多個(gè) Event 鏡像與之對(duì)應(yīng),例如 BreakpointEvent 等。

??????Mirror 實(shí)例或是由調(diào)試器創(chuàng)建,或是由目標(biāo)虛擬機(jī)創(chuàng)建,調(diào)用 Mirror 實(shí)例 virtualMachine() 可以獲取其虛擬機(jī)信息。該接口提供了一套方法,可以用來(lái)直接或間接地獲取目標(biāo)虛擬機(jī)上所有的數(shù)據(jù)和狀態(tài)信息,也可以?huà)炱?、恢?fù)、終止目標(biāo)虛擬機(jī)。

1.2 連接模塊

??????連接是調(diào)試器與目標(biāo)虛擬機(jī)之間交互的渠道,一次連接可以由調(diào)試器發(fā)起,也可以由被調(diào)試的目標(biāo)虛擬機(jī)發(fā)起。一個(gè)調(diào)試器可以連接多個(gè)目標(biāo)虛擬機(jī),但一個(gè)目標(biāo)虛擬機(jī)最多只能連接一個(gè)調(diào)試器。下面的例子中就講了一種常見(jiàn)的連接方式: 由調(diào)試器啟動(dòng)目標(biāo)虛擬機(jī)的連接方式。也可以在虛擬機(jī)處于運(yùn)行狀態(tài)時(shí),采用attach的方式連接到目標(biāo)虛擬機(jī)(我們平時(shí)用的Intellij 用的就是這種方式)。

1.3 事件處理模塊

??????主要在com.sun.jdi.event 和 com.sun.jdi.request 包中。

  • 事件類(lèi)型: JDI中事件的接口叫Event . 它定義了18種具體的事件類(lèi)型。


    事件類(lèi)型

    事件集是事件發(fā)送的最小單位,并且事件集一旦被創(chuàng)建,則不可以被修改。

  • 事件請(qǐng)求:Event接口定義了request方法,該方法會(huì)返回由調(diào)試器Debugger發(fā)出的針對(duì)該事件的事件請(qǐng)求(EventRequest)。事件請(qǐng)求是由調(diào)試器向目標(biāo)虛擬機(jī)發(fā)出的,目的是請(qǐng)求目標(biāo)虛擬機(jī)在發(fā)生指定的事件后通知調(diào)試器。只有當(dāng)調(diào)試器發(fā)出的請(qǐng)求與目標(biāo)虛擬機(jī)上發(fā)生的事件匹配時(shí),這些事件才會(huì)被分發(fā)到各個(gè)事件集,進(jìn)而等待發(fā)送至調(diào)試器端。

??????當(dāng)然了,Debugger發(fā)送給Target VM的所有事件請(qǐng)求,不一定Target VM 都感興趣。因此JDI提供了事件的過(guò)濾機(jī)制,來(lái)刪選出最終真正要發(fā)送給Target VM的事件。

  • 對(duì)事件請(qǐng)求的管理: 在JDI中,事件請(qǐng)求的管理是通過(guò)EventRequestManager來(lái)完成的。它有許多createXXXRequest方法來(lái)創(chuàng)建不同類(lèi)型的事件請(qǐng)求,也有許多deleteXXXRequest方法來(lái)刪除不同類(lèi)型的事件請(qǐng)求,還有xxxRequests方法來(lái)列出各種類(lèi)型的事件請(qǐng)求。有一點(diǎn)需要注意的是,這里由EventRequestManager創(chuàng)建的createXXXRequest的事件都是非激活的,因此這些事件請(qǐng)求當(dāng)發(fā)送給Target VM不會(huì)起任何作用,除非調(diào)用EventRequest的setEnable(true)使得該事件進(jìn)入激活狀態(tài)。
  • 事件隊(duì)列:事件隊(duì)列(EventQueue)的擁有者是目標(biāo)虛擬機(jī),EventQueue 將這些事件集以“先進(jìn)先出”策略依次地發(fā)送到調(diào)試器端。EventQueue 負(fù)責(zé)管理來(lái)自目標(biāo)虛擬機(jī)的事件,一個(gè)被調(diào)試的目標(biāo)虛擬機(jī)上有且僅有一個(gè) EventQueue實(shí)例。
  • Debugger 跟 targetVM之間的事件交互:
  • Debugger調(diào)用Target VM的 eventQueue() 和 eventRequestManager() 分別獲取唯一的 EventQueue 實(shí)例和 EventRequestManager 實(shí)例.
  • Debugger通過(guò) EventRequestManager 的 createXXXRequest() 創(chuàng)建需要的事件請(qǐng)求,并添加過(guò)濾器和設(shè)置掛起策略.
  • targetVM 上某個(gè)事件觸發(fā)且匹配上eventRequest , 則將event放入對(duì)應(yīng)的eventSet.
  • targetVM 上的EventQueue 管理這些eventSet, 按照FIFO原則發(fā)送給Debugger.
  • Debugger通過(guò)第一步獲取到的EventQueue實(shí)例 獲取來(lái)自Target VM的事件響應(yīng)。

一句話(huà)概括就是 EventRequest總是由Debugger發(fā)向Target VM ,而當(dāng)請(qǐng)求與目標(biāo)虛擬機(jī)上發(fā)生事件匹配,則事件會(huì)被歸到EventSet中,EventSet會(huì)被Target VM的EventQueue所管理,并且按照FIFO原則發(fā)送到Debugger

2、一個(gè)例子

(1)首先來(lái)個(gè)測(cè)試

public class HelloWorld {
    public static void main(String[] args) {
        String str = "Hello world!";
        System.out.println(str);
    }
}

(2)JDI agent的例子

import java.util.List;
import java.util.Map;
import com.sun.jdi.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;
/**
 * Created by zhangpeng48 on 2018/7/16.
 */
public class MethodTrace {
    private static VirtualMachine vm;
    private static Process process;
    private static EventRequestManager eventRequestManager;
    private static EventQueue eventQueue;
    private static EventSet eventSet;
    private static boolean vmExit = false;
    //write your own testclass
    private static String className = "HelloWorld";

    public static void main(String[] args) throws Exception {
        System.out.println("begin....");
        launchDebugee();
        registerEvent();

        processDebuggeeVM();

        // Enter event loop
        eventLoop();

        destroyDebuggeeVM();

    }

    public static void launchDebugee() {
        LaunchingConnector launchingConnector = Bootstrap
                .virtualMachineManager().defaultConnector();

        // Get arguments of the launching connector
        Map<String, Connector.Argument> defaultArguments = launchingConnector
                .defaultArguments();
        Connector.Argument mainArg = defaultArguments.get("main");
        Connector.Argument suspendArg = defaultArguments.get("suspend");

        // Set class of main method
        mainArg.setValue(className);
        suspendArg.setValue("true");
        try {
            vm = launchingConnector.launch(defaultArguments);
        } catch (Exception e) {
            // ignore
        }
    }

    public static void processDebuggeeVM() {
        process = vm.process();
    }

    public static void destroyDebuggeeVM() {
        process.destroy();
    }

    public static void registerEvent() {
        // Register ClassPrepareRequest
        eventRequestManager = vm.eventRequestManager();
        MethodEntryRequest entryReq = eventRequestManager.createMethodEntryRequest();

        entryReq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
        entryReq.addClassFilter(className);
        entryReq.enable();

        MethodExitRequest exitReq = eventRequestManager.createMethodExitRequest();
        exitReq.addClassFilter(className);
        exitReq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
        exitReq.enable();
    }

    private static void eventLoop() throws Exception {
        eventQueue = vm.eventQueue();
        while (true) {
            Thread.sleep(10000);
            if (vmExit == true) {
                System.out.println("vmexit");
                break;
            }
            eventSet = eventQueue.remove();
            EventIterator eventIterator = eventSet.eventIterator();
            while (eventIterator.hasNext()) {
                Event event = (Event) eventIterator.next();
                execute(event);
                if (!vmExit) {
                    eventSet.resume();
                }
            }
        }
    }

    private static void execute(Event event) throws Exception {
        if (event instanceof VMStartEvent) {
            System.out.println("VM started");
        } else if (event instanceof MethodEntryEvent) {
            Method method = ((MethodEntryEvent) event).method();
            System.out.printf("Enter -> Method: %s, Signature:%s\n",method.name(),method.signature());
            System.out.printf("\t ReturnType:%s\n", method.returnTypeName());
        } else if (event instanceof MethodExitEvent) {
            Method method = ((MethodExitEvent) event).method();
            System.out.printf("Exit -> method: %s\n",method.name());
        } else if (event instanceof VMDisconnectEvent) {
            vmExit = true;
        }
    }
}

(3)編譯與運(yùn)行
編譯測(cè)試 HelloWorld :

javac HelloWorld.java

編譯 JDI agent:

#注意 classpath 多個(gè)引用時(shí),在linux環(huán)境使用“:”分割。
javac -classpath $JAVA_HOME/lib/tools.jar:. MethodTrace.java    

運(yùn)行:

java -classpath $JAVA_HOME/lib/tools.jar:. MethodTrace HelloWorld

結(jié)果是這個(gè)樣子的~

結(jié)果

4)demo下載地址
鏈接: https://pan.baidu.com/s/1bKVKexvWKCh6F0hG4nkRDQ 密碼: etk3

3、核心api的分析

源碼地址:
hotspot實(shí)現(xiàn): hotspot的實(shí)現(xiàn)
JDK自帶實(shí)現(xiàn): openjdk 的實(shí)現(xiàn)

本文主要是對(duì)JDK自帶的實(shí)現(xiàn)進(jìn)行分析~

3.1 launchDebugee

launchDebugee

首先獲取一個(gè)獲取虛擬機(jī)管理器virtualmachineManager, 然后獲取默認(rèn)的連接器

public List<LaunchingConnector> launchingConnectors() {
        ArrayList var1 = new ArrayList(this.connectors.size());
        Iterator var2 = this.connectors.iterator();

        while(var2.hasNext()) {
            Connector var3 = (Connector)var2.next();
            if(var3 instanceof LaunchingConnector) {
                var1.add((LaunchingConnector)var3);
            }
        }

        return Collections.unmodifiableList(var1);
    }

然后為連接器設(shè)置參數(shù) 主要是設(shè)置main(mainArg.setValue 設(shè)置該main對(duì)應(yīng)哪個(gè)類(lèi)), 跟suspend (suspend=y/n 是否在調(diào)試客戶(hù)端建立連接之后啟動(dòng) VM 。如果是y,則方便調(diào)試vm啟動(dòng)過(guò)程中的一些步驟。本文設(shè)置為true.)
最后 launch ( 啟動(dòng)目標(biāo)程序,連接調(diào)試器(Debuuger)與目標(biāo)虛擬機(jī)(VirtualMachine)) 。主要是創(chuàng)建了一個(gè)socket 以及一個(gè)VM . 代碼如下。

public VirtualMachine launch(Map<String, ? extends Argument> var1) throws IOException, IllegalConnectorArgumentsException, VMStartException {
        // 獲取各種參數(shù)
            String var3 = this.argument("home", var1).value();
        String var4 = this.argument("options", var1).value();
        String var5 = this.argument("main", var1).value();
        boolean var6 = ((BooleanArgumentImpl)this.argument("suspend", var1)).booleanValue();
        String var7 = this.argument("quote", var1).value();
        String var8 = this.argument("vmexec", var1).value();
        String var9 = null;
        if(var7.length() > 1) {
            throw new IllegalConnectorArgumentsException("Invalid length", "quote");
        } else if(var4.indexOf("-Djava.compiler=") != -1 && var4.toLowerCase().indexOf("-djava.compiler=none") == -1) {
            throw new IllegalConnectorArgumentsException("Cannot debug with a JIT compiler", "options");
        } else {
            // 進(jìn)入主題
            ListenKey var10;
            String var13;
            if(!this.usingSharedMemory) {
              // 本文分析的是SunCommandLineLauncher ,本默認(rèn)走這個(gè)分支。  下面函數(shù)的作用是綁定了一個(gè)socket 到 SocketTransportService 
                var10 = this.transportService().startListening();
            } else {
                Random var11 = new Random();
                int var12 = 0;

                while(true) {
                    try {
                        var13 = "javadebug" + String.valueOf(var11.nextInt(100000));
                        var10 = this.transportService().startListening(var13);
                        break;
                    } catch (IOException var18) {
                        ++var12;
                        if(var12 > 5) {
                            throw var18;
                        }
                    }
                }
            }
                        // 獲取了socket的地址。
            String var19 = var10.address();
                        // 創(chuàng)建了一個(gè)VM.
            VirtualMachine var2;
            try {
                if(var3.length() > 0) {
                    var9 = var3 + File.separator + "bin" + File.separator + var8;
                } else {
                    var9 = var8;
                }

                if(hasWhitespace(var9)) {
                    var9 = var7 + var9 + var7;
                }
                                // 組裝參數(shù): transport , address, suspend
                String var20 = "transport=" + this.transport().name() + ",address=" + var19 + ",suspend=" + (var6?'y':'n');
                if(hasWhitespace(var20)) {
                    var20 = var7 + var20 + var7;
                }
                                // 繼續(xù)組裝參數(shù) 組裝之后的結(jié)構(gòu)類(lèi)似: "java ${jrePath} -Xdebug -Xrunjdwp:transport=dt_socket,address=local:40023,suspend=y"
                var13 = var9 + ' ' + var4 + ' ' + "-Xdebug " + "-Xrunjdwp:" + var20 + ' ' + var5;
                // 核心。 launcher. VM的創(chuàng)建  下面重點(diǎn)分析一下。
                var2 = this.launch(this.tokenizeCommand(var13, var7.charAt(0)), var19, var10, this.transportService());
            } finally {
                this.transportService().stopListening(var10);
            }

            return var2;
        }
    }

this.transportService().startListening(); 綁定了一個(gè)socket 到 SocketTransportService 。 分析如下:

ListenKey startListening(String var1, int var2) throws IOException {
        InetSocketAddress var3;
        if(var1 == null) {
            // 創(chuàng)建InetSocketAddress 里面包含了ip 跟 port , 此處ip為 0.0.0.0 (Returns the InetAddress representing anyLocalAddress)
            var3 = new InetSocketAddress(var2);
        } else {
            var3 = new InetSocketAddress(var1, var2);
        }
                // 創(chuàng)建socket 
        ServerSocket var4 = new ServerSocket();
            // socket 的bind
        var4.bind(var3);
        return new SocketTransportService.SocketListenKey(var4);
    }

this.launch(this.tokenizeCommand(var13, var7.charAt(0)), var19, var10, this.transportService()); 的實(shí)現(xiàn)

protected VirtualMachine launch(String[] var1, String var2, ListenKey var3, TransportService var4) throws IOException, VMStartException {
        AbstractLauncher.Helper var5 = new AbstractLauncher.Helper(var1, var2, var3, var4);
        var5.launchAndAccept(); // socket 執(zhí)行 accept ,connect
        VirtualMachineManager var6 = Bootstrap.virtualMachineManager(); 
        return var6.createVirtualMachine(var5.connection(), var5.process());// 建立一個(gè)virtualMachineManager 
    }

createVirtualMachine 中最核心的就是 創(chuàng)建了一個(gè) VirtualMachineImpl。

VirtualMachineImpl(VirtualMachineManager var1, Connection var2, Process var3, int var4) {
        super((VirtualMachine)null);
        this.vm = this;
        this.vmManager = (VirtualMachineManagerImpl)var1;
        this.process = var3;
        this.sequenceNumber = var4;
        this.threadGroupForJDI = new ThreadGroup(this.vmManager.mainGroupForJDI(), "JDI [" + this.hashCode() + "]");
            // 創(chuàng)建一個(gè)TargetVM, 這個(gè)過(guò)程新建了一個(gè)線(xiàn)程,并且設(shè)置為后臺(tái)常駐。
        this.target = new TargetVM(this, var2);
        
        EventQueueImpl var5 = new EventQueueImpl(this, this.target);
            // 新增一個(gè) event處理器 線(xiàn)程。 
        new InternalEventHandler(this, var5);
        this.eventQueue = new EventQueueImpl(this, this.target);
            // new一個(gè)事件請(qǐng)求管理器
        this.eventRequestManager = new EventRequestManagerImpl(this);
            // 起飛。。。
        this.target.start();

        IDSizes var6;
        try {
            var6 = IDSizes.process(this.vm);
        } catch (JDWPException var8) {
            throw var8.toJDIException();
        }
        // 下面的內(nèi)容可能跟mirror中提到的各種鏡像有關(guān)。。。
        this.sizeofFieldRef = var6.fieldIDSize; // 屬性相關(guān)的鏡像
        this.sizeofMethodRef = var6.methodIDSize;  // 方法相關(guān)的鏡像
        this.sizeofObjectRef = var6.objectIDSize; // 對(duì)象實(shí)例 相關(guān)的鏡像
        this.sizeofClassRef = var6.referenceTypeIDSize;  // 類(lèi)相關(guān)的鏡像
        this.sizeofFrameRef = var6.frameIDSize;   //   StackFrame 鏡像
            
        this.internalEventRequestManager = new EventRequestManagerImpl(this);
        //  createClassPrepareRequest  相關(guān)的event
            ClassPrepareRequest var7 = this.internalEventRequestManager.createClassPrepareRequest();
        var7.setSuspendPolicy(0);
        var7.enable();
            //  createClassUnloadRequest  相關(guān)的event
        ClassUnloadRequest var9 = this.internalEventRequestManager.createClassUnloadRequest();
        var9.setSuspendPolicy(0);
        var9.enable();
        this.notifyInitCompletion();
    }

當(dāng)連接完成后,調(diào)試器與目標(biāo)虛擬機(jī)便可以進(jìn)行雙向通信了。調(diào)試器將用戶(hù)的操作轉(zhuǎn)化為調(diào)試命令,命令通過(guò)連接被發(fā)送到前端運(yùn)行目標(biāo)程序的虛擬機(jī)上;然后,目標(biāo)虛擬機(jī)根據(jù)接受的命令做出相應(yīng)的操作,將調(diào)試的結(jié)果發(fā)回給后端的調(diào)試器;最后,調(diào)試器可視化數(shù)據(jù)信息反饋給用戶(hù)。

再補(bǔ)充個(gè)知識(shí)點(diǎn): 如果采用jdwp agent, 則建立完連接之后,第一件事情就是handshake 看一下代碼。

void handshake(Socket s, long timeout) throws IOException {
        s.setSoTimeout((int)timeout);
 
        byte[] hello = "JDWP-Handshake".getBytes("UTF-8");
        s.getOutputStream().write(hello);
 
        byte[] b = new byte[hello.length];
        int received = 0;
        while (received < hello.length) {
            int n;
            try {
                n = s.getInputStream().read(b, received, hello.length-received);
            } catch (SocketTimeoutException x) {
                throw new IOException("handshake timeout");
            }
            if (n < 0) {
                s.close();
                throw new IOException("handshake failed - connection prematurally closed");
            }
            received += n;
        }
        for (int i=0; i<hello.length; i++) {
            if (b[i] != hello[i]) {
                throw new IOException("handshake failed - unrecognized message from target VM");
            }
        }
 
        // disable read timeout
        s.setSoTimeout(0);

發(fā)送的"JDWP-Handshake"就是JDWP協(xié)議里面規(guī)定的。在連接建立之后,發(fā)送數(shù)據(jù)包之前,debugger跟debuggee必須要有一個(gè)handshake的過(guò)程,handshake分為兩步,

    1. debugger發(fā)送14個(gè)字節(jié),也就是JDWP-Handshake,給debuggee;
    1. debuggee發(fā)送同樣的14個(gè)字節(jié)回應(yīng);

JDWP協(xié)議的細(xì)節(jié)將在另外一篇文章介紹。

3.2 registerEvent

注冊(cè)了2個(gè)event.

 public void registerEvent() {
        // Register ClassPrepareRequest  這個(gè)方法繼承了mirrorImpl, mirror機(jī)制也是我們后面分析的重點(diǎn)。
        eventRequestManager = vm.eventRequestManager();
        // 創(chuàng)建一個(gè)方法進(jìn)入的event
            MethodEntryRequest entryReq = eventRequestManager.createMethodEntryRequest();
        entryReq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
        entryReq.addClassFilter(className);
        entryReq.enable();

            // 創(chuàng)建一個(gè)方法退出的event
        MethodExitRequest exitReq = eventRequestManager.createMethodExitRequest();
        exitReq.addClassFilter(className);
        exitReq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
        exitReq.enable();
    }

3.3 processDebuggeeVM

vm.process(). vm 跑起來(lái)。

3.4 eventLoop

事件循環(huán),具體處理在execute.

private void eventLoop() throws Exception {
        eventQueue = vm.eventQueue();
        while (true) {
            Thread.sleep(10000);
            if (vmExit == true) {
                System.out.println("vmexit");
                break;
            }
            eventSet = eventQueue.remove();
            EventIterator eventIterator = eventSet.eventIterator();
            while (eventIterator.hasNext()) {
                Event event = (Event) eventIterator.next();
                execute(event);
                if (!vmExit) {
                    eventSet.resume();
                }
            }
        }
    }

execute: 目前只是簡(jiǎn)單地輸出了各種event的日志。

private void execute(Event event) throws Exception {
        if (event instanceof VMStartEvent) {
            System.out.println("VM started");
        } else if (event instanceof MethodEntryEvent) {
            Method method = ((MethodEntryEvent) event).method();
            System.out.printf("Enter -> Method: %s, Signature:%s\n",method.name(),method.signature());
            System.out.printf("\t ReturnType:%s\n", method.returnTypeName());
        } else if (event instanceof MethodExitEvent) {
            Method method = ((MethodExitEvent) event).method();
            System.out.printf("Exit -> method: %s\n",method.name());
        } else if (event instanceof VMDisconnectEvent) {
            vmExit = true;
        }
    }

3.5 destroyDebuggeeVM

process.destroy();

參考文獻(xiàn)

1、JPDA 架構(gòu)研究20 - JDI的事件請(qǐng)求和處理模塊
2、https://www.ibm.com/developerworks/cn/java/j-lo-jpda4/index.html?ca=drs-
3、https://yq.aliyun.com/articles/56?hmsr=toutiao.io&spm=5176.100240.searchblog.18&utm_medium=toutiao.io&utm_source=toutiao.io

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,568評(píng)論 19 139
  • 文章來(lái)源:美團(tuán)點(diǎn)評(píng)技術(shù)團(tuán)隊(duì) 作為移動(dòng)開(kāi)發(fā)者,最頭疼的莫過(guò)于遇到產(chǎn)品上線(xiàn)以后出現(xiàn)了bug,但是本地開(kāi)發(fā)環(huán)境又無(wú)法復(fù)現(xiàn)...
    程序農(nóng)猿閱讀 2,059評(píng)論 3 2
  • 我是007-6111號(hào)梁海霞,能與大家一起見(jiàn)證成長(zhǎng)是一件樂(lè)事,更是一件幸事。 目前,本人在一家民企擔(dān)任行政管理工作...
    海藍(lán)藍(lán)_4f9e閱讀 131評(píng)論 0 0
  • 2017.6.28(星期三) 1.重新拾起簡(jiǎn)書(shū),每周更新文章,并下定決心每周至少更新一篇文章,風(fēng)雨無(wú)阻。 2.買(mǎi)了...
    鍋包魚(yú)兒閱讀 278評(píng)論 0 1
  • 無(wú)論任何時(shí)候當(dāng)我回首,你還在,人未老,時(shí)光已過(guò),歲月靜好! 往事是塵封在記憶中的夢(mèng),而你是我唯一鮮明的記憶,那綠葉...
    玲玲d閱讀 910評(píng)論 0 0

友情鏈接更多精彩內(nèi)容