Provide additional contextual information about bytecode-verification errors to ease diagnosis of bytecode or stackmap deficiencies in the field.
Bytecodes are verified by the JVM's bytecode verifier. Bytecodes emitted
by the JDK's
javac compiler generally result in highly conformant bytecode.
However, there are other tools and packages that modify bytecode for
various reasons, including instrumentation and debugging. Occasionally,
post-processing bytecode results in a
VerifyError. Currently, the message
associated with the error is rather terse and non-specific. The purpose
of this feature is to expand the information available when a
occurs in modern classfiles so that a developer can more quickly resolve the
While additional data about errors can be provided, it is not the goal to provide "all possible" data about an error, especially in cases where additional data can be obtained by other means. For instance, while the actual bytecode where the problem occurs may be included in an error message, it may not be disassembled into human-readable form. Additionally the state of the system such as loaded classes and their relationships are not provided as this information is available using other mechanisms.
This extra data will only be available and displayed for errors that are generated by the type-checking verifier. Classfiles with a version less than 50 are verified using the inferencing verifier. Errors generated by the inferencing verifier will not change.
The format of the error message and it's data are not official nor fully specified, and are subject to change in any release.
This project is considered successful if the additional data provided is used by any single engineer to diagnose a real verification error situation. Given the unpredictability of the problem, and the continuing resistance of engineers to allow implanted tracking chips in their brains, there really is no useful metric that can be applied in this case. We have to rely upon common sense and experience in determining what is likely to be useful, which is historically a notoriously hard thing to quantify.
Verification errors are hard to decipher. The current messages attached to VerifyError messages are cryptic and provide very little help in pinpointing where and why the bytecode or stackmap is flawed.
There are two parts to this project. The first is the exposure of a previously internal flag which traces the verification project, and the second an augmentation of the messages contained in a VerifyError with additional contextual information.
The flag which turns on verification tracing was previously an internal global flag which required a change to the source and a recompile of the JVM (in debug mode) to enable. This project turns that flag into the diagnostic flag, 'VerboseVerification'. As such, verification tracing can be turned on (even in product mode) using the command-line flags: "-XX:+UnlockDiagnosticVMOptions -XX:+VerboseVerification"
When run with this flag enabled, the JVM will output tracing information to stdout. This tracing information details which methods are being verified, what mechanism is used for verification, the method's stackmap table. and in debug mode, each instruction's bci, opcode, and current frame state.
In addition, the traditional error message included in thrown VerifyError instance is augmented with the following detailed information: the exact location where the error was detected (class and method name plus bytecode offset); a more detailed error notification which details the origins of any referenced types (if applicable); the current analysis frame state (if applicable); a hex dump of the bytecode; a textual representation of the exception handler table; a textual representation of the stack map table.
The additional error details are formatted into sections and present in a multi-line format in the exception message. The original error message is also unchanged and present at the beginning of the exception message.
One alternative is to not augment with detailed error messages unless a command-line flag is present. This is still a possibility, but since VerifyErrors are rare it is assumed that having extra details by default will not cause any problems.
Existing test suites for the bytecode verifier can be used to validate this feature. Some tests however, might need to be enhanced to expect additional diagnostic information provided by this feature.
Comprehensive testing requires crafting a number of tests (each being a specialized classfile) which triggers a VerifyError in each possible place in the code, as well as creating a variety of tests that exercise the different origins for types (stack/locals/constant pool).
Risks and Assumptions
Very low risk. We do assume that no one is dependent on the existing contents of the VerifyError exception message and thus it is safe for us to augment it without compatibility problems. If this assumption is wrong we may want to investigate the listed alternative.
We expect no impact upon other parts of the platform.