JPDA (Java Platform Debugger Architecture) ialah singkatan kepada Java Platform Debugger Architecture , melalui API yang disediakan oleh JPDA, pembangun boleh membina aplikasi penyahpepijatan Java dengan mudah dan fleksibel.
JPDA terutamanya terdiri daripada tiga bahagian: Antara Muka Alat Mesin Maya Maya (JVMTI), Protokol Wayar Nyahpepijat Java (JDWP) dan Antara Muka Nyahpepijat Java (JDI).
Semua program Java dijalankan pada mesin maya Java Jika kita ingin menyahpepijat program Java, kita sebenarnya perlu meminta status berjalan semasa daripada mesin maya Java, mengeluarkan arahan tertentu kepada mesin maya dan menetapkan. beberapa Panggilan Balik, dsb., maka sistem penyahpepijatan Java ialah set lengkap alat dan antara muka untuk penyahpepijatan mesin maya.
1: Editor bertindak sebagai klien dan program pelayan mewujudkan sambungan soket melalui port pendengaran terdedah
2: Klien IDE Peristiwa titik putus dibuat di lokasi titik putus dan dihantar ke VM pelayan (program) melalui antara muka JDI VM memanggil penggantungan untuk menggantung VM
3: Selepas VM digantung. , maklumat VM yang perlu diperolehi oleh pelanggan ialah Kembali kepada pelanggan Selepas kembali, resume VM menyambung semula keadaan berjalannya
4: Selepas pelanggan memperoleh maklumat yang dikembalikan oleh VM, ia boleh memaparkannya dalam. cara yang berbeza
JPDA mentakrifkan sistem yang lengkap dan bebas, yang terdiri daripada tiga lapisan yang agak bebas, dan menetapkan mod interaksi antara mereka, atau mentakrifkan antara muka untuk mereka komunikasi.
Tiga peringkat dari rendah ke tinggi ialah Java Virtual Machine Tool Interface (JVMTI), Java Debug Wire Protocol (JDWP) dan Java Debug Interface (JDI).
Tiga modul ini memecahkan proses penyahpepijatan kepada beberapa konsep semula jadi: penyahpepijat (debugger) dan penyahpepijat (debuggee), serta komunikator di antara mereka.
Penyahpepijat berjalan pada mesin maya Java yang ingin kami nyahpepijat Ia boleh memantau maklumat mesin maya semasa melalui antara muka standard JVMTI mentakrifkan antara muka penyahpepijatan yang boleh digunakan oleh pengguna, melalui antara muka ini , pengguna boleh menghantar arahan nyahpepijat ke mesin maya yang dinyahpepijat, dan penyahpepijat menerima serta memaparkan hasil penyahpepijatan.
Protokol komunikasi JDWP digunakan untuk menghantar arahan penyahpepijatan dan hasil penyahpepijatan Ia menghubungkan penyahpepijat dan penyahpepijat semasa penyahpepijatan. Semua arahan dirangkumkan ke dalam pakej arahan JDWP dan dihantar kepada nyahpepijat melalui lapisan pengangkutan Selepas penyahpepijat menerima pakej arahan JDWP, ia menghuraikan arahan dan menukarkannya kepada panggilan JVMTI, yang dijalankan pada penyahpepijat.
Begitu juga, JVMTI menukar hasil yang sedang dijalankan ke dalam bentuk paket data JDWP, menghantar keputusan kepada penyahpepijat dan kembali ke panggilan JDI. Pembangun penyahpepijat mendapatkan data dan mengeluarkan arahan melalui JDI.
Seperti yang ditunjukkan dalam rajah di atas, JPDA terdiri daripada tiga lapisan:
JVM TI
- Antara muka alat Java VM. Mentakrifkan perkhidmatan penyahpepijatan yang disediakan oleh VM.
JDWP
- Protokol komunikasi penyahpepijatan Java. Mentakrifkan komunikasi antara penyahpepijat dan proses penyahpepijat.
JDI
- Antara muka penyahpepijatan Java. Mentakrifkan antara muka bahasa Java peringkat tinggi yang boleh digunakan oleh pembangun alat untuk menulis aplikasi penyahpepijat jauh.
Melalui antara muka JPDA, kami boleh membangunkan alatan nyahpepijat kami sendiri. Melalui antara muka dan protokol yang disediakan oleh JPDA ini, pembangun penyahpepijat boleh melanjutkan dan menyesuaikan aplikasi penyahpepijatan Java mengikut keperluan pembangun tertentu.
Alat penyahpepijatan IDE yang kami nyatakan sebelum ini semuanya dibangunkan berdasarkan sistem JPDA Satu-satunya perbezaan ialah ia mungkin menyediakan antara muka grafik yang berbeza dan mempunyai beberapa fungsi tersuai yang berbeza.
Selain itu, kita harus ambil perhatian bahawa JPDA ialah satu set piawaian dan mana-mana pelaksanaan JDK mesti memenuhi piawaian ini Oleh itu, alat penyahpepijatan yang dibangunkan melalui JPDA sememangnya merentas platform dan tidak bergantung pada pelaksanaan mesin maya. Versi JDK bebas dan kelebihan port lain, jadi kebanyakan alat penyahpepijatan adalah berdasarkan sistem ini.
[1] Bina projek WEB SpringBoot. Versi SpringBoot yang sedang kami gunakan ialah 2.3.0.RELEASE. Versi tomcat yang sepadan ialah 9.X.
Bungkus projek SpringBoot dan tetapkan port aplikasi kepada 9999. Menggunakan program ini ke pelayan Linux, sama ada menggunakan pakej JAR atau Docker, tiada kaitan dengan penyahpepijatan jauh.
【3】Rujukan kod atur cara penempatan adalah seperti berikut, iaitu permintaan mudah memproses maklumat cetakan
/** * 测试程序 * @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】Parameter permulaan program penempatan adalah seperti berikut
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8888 -jar debugger-0.0.1-SNAPSHOT.jar
di mana alamat= 8888 bermaksud membuka port 8888 sebagai port komunikasi Soket untuk penyahpepijatan jauh
Jika ia adalah projek web biasa yang digunakan di bawah tomcat, rujukannya adalah seperti berikut:
Kurang daripada versi tomcat9
tomcat 中 bin/catalina.sh 中增加 CATALINA_OPTS=‘-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=18006’
如下图所示:
大于等于 tomcat9 版本
tomcat 中 bin/catalina.sh 中的 JPDA_ADDRESS=“localhost:8000” 这一句中的localhost修改为0.0.0.0(允许所有ip连接到8000端口,而不仅是本地)8000是端口,端口号可以任意修改成没有占用的即可
如下图所示:
【5】测试部署的程序正常后,下面构建客户端远程调试,当前以IDEA工具作为客户端
参考:
【1】-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888
【2】Host:远程服务器地址
【3】Port:远程服务器开放的调试通信端口,非应用端口
测试接口: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 不会挂起
(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配置
(2)自定义调试器代码
开发调试器需要JNI工具支持,JDI操作的API工具在tools.jar中 ,需要在 CLASSPATH 中添加/lib/tools.jar
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模式,并在该位置查看所获取的信息,同时避免直接释放断点。
【2】Tetapkan lokasi titik putus kepada baris 22 kelas DebuggerApplication
【3】Uji antara muka selepas permulaan. Anda boleh mendapati bahawa konsol program pelayan mencetak hasil berikut. Baris 22 belum dilaksanakan lagi.
【4】Pada masa ini, program penyahpepijat sedang diperhatikan. Anda boleh melihat bahawa data rangka tindanan program pelayan telah diperolehi
[5] Lepaskan titik putus, dan pelayan akan berjalan seperti biasa dan menyelesaikan proses pemprosesan permintaan
Atas ialah kandungan terperinci Apakah prinsip sistem penyahpepijatan platform Java?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!