JEP 136:增强的验证错误
概述
提供有关字节码验证错误的额外上下文信息,以帮助诊断现场中的字节码或堆栈映射缺陷。
目标
字节码由 JVM 的字节码验证器进行验证。JDK 的 javac
编译器生成的字节码通常会产生高度合规的字节码。然而,还有一些其他工具和包出于各种原因(包括检测和调试)修改字节码。有时,后处理字节码会导致 VerifyError
错误。目前,与此错误相关的信息较为简略且不具体。此功能的目的是在现代类文件中发生 VerifyError
时,扩展可用的信息,以便开发者能够更快地解决问题。
非目标
虽然可以提供有关错误的更多数据,但目的并非提供关于错误的“所有可能”数据,特别是在可以通过其他方式获取附加数据的情况下。例如,虽然问题实际发生的字节码可能会包含在错误消息中,但它可能不会被反汇编成人类可读的形式。此外,系统的状态(例如已加载的类及其关系)也不会提供,因为这些信息可以通过其他机制获得。
此额外数据仅适用于类型检查验证程序生成的错误,并且仅针对这些错误进行显示。版本低于 50 的类文件将使用推理验证程序进行验证。推理验证程序生成的错误不会发生变化。
错误消息及其数据的格式并非官方指定,也未完全规范,并且可能在任何版本中发生变化。
成功指标
如果任何一位工程师使用所提供的额外数据来诊断一个真实的验证错误情况,该项目便被视为成功。鉴于问题的不可预测性,以及工程师们持续抵制在大脑中植入追踪芯片,因此在此情况下确实没有任何有用的指标可以应用。我们必须依靠常识和经验来判断哪些内容可能有用,而从历史上看,这一直是一个难以量化的方面。
动机
验证错误很难解读。当前附加到 VerifyError
消息中的信息非常晦涩,对于确定字节码或堆栈映射在何处以及为何存在缺陷提供的帮助极少。
描述
该项目分为两部分。第一部分是公开之前内部使用的跟踪验证项目的标志,第二部分是通过附加的上下文信息来增强 VerifyError 中包含的消息。
用于开启验证跟踪的标志之前是一个内部全局标志,需要修改源代码并重新编译 JVM(在调试模式下)才能启用。本项目将该标志转变为诊断标志 'VerboseVerification'
。因此,可以通过命令行标志 "-XX:+UnlockDiagnosticVMOptions -XX:+VerboseVerification"
来开启验证跟踪(即使在生产模式下也可使用)。
当使用此标志运行时,JVM 会将跟踪信息输出到标准输出(stdout)。这些跟踪信息详细说明了哪些方法正在被验证、使用了何种验证机制、方法的栈映射表(stackmap table),以及在调试模式下,每条指令的字节码索引(bci)、操作码(opcode)和当前帧状态。
此外,抛出的 VerifyError
实例中包含的传统错误消息会增加以下详细信息:检测到错误的确切位置(类和方法名称加上字节码偏移量);更详细的错误通知,详细说明任何引用类型的来源(如适用);当前分析帧状态(如适用);字节码的十六进制转储;异常处理程序表的文本表示;栈映射表的文本表示。
附加的错误详细信息被格式化为多个部分,并以多行格式呈现在异常消息中。原始错误消息也保持不变,并出现在异常消息的开头。
替代方案
一种替代方法是,除非存在命令行标志,否则不增加详细的错误消息。这仍然是一个可能性,但由于 VerifyErrors 很少见,因此默认情况下假定拥有更多细节不会导致任何问题。
测试
现有的字节码验证器测试套件可以用来验证此功能。然而,某些测试可能需要增强,以期望获得此功能提供的额外诊断信息。
全面的测试需要设计多个测试用例(每个测试用例是一个专门的类文件),这些测试用例会在代码中所有可能的位置触发 VerifyError
,同时还需要创建各种测试用例来检验类型的不同来源(栈/局部变量/常量池)。
风险与假设
非常低的风险。我们假设没有人依赖 VerifyError
异常消息的现有内容,因此我们可以安全地对其进行扩展而不会产生兼容性问题。如果这个假设是错误的,我们可能需要研究列出的替代方案。
依赖
无依赖项
影响
我们预计对平台的其他部分没有影响。