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