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

Reply via email to