如何编写自己的Java / Scala调试器

通过这篇文章,我们将探讨Java / Scala调试器的编写和工作方式。 诸如Windows的WinDbg或Linux / Unix的gdb之类的本机调试器通过操作系统直接提供给它们的钩子来获取其强大功能,以监视和操纵外部进程的状态。 JVM充当OS之上的抽象层,它提供了自己的独立体系结构来调试字节码。 blog_MyDebugger2

该框架及其API是完全开放的,有文档的和可扩展的,这意味着您可以轻松编写自己的调试器。 该框架的当前设计由两个主要部分组成-JDWP协议和JVMTI API层。 每种方法都有其自己的一套优点和最佳用例。

JDWP协议

Java调试器有线协议通常用于在网络上使用二进制消息在调试器和被调试进程之间传递请求和接收事件(例如线程状态或异常的更改)。 该体系结构背后的概念是在两者之间建立尽可能多的隔离。 这是为了减少让调试器在目标代码运行时改变其执行的海森堡效应(物理学家维尔纳,而不是友好的Meth Cooking Walt )。

从目标进程中删除尽可能多的调试器逻辑也有助于确保已调试VM状态的更改(例如“停止世界” GC或OutOfMemoryErrors)不会影响调试器本身。 为了简化操作,JDK附带了JDI (Java调试器接口),该接口提供了协议的完整调试器端实现,并具有连接,分离,监视和操纵目标VM的状态的能力。

例如,该协议与Eclipse的调试器使用的协议相同。 如果查看在IDE调试时传递给Java进程的命令行参数,您会注意到Eclipse传递给它的其他参数(-agentlib:jdwp = transport = dt_socket,…)来启用JVM调试,并且还会建立发送请求和事件的端口。

JVMTI API

现代JVM调试器体系结构中的第二个关键组件是一组本机API,涵盖了与JVM操作相关的广泛领域,称为JVM工具接口 (即JVMTI)。 与JDWP不同,JVMTI被设计为一组C / C ++ API,并且具有JVM动态加载利用API提供的命令的预编译库(例如.dll或.so)的机制。

这种方法与JDWP的不同之处在于,它实际上在目标进程内部执行调试器。 这增加了调试器在性能和稳定性方面影响应用程序代码的可能性。 但是,关键优势在于能够以近乎实时的方式直接与JVM交互。

由于JVMTI提供了一组功能强大的低级API集,所以我认为有必要进一步深入研究并解释其工作原理,以及可以使用它进行哪些出色的工作。 可通过JDK随附的jvmti.h获得API标头。

编写调试器库

编写自己的调试器需要使用C ++创建本机OS库。 在这种情况下,您的“主要”功能看起来像–

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void*)

当您的调试器代理由JVM加载时,该函数将由JVM调用。 传递给您的日益重要的JavaVM指针将为您提供与JVM对话所需的一切。 它引入了JavaVM :: GetEnv方法中可用的jvmtiEnv类,该类使您能够通过功能和事件的概念与JVMTI层进行交互。

JVMTI功能

编写调试器的关键方面之一是要特别注意调试器代码对目标进程的影响。 这对于本机调试器库尤其重要,在该库中,代码与应用程序非常紧密地运行。 为了帮助您更好地控制调试器如何影响代码的执行,JVMTI规范引入了功能概念。

在编写调试器时,您可以提前告诉JVM您打算使用哪些API命令或事件集(即设置断点,挂起线程等)。 这使JVM可以提前为此做准备,并使您可以更好地控制调试器的运行时开销。 这种方法还使来自不同供应商的JVM能够以编程方式告诉您整个JVMTI规范中当前支持哪些API命令。

并非所有能力都是平等的 。 某些功能的性能开销相对较小。 其他有趣的,如can_generate_exception_events接收回调时异常的代码抛出,或用于接收回调时获取锁定can_generate_monitor_events,付出更高的成本。 原因是它们阻止JVM在JIT编译期间完全优化代码,并可能迫使JVM在运行时进入解释模式。

其他功能,例如can_generate_field_modification_events用于在设置目标对象字段(即设置监视)时接收通知,其成本甚至更高,从而使代码执行速度大大降低。 即使JVM支持同时加载多个本机库,HotSpot中的某些功能(例如can_suspend用于挂起和恢复线程)也只能一次声明一个库。

构建Takipi的生产调试器时,我们面临的最困难的部分之一就是提供类似的功能,而又不会产生这种开销(在以后的文章中会介绍更多)。

设置回调 。 收到一组功能后,下一步就是设置回调,JVM将调用这些回调以让您知道实际发生的时间。 这些回调中的每一个都会提供有关发生的事件的相当深的信息。 例如,对于异常回调,此信息将包括引发异常的字节码位置,线程,异常对象以及是否以及将在何处捕获该异常。

void JNICALL ExceptionCallback(jvmtiEnv *jvmti,JNIEnv *jni, jthread thread, jmethodID method,jlocation location, jobject exception,jmethodID catch_method, jlocation catch_location)

重要的是要注意,功能的开销有时分为两部分。 第一部分仅是通过启用它来完成的,因为这将导致JIT编译器以不同的方式编译事物,从而产生对代码进行调用的潜力。 第二部分是在您实际安装回调函数时出现的,因为它会使JVM在运行时选择优化程度较低的执行路径,通过该路径它可以调用您的代码,并带来解析和传递的额外开销。您有意义的数据。

断点和手表 。 您的调试器可以提供在运行时检查特定状态的熟悉功能,例如SetBreakpoint可以通知JVM暂停执行特定字节代码指令,或者SetFieldModificationWatch可以在修改字段时暂停执行。 到那时,您可以使用其他补充功能,例如GetStackTraceGetThreadInfo来了解有关您当前在代码中的位置的更多信息并将其报告。

如下所示的大多数JVMTI函数都使用称为jmethodID和jclass的抽象句柄来引用类和方法(如果您曾经编写过Java Native Interface代码,则应该很熟悉)。 提供了诸如GetMethodNameGetClassSignature之类的附加功能,以帮助您从类的常量池中获取实际的符号名称。 然后,您可以使用它们以可读格式记录数据或将其呈现在UI中,就像我们每天在IDE中看到的那样。

附加调试器

编写调试器库后,下一步就是将其附加到JVM。 有几种方法可以做到–

1.连接JDWP 。 如果要编写基于JDWP的调试器,则需要以– agentlib:jdwp = transport = dt_socket,suspend = y,address = localhost:<port>的形式向调试对象添加启动参数以通过线路启用调试。 这些参数详细说明了调试器和目标(在本例中为套接字)之间的通信形式,以及是否以挂起模式启动调试对象。

2.附加JVMTI库 。 JVM通过传递给debuggee进程并指向您的库在磁盘上的位置的agentpath命令行参数加载JVMTI库。

另一种方法是将代理程序命令行参数附加到全局JAVA_TOOL_OPTIONS环境变量,该环境变量将由每个新JVM拾取,并且其值会自动附加到其现有参数列表中。

3.远程连接 。 附加调试器的另一种方法是使用远程附加API 。 这个简单而强大的API使您可以将代理附加到正在运行的JVM进程,而无需使用任何命令行参数启动它们。 不利的一面是您将无法使用通常需要的某些功能,例如can_generate_exception_events ,因为这些功能仅在VM启动时才需要-遗憾的是,调试器有些麻烦了。

您可以下载Takipi的生产调试器,以在此处查看其中的一些方法。

参考: 如何从我们的JCG合作伙伴 Tal Weiss在Takipi博客上编写自己的Java / Scala调试器 。

翻译自: https://www.javacodegeeks.com/2013/09/how-to-write-your-own-java-scala-debugger.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367868.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试bug文档模板,软件bug测试记录模板

软件bug测试记录模板 XXX软件bug测试记录表 文档编号&#xff1a; 背景信息 项目名称 测试目的 硬件环境 软件环境 测试时间 测试人员 测试说明 1、严重等级&#xff1a; A-Crash(崩溃的)&#xff1a;由于程序所引起的死机、非法退出、死循环&#xff1b;数据库发生死锁&#x…

js模版引擎handlebars.js实用教程——结束语

返回目录 有了这些功能&#xff0c;[ajax json Handlebars]替代[vo el表达式]不成问题&#xff0c;新时代的曙光已经来临&#xff0c;最佳解决方案在此&#xff0c;您还等什么&#xff1f; 教程到此结束。。。祝读者学习愉快。。。 转载请注明&#xff1a;前端录js模版引擎hand…

rem根据屏幕大小适配字体大小

(function(window, document) {function rootSize() {var screenWidth document.documentElement.clientWidth || window.innerWidth;// console.log(screenWidth);var rootSize screenWidth > 750 ? 100 :screenWidth < 320 ? 320 / 750 * 100 : screenWidth…

mac mysql 列表_Mac终端操作Mysql,以及Mysql的操作方法详解

首先手动启动Mysql应用程序打开终端输入如下命令&#xff1a; /usr/local/mysql/bin/mysql -u root -p(注意&#xff1a;Windows下的是&#xff1a; mysql -u root -p)其中root为用户名。这时会出现如下命令&#xff1a;Enter password: 123456这样就可以访问你的数据库服务…

Java即时编译:不仅仅是一个流行词

最近的Java生产性能问题迫使我重新审视并真正欣赏Java VM即时&#xff08;JIT&#xff09;编译器。 大多数Java开发人员和支持人员都听说过这种JVM运行时性能优化&#xff0c;但是有多少人真正理解并欣赏它的好处&#xff1f; 本文将与您分享在添加新的虚拟服务器&#xff08…

php ajax实现分页效果

ajaxpage.php【这里是数据展示页面的代码】: <meta charset’utf-8′> <script src” http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js ” type”text/javascript”/></script> <script type”text/javascript”> //获取当前url地址…

常用的JVM调优参数总结汇总【随时查阅学习】

本文章参数根据后期用的参数会持续更新 --- &#xff08;1&#xff09;-Xms20M 表示设置JVM启动内存的最小值为20M&#xff0c;必须以M为单位 &#xff08;2&#xff09;-Xmx20M 表示设置JVM启动内存的最大值为20M&#xff0c;必须以M为单位。将-Xmx和-Xms设置为一样可以避免J…

win10设置计算机关机时间,教你windows10电脑怎么设置定时关机

今天小编教你windows10电脑怎么设置定时关机&#xff0c;相信大家都有过这样的经历吧!那就是有时开着电脑玩累了,躺在床上睡了一会&#xff0c;结果醒来的时候发现睡了比较久&#xff0c;且还不愿意下床关电脑怎么办&#xff0c;其实我们可设置定时关机&#xff0c;有需要的用户…

Android内存优化9 内存检测工具3 MAT比Menmery Monitor更强大

在Android性能优化第&#xff08;一&#xff09;篇---基本概念中讲了JAVA的四大引用&#xff0c;讲了一下GCRoot&#xff0c;第二篇Memory Monitor检测内存泄露仅仅说了Menmery Monitor的使用&#xff0c;这篇博客谈一下MAT来寻找内存泄露&#xff0c;相对来说&#xff0c;Memo…

使用Java使用Amazon Simple Queue Service

Amazon Simple Queue Service或SQS是Amazon Webservice堆栈提供的高度可扩展的托管消息队列。 Amazon SQS可用于完全解耦系统中不同组件的操作&#xff0c;这些组件否则将交换数据以执行独立的任务。 Amazon SQS还可以帮助我们保存在应用程序关闭或组件之一不可用时丢失的数据。…

计算机三种引用方式,单元格的引用方式有哪几种

EXCEL单元格的引用包括绝对引用、相对引用和混合引用三种。单元格引用是Excel中的术语&#xff0c;指用单元格在表中的坐标位置的标识。是计算机基础的重要学习内容。1、绝对引用&#xff1a;单元格中的绝对单元格引用(例如$F$6)总是在指定位置引用单元格F6。如果公式所在单元格…

HTML5标签用法及描述

2000 年底&#xff0c;国际 W 3C 织组织公布发行了 X HTML 1.0 版本&#xff0c;到现在已经有 10 年了。这 10 年里&#xff0c; Web 飞速发展&#xff0c;X HTML 1.0 显得“力不从心”&#xff0c;已经跟不上时代的发展了&#xff0c;于是 HTML 5 孕育而生。 W 3C 在 201…

什么是作用域链?

1、简单说就是作用域集合 当前作用域 -> 父级作用域 -> ... -> 全局作用域 形成的作用域链条全局作用域的变量和方法都可以进行调用局部的变量和方法只能局部进行调用( 除闭包外 )局部可以访问全局的变量和方法转载于:https://www.cnblogs.com/wangfencs/p/9446293…

2018 国内 DevOps 趋势分析与实践分享

2017 年年末&#xff0c;《中国第一份 DevOps 年度调查报告》发布&#xff0c;这对国内一直摸着石头过河的 DevOps 先行者和 DevOps 在中国的发展都有着里程碑式的意义。它使国内的 DevOps 先行实践者们既能够对自身的 DevOps 实践有明确定位&#xff0c;又为踌躇不前的观望者们…

linux上php指向mysql_linux环境下 php如何配置mysql

展开全部Linux下配置安装PHP环境参考别人的做法,遇到问题上网查,下面就是安装步骤.一、安装Apache2.2.221、到官网下636f707962616964757a686964616f31333339666133载 http://httpd.apache.org/download.cgi2、解压tar -zxvf httpd-2.2.22.tar.gz3、建立目标文件夹(注意以下所有…

Java 7 Swing:创建半透明和成形的Windows

Java 7 Swing支持具有透明和非矩形形状的窗口。 以下屏幕截图显示了创建的不透明度为75&#xff05;的圆形窗口。 您可以通过在JFrame上使用setOpacity方法更改其不透明度来创建半透明窗口。 请注意&#xff0c;只有底层操作系统支持时&#xff0c;您才能创建半透明窗口。 另外…

ajax用post方法,jquery中get,post和ajax方法的使用小结

在JQuery中可以使用get&#xff0c;post和ajax方法给服务器端传递数据get方法的使用(customForGet.js文件)&#xff1a;function verify(){//1.获取文本框的数据//通过DOM的方式获取//document.getElementByIdx("userName");//通过JQuery的方式获取var jqueryObj $(…

vim使用手册

https://www.cnblogs.com/lijia0511/p/5644566.html转载于:https://www.cnblogs.com/kusy/p/9450311.html

JAVASCRIPT常用20种小技巧汇总

1.TEXTAREA自适应文字行数的多少 2.脚本永不出错<script LANGUAGE"javascript"></script>3.ENTER键可以让光标移到下一个输入框4.预定秒数内自动转到指定网址5.怎么改变滚动条的颜色&#xff0c;只有ie5.5版本以上才能支持。 这是使用CSS语言&#xff0c…

JavaWeb之动态页面技术JSP/EL/JSTL

一、JSP技术 1&#xff0e;jsp脚本和注释 jsp脚本&#xff1a; 1&#xff09;<%java代码%> ----- 内部的java代码翻译到service方法的内部 2&#xff09;<%java变量或表达式> ----- 会被翻译成service方法内部out.print() 3&#xff09;<%!java代码%> ---- 会…