Javaプラットフォームデバッグシステムの原理は何ですか?

PHPz
リリース: 2023-05-21 23:47:46
転載
1420 人が閲覧しました

    1. 原理分析

    (1) はじめに

    JPDA (Java Platform Debugger Architecture) は、Java Platform Debugger Architecture の略称です。 、JPDA が提供する API を使用して、開発者は Java デバッグ アプリケーションを簡単かつ柔軟に構築できます。

    JPDA は主に、Java 仮想マシン ツール インターフェイス (JVMTI)、Java デバッグ ワイヤ プロトコル (JDWP)、および Java デバッグ インターフェイス (JDI) の 3 つの部分で構成されます。

    Java プログラムはすべて Java 仮想マシン上で実行されます。Java プログラムをデバッグしたい場合は、実際に Java 仮想マシンから現在の実行ステータスを要求し、仮想マシンに特定の命令を発行し、設定する必要があります。コールバックなどの場合、Java デバッグ システムは、仮想マシンをデバッグするためのツールとインターフェイスの完全なセットです。

    (2) IDEA と Eclipse のデバッグ原則は

    1: エディターはクライアントおよびサーバー プログラムとして機能し、公開されたリスニング ポートを介してソケット接続を確立します

    2: IDE クライアント ブレークポイント イベントがブレークポイントの場所に作成され、JDI インターフェイスを介してサーバー (プログラム) の VM に渡されます。VM は、suspend を呼び出して VM を一時停止します。

    3: VM が一時停止された後, クライアントが取得する必要がある VM 情報は、クライアントに戻ります。戻った後、VM 再開は実行状態を再開します。

    #4: クライアントは、VM から返された情報を取得した後、それを次の形式で表示できます。さまざまな方法

    (3) アーキテクチャ システム

    JPDA は、比較的独立した 3 つの層から構成される完全で独立したシステムを定義し、それらの間の相互作用モードを規定したり、それらの層のインターフェイスを定義したりします。コミュニケーション。

    3 つのレベルは、低位から高位まで、Java Virtual Machine Tool Interface (JVMTI)、Java Debug Wire Protocol (JDWP)、および Java Debug Interface (JDI) です。

    これら 3 つのモジュールは、デバッグ プロセスをいくつかの自然な概念、つまりデバッガー (デバッガー) とデバッグ対象 (デバッグ対象)、およびそれらの間のコミュニケーターに分解します。

    デバッグ対象は、デバッグする Java 仮想マシン上で実行されます。デバッグ対象は、JVMTI 標準インターフェイスを通じて現在の仮想マシンの情報を監視できます。デバッガは、ユーザーがこれらのインターフェイスを通じて使用できるデバッグ インターフェイスを定義します。ユーザーはデバッグ対象の仮想マシンにデバッグ コマンドを送信でき、デバッガーはデバッグ結果を受け入れて表示します。

    JDWP 通信プロトコルは、デバッグ コマンドとデバッグ結果の送信に使用され、デバッグ中にデバッガとデバッグ対象を接続します。すべてのコマンドは JDWP コマンド パッケージにカプセル化され、トランスポート層を介してデバッグ対象に送信されます。デバッグ対象は JDWP コマンド パッケージを受信すると、コマンドを解析して JVMTI 呼び出しに変換し、デバッグ対象で実行されます。

    同様の状況として、JVMTI が実行結果を JDWP データ パケットの形式に変換し、その結果をデバッガに送信して、JDI 呼び出しに返します。デバッガ開発者は、JDI を通じてデータを取得し、命令を発行します。

    Javaプラットフォームデバッグシステムの原理は何ですか?

    上の図に示すように、JPDA は 3 つの層で構成されています。

    • JVM TI - Java VM ツール インターフェイス。 VM によって提供されるデバッグ サービスを定義します。

    • JDWP - Java デバッグ通信プロトコル。デバッグ対象とデバッガプロセス間の通信を定義します。

    • #JDI - Java デバッグ インターフェイス。ツール開発者がリモート デバッガー アプリケーションを作成するために簡単に使用できる高レベルの Java 言語インターフェイスを定義します。

    JPDA インターフェイスを通じて、独自のデバッグ ツールを開発できます。これらの JPDA が提供するインターフェイスとプロトコルを通じて、デバッガー開発者は、特定の開発者のニーズに応じて Java デバッグ アプリケーションを拡張およびカスタマイズできます。

    前述の IDE デバッグ ツールはすべて JPDA システムに基づいて開発されていますが、唯一の違いは、提供するグラフィカル インターフェイスとカスタム機能が異なる場合があることです。

    さらに、JPDA は一連の標準であり、JDK 実装はこの標準を満たす必要があることに注意してください。したがって、JPDA を通じて開発されたデバッグ ツールは本質的にクロスプラットフォームであり、仮想マシンの実装に依存しません。 JDK のバージョンに依存しないことやその他の移植の利点があるため、ほとんどのデバッグ ツールはこのシステムに基づいています。

    2. リモート デバッグの例

    [1] SpringBoot WEB プロジェクトをビルドします。現在使用している SpringBoot のバージョンは 2.3.0.RELEASE です。対応する Tomcat のバージョンは 9.X です。

    SpringBoot プロジェクトをパッケージ化し、アプリケーション ポートを 9999 に設定します。 JAR パッケージを使用するか Docker を使用するかに関係なく、このプログラムを Linux サーバーにデプロイすることは、リモート デバッグとは何の関係もありません。

    [3] デプロイメント プログラムのコード リファレンスは次のとおりです。これは、単純な要求処理の出力情報です。

    /** 
     *   测试程序
     * @author zhangyu
     * @date 2022/2/17
    */
    @SpringBootApplication
    @RestController
    public class DebuggerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DebuggerApplication.class, args);
        }
    
        @GetMapping("/test")
        public  String test(){
            System.out.println(111);
            System.out.println(222);
            return "OK";
        }
    
    }
    ログイン後にコピー

    [4] デプロイメント プログラムの起動パラメータは次のとおりです。

    java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8888 -jar debugger-0.0.1-SNAPSHOT.jar
    ログイン後にコピー

    ここで、アドレス= 8888 は、リモート デバッグ用のソケット通信ポートとしてポート 8888 を開くことを意味します。

    Javaプラットフォームデバッグシステムの原理は何ですか?

    Tomcat の下にデプロイされた通常の Web プロジェクトの場合は、次を参照してください。

    Tomcat9 バージョン未満

    tomcat 中 bin/catalina.sh 中增加 CATALINA_OPTS=‘-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=18006’

    如下图所示:

    Javaプラットフォームデバッグシステムの原理は何ですか?

    大于等于 tomcat9 版本

    tomcat 中 bin/catalina.sh 中的 JPDA_ADDRESS=“localhost:8000” 这一句中的localhost修改为0.0.0.0(允许所有ip连接到8000端口,而不仅是本地)8000是端口,端口号可以任意修改成没有占用的即可

    如下图所示:

    Javaプラットフォームデバッグシステムの原理は何ですか?

    【5】测试部署的程序正常后,下面构建客户端远程调试,当前以IDEA工具作为客户端

    Javaプラットフォームデバッグシステムの原理は何ですか?

    参考:

    【1】-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888

    【2】Host:远程服务器地址

    【3】Port:远程服务器开放的调试通信端口,非应用端口

    Javaプラットフォームデバッグシステムの原理は何ですか?

    Javaプラットフォームデバッグシステムの原理は何ですか?

    测试接口:http://XXX:9999/test。注意本地代码需要和远程部署程序一致。

    通过上图可以看到客户端设置断点已经生效,其中在客户端执行了一个调试输出,这个是自定义输出的内容服务器程序并没有,在执行后右侧的服务器控制台日志输出了该信息,因此远程Debug是正常通信和处理的。

    (一)调试参数详解

    • -Xdebug:启用调试特性

    • -Xrunjdwp:在目标 VM 中加载 JDWP 实现。它通过传输和 JDWP 协议与独立的调试器应用程序通信。下面介绍一些特定的子选项

    从 Java V5 开始,您可以使用 -agentlib:jdwp 选项,而不是 -Xdebug 和 -Xrunjdwp。如果连接到VM版本低于V5的情况下,只能使用 -Xdebug 和 -Xrunjdwp 选项。下面简单描述 -Xrunjdwp 子选项。

    • -Djava.compiler=NONE: 禁止 JIT 编译器的加载

    • transport : 传输方式,有 socket 和 shared memory 两种,我们通常使用 socket(套接字)传输,但是在 Windows 平台上也可以使用shared memory(共享内存)传输。

    • server(y/n): VM 是否需要作为调试服务器执行

    • address: 调试服务器的端口号,客户端用来连接服务器的端口号

    • suspend(y/n):值是 y 或者 n,若为 y,启动时候自己程序的 VM 将会暂停(挂起),直到客户端进行连接,若为 n,自己程序的 VM 不会挂起

    三、JDI工具代码实践

    (一)JDI技术架构

    Javaプラットフォームデバッグシステムの原理は何ですか?

    (二)实践案例

    (1)被调试程序

    创建一个SpringBoot的WEB项目,提供一个简单的测试接口,并在测试方法中提供一些方法参数变量和局部变量作为后面的调试测试用。

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    @RestController
    public class DebuggerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DebuggerApplication.class, args);
        }
    
        @GetMapping("/test")
        public  String test(String name){
            System.out.println("进入方法");
            int var=100;
            System.out.println(name);
            System.out.println(var);
            System.out.println("方法结束");
            return "OK";
        }
    }
    ログイン後にコピー

    项目启动配置参考,需要启用Debug配置

    Javaプラットフォームデバッグシステムの原理は何ですか?

    (2)自定义调试器代码

    开发调试器需要JNI工具支持,JDI操作的API工具在tools.jar中 ,需要在 CLASSPATH 中添加/lib/tools.jar

    Javaプラットフォームデバッグシステムの原理は何ですか?

    import com.sun.jdi.*;
    import com.sun.jdi.connect.AttachingConnector;
    import com.sun.jdi.connect.Connector;
    import com.sun.jdi.event.*;
    import com.sun.jdi.request.BreakpointRequest;
    import com.sun.jdi.request.EventRequest;
    import com.sun.jdi.request.EventRequestManager;
    import com.sun.tools.jdi.SocketAttachingConnector;
    
    import java.util.List;
    import java.util.Map;
    
    /**
     *  通过JNI工具测试Debug
     * @author zhangyu
     * @date 2022/2/20
    */
    public class TestDebugVirtualMachine {
    
    
        private static VirtualMachine vm;
    
        public static void main(String[] args) throws Exception {
            //获取SocketAttachingConnector,连接其它JVM称之为附加(attach)操作
            VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
            List<AttachingConnector> connectors = vmm.attachingConnectors();
            SocketAttachingConnector sac = null;
            for(AttachingConnector ac : connectors) {
                if(ac instanceof SocketAttachingConnector) {
                    sac = (SocketAttachingConnector) ac;
                }
            }
            assert sac != null;
            //设置好主机地址,端口信息
            Map<String, Connector.Argument> arguments = sac.defaultArguments();
            Connector.Argument hostArg = arguments.get("hostname");
            Connector.Argument portArg = arguments.get("port");
            hostArg.setValue("127.0.0.1");
            portArg.setValue(String.valueOf(8800));
            //进行连接
             vm = sac.attach(arguments);
            //相应的请求调用通过requestManager来完成
            EventRequestManager eventRequestManager = vm.eventRequestManager();
            //创建一个代码判断,因此需要获取相应的类,以及具体的断点位置,即相应的代码行。
            ClassType clazz = (ClassType) vm.classesByName("com.zy.debugger.DebuggerApplication").get(0);
            //设置断点代码位置
            Location location = clazz.locationsOfLine(22).get(0);
            //创建新断点并设置阻塞模式为线程阻塞,即只有当前线程被阻塞
            BreakpointRequest breakpointRequest = eventRequestManager.createBreakpointRequest(location);
            //设置阻塞并启动
            breakpointRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
            breakpointRequest.enable();
            //获取vm的事件队列
            EventQueue eventQueue = vm.eventQueue();
            while(true) {
                //不断地读取事件并处理断点记录事件
                EventSet eventSet = eventQueue.remove();
                EventIterator eventIterator = eventSet.eventIterator();
                while(eventIterator.hasNext()) {
                    Event event = eventIterator.next();
                    execute(event);
                }
                //将相应线程resume,表示继续运行
                eventSet.resume();
            }
        }
    
        /**
         *  处理监听到事件
         * @author zhangyu
         * @date 2022/2/20
        */
        public static void execute(Event event) throws Exception {
            //获取的event为一个抽象的事件记录,可以通过类型判断转型为具体的事件,这里我们转型为BreakpointEvent,即断点记录,
            BreakpointEvent breakpointEvent = (BreakpointEvent) event;
            //并通过断点处的线程拿到线程帧,进而获取相应的变量信息,并打印记录。
            ThreadReference threadReference = breakpointEvent.thread();
            StackFrame stackFrame = threadReference.frame(0);
            List<LocalVariable> localVariables = stackFrame.visibleVariables();
            //输出当前线程栈帧保存的变量数据
            localVariables.forEach(t -> {
                Value value = stackFrame.getValue(t);
                System.out.println("local->" + value.type() + "," + value.getClass() + "," + value);
            });
        }
    
    }
    ログイン後にコピー

    (3)代码分析

    【1】通过Bootstrap.virtualMachineManager();获取连接器,客户端即通过相应的connector进行连接,配置服务器程序ip地址和端口,连接后获取对应服务器的VM信息。

    定位目标debug的类文件,可通过遍历获取的类集合并结合VirtualMachine获取类信息实现

    【3】对目标类代码特定位置设置并启用断点

    【4】记录断点信息,阻塞服务器线程,并根据对应事件获取相应的信息

    【5】执行event.resume释放断点,服务器程序继续运行

    (4)运行测试

    启动 SpringBoot 的 web 项目,也就是服务器程序。启动调试器代码时使用debug模式,并在该位置查看所获取的信息,同时避免直接释放断点。

    Javaプラットフォームデバッグシステムの原理は何ですか?

    【2】DebuggerApplication クラスの 22 行目にブレークポイントの位置を設定します。

    Javaプラットフォームデバッグシステムの原理は何ですか?

    【3】起動後にインターフェースをテストします。サーバー プログラム コンソールに次の結果が出力されることがわかります。 22行目はまだ実行されていません。

    Javaプラットフォームデバッグシステムの原理は何ですか?

    【4】現時点ではデバッガプログラムを監視しています。サーバープログラムのスタックフレームのデータが取得できていることが確認できます

    Javaプラットフォームデバッグシステムの原理は何ですか?

    #[5] ブレークポイントを解除するとサーバーは正常に動作し、リクエスト処理プロセスが完了します。 ##############################

    以上がJavaプラットフォームデバッグシステムの原理は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    関連ラベル:
    ソース:yisu.com
    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
    人気のチュートリアル
    詳細>
    最新のダウンロード
    詳細>
    ウェブエフェクト
    公式サイト
    サイト素材
    フロントエンドテンプレート