Useful message about NullPointerException
Peter Levart
peter.levart at gmail.com
Wed Jan 28 15:59:38 UTC 2015
On 01/28/2015 03:30 AM, David Holmes wrote:
>> If you have access to sources, then perhaps an easier solution would be
>> for stack traces to include column number in addition to line number of
>> location in source that resulted in bytecodes that include the one that
>> triggered the NPE.
>>
>> There is already a RFE for that:
>>
>> https://bugs.openjdk.java.net/browse/JDK-8020204
>
> Once past suggestion has been to include the ByteCode Index (BCI) as
> part of the exception stacktrace information:
>
> https://bugs.openjdk.java.net/browse/JDK-4185378
>
> David
Right,
I checked per-method CharacterRangeTable that gets emitted by javac
-Xjcov option, and unfortunately it is of limited use. The
CharacterRangeTable contains mappings from byte code index ranges
(start-bci, end-bci) -> character ranges (start-line, start-column,
end-line, end-column) of adequate code in source file. The ranges I have
observed have 2 granularities: "statement" and "block". For example, the
following program:
public class CharRangeTest {
String str() {
return "ABC";
}
public static void main(String[] args) {
int i = new CharRangeTest().str().substring(1).length();
System.out.println(i);
}
}
Compiles to the following bytecodes for main method:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #3 // class CharRangeTest
3: dup
4: invokespecial #4 // Method "<init>":()V
7: invokevirtual #5 // Method
str:()Ljava/lang/String;
10: iconst_1
11: invokevirtual #6 // Method
java/lang/String.substring:(I)Ljava/lang/String;
14: invokevirtual #7 // Method
java/lang/String.length:()I
17: istore_1
18: getstatic #8 // Field
java/lang/System.out:Ljava/io/PrintStream;
21: iload_1
22: invokevirtual #9 // Method
java/io/PrintStream.println:(I)V
25: return
LineNumberTable:
line 11: 0
line 12: 18
line 13: 25
CharacterRangeTable:
0, 17, 2c09, 2c41, 1 // 0, 17, 11:09,
11:65, statement
18, 24, 3009, 301f, 1 // 18, 24, 12:09,
12:31, statement
0, 25, 282c, 3406, 2 // 0, 25, 10:44,
13:06, block
CharacterRangeTable says that bytecodes at indexes 0 to 17 map to source
code from (line 11, column 9) to (line 11, column 64) and that this
range is a "statement". Unfortunately, the assignment to i of the result
of the whole chain of invocations is a single Java "statement":
int i = new CharRangeTest().str().substring(1).length();
So if NPE happens anywhere in this statement, we could only pin-point
the statement and not a particular null dereference. The only time this
would give us some additional info is when there are more statements in
the line, like:
x.doSomething(); y.doSomeMore();
...but such code formatting is very rare if non-existent.
If adding column number to StackTraceElement (JDK-8020204) is currently
not an easy thing to do, since it would require javac changes, adding
byte code index (JDK-4185378 ) is trivial:
http://cr.openjdk.java.net/~plevart/jdk9-dev/StackTraceElement.byteCodeIndex/jdk.webrev.01/
http://cr.openjdk.java.net/~plevart/jdk9-dev/StackTraceElement.byteCodeIndex/hotspot.webrev.01/
With this patch, I get the following style of stack-traces:
Exception in thread "main" java.io.FileNotFoundException: /tmp/x (No
such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open[2](FileInputStream.java:195)
at java.io.FileInputStream.<init>[97](FileInputStream.java:138)
at java.io.FileInputStream.<init>[17](FileInputStream.java:93)
at Test.dump[9](Test.java:12)
at Test.main[3](Test.java:19)
The numbers in square brackets are byte code indexes that pin-point the
location in byte code. With the help of javap, one can use this info to
find out the cause of exception even without having access to sources.
Regards, Peter
More information about the core-libs-dev
mailing list