网站界面设计说明,域名备案后怎样做网站,互联网是什么,国外交易平台有哪些scala本地调试在本文中#xff0c;我们将探讨Java / Scala调试器的编写和工作方式。 诸如Windows的WinDbg或Linux / Unix的gdb之类的本机调试器通过操作系统直接提供给它们的钩子来获取其功能#xff0c;以监视和操纵外部进程的状态。 JVM充当OS之上的抽象层#xff0c;它提… scala本地调试 在本文中我们将探讨Java / Scala调试器的编写和工作方式。 诸如Windows的WinDbg或Linux / Unix的gdb之类的本机调试器通过操作系统直接提供给它们的钩子来获取其功能以监视和操纵外部进程的状态。 JVM充当OS之上的抽象层它提供了自己的独立体系结构来调试字节码。 该框架及其API是完全开放的有文档的且可扩展的这意味着您可以轻松编写自己的调试器。 该框架的当前设计由两个主要部分组成-JDWP协议和JVMTI API层。 每种方法都有其各自的优点和用例它们可以最佳地发挥作用。 JDWP协议 Java调试器有线协议通常用于在网络上通过二进制消息在调试器和被调试进程之间传递请求和接收事件例如线程状态或异常的更改。 该体系结构背后的概念是在两者之间建立尽可能多的隔离。 这是为了减少让调试器在运行目标代码时更改其执行的海森堡效应物理学家维尔纳而不是友好的Meth Cooking Walt 。 从目标进程中删除尽可能多的调试器逻辑也可以帮助确保已调试VM状态的更改例如“停止世界” GC或OutOfMemoryErrors不会影响调试器本身。 为了使事情变得更容易JDK附带了JDI Java调试器接口它提供了协议的完整调试器端实现并具有连接分离监视和操纵目标VM的状态的能力。 例如该协议与Eclipse的调试器使用的协议相同。 如果查看在IDE调试时传递给Java进程的命令行参数您会注意到Eclipse传递给它的其他参数-agentlibjdwp 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在字段被修改时暂停执行。 到那时您可以使用其他补充功能例如GetStackTrace和GetThreadInfo来了解有关您在代码中当前位置的更多信息并将其报告。 如下所示的大多数JVMTI函数都使用称为jmethodID和jclass的抽象句柄来引用类和方法如果您曾经编写过Java Native Interface代码则应该很熟悉。 提供了诸如GetMethodName和GetClassSignature之类的附加功能以帮助您从类的常量池中获取实际的符号名称。 然后您可以使用它们以可读格式记录数据或将其呈现在UI中就像我们每天在IDE中看到的那样。 附加调试器 编写调试器库后下一步就是将其附加到JVM。 有几种方法可以做到– 1.连接JDWP 。 如果要编写基于JDWP的调试器则需要以– agentlibjdwp transport dt_socketsuspend yaddress localhostport的形式向调试对象添加启动参数以通过线路启用调试。 这些参数详细说明了调试器和目标在本例中为套接字之间的通信形式以及是否以挂起模式启动调试对象。 2.附加JVMTI库 。 JVM通过传递给debuggee进程并指向您的库在磁盘上的位置的agentpath命令行参数加载JVMTI库。 另一种方法是将代理程序命令行参数附加到全局JAVA_TOOL_OPTIONS环境变量该环境变量将由每个新JVM拾取并且其值会自动附加到其现有参数列表中。 3.远程连接 。 附加调试器的另一种方法是使用远程附加API 。 这个简单而强大的API使您可以将代理附加到正在运行的JVM进程而无需使用任何命令行参数启动它们。 不利的一面是您将无法使用通常需要的某些功能例如can_generate_exception_events 因为这些功能仅在VM启动时才需要-不幸的是调试器有些麻烦了。 您可以下载Takipi的生产调试器以在此处查看其中的一些方法。 参考 Takipi博客上的JCG合作伙伴 Tal Weiss提供了如何编写自己的Java / Scala调试器的信息 。 翻译自: https://www.javacodegeeks.com/2013/09/how-to-write-your-own-java-scala-debugger.htmlscala本地调试