ScopeDescs incorrect in Java 8

David Griffiths dgriffiths at undo.io
Thu Oct 8 14:11:36 UTC 2020


Hi, I am writing some code which makes use of ScopeDesc info in a live JVM
and am seeing what looks like a bug in Java 8. I realise this is now
ancient technology but wondered if anybody could just point me in vaguely
the right direction to try and find the cause. The bug does not exist in
Java 11 onwards so I think it must have been fixed. In a nutshell, I'm
finding that the mere presence of a particular line at the end of a method
completely screws the ScopeDescs for the complete method - the compiled
code no longer lines up with the associated ScopeDescs.

I'm running with the options "-XX:-Inline -XX:TieredStopAtLevel=1" (there
is a reason for this). The method (called enough times to JIT compile) is:

 16     static void topMethod(int a, int b) {
 17         int c = a + 1;
 18         int d = iMethodA(a) + iMethodB(a);
 19         int e = iMethodA(a) * (iMethodA(b) - iMethodB(b));
 20         vMethod(a);
 21         testArgs(7.0);
 22     }

Simply changing the last line to testArgs(a) results in the ScopeDescs
being ok! The called methods are trivial one liners. This is the bad
compile info (nops and blank lines removed):

[Verified Entry Point]
0x00007f24675adb20:    mov [rsp-0x14000], eax
0x00007f24675adb27:    push rbp
0x00007f24675adb28:    sub rsp, 0x50
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 5, line = 18
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ([3], illegal) ([4], illegal)
0x00007f24675adb2c:    mov [rsp+0x24], edx
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 9, line = 18
0x00007f24675adb30:    mov rdi, rsi
0x00007f24675adb33:    inc edi
0x00007f24675adb35:    mov rbx, rsi
0x00007f24675adb38:    mov rsi, rbx
0x00007f24675adb3b:    mov [rsp+0x28], edi
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 9, line = 18
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ([3], illegal) ([4], illegal)
            expressions ([0], stack[44], normal)
0x00007f24675adb3f:    mov [rsp+0x20], ebx
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 15, line = 19
0x00007f24675adb47:    call 0x00007F24675AD520
0x00007f24675adb4c:    mov esi, [rsp+0x20]
0x00007f24675adb50:    mov [rsp+0x2C], eax
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 15, line = 19
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ([4], illegal)
0x00007f24675adb57:    call 0x00007F24675AD7E0
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 19, line = 19
0x00007f24675adb5c:    mov edi, [rsp+0x2C]
0x00007f24675adb60:    add edi, eax
0x00007f24675adb62:    mov esi, [rsp+0x20]
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 19, line = 19
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ([4], illegal)
            expressions ([0], stack[52], normal)
0x00007f24675adb66:    mov [rsp+0x30], edi
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 23, line = 19
0x00007f24675adb6f:    call 0x00007F24675AD520
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 23, line = 19
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ([4], illegal)
            expressions ([0], stack[52], normal) ([1], stack[56], normal)
0x00007f24675adb74:    mov esi, [rsp+0x24]
0x00007f24675adb78:    mov [rsp+0x34], eax
0x00007f24675adb7f:    call 0x00007F24675AD520
0x00007f24675adb84:    mov esi, [rsp+0x24]
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 31, line = 20
0x00007f24675adb88:    mov [rsp+0x38], eax
0x00007f24675adb8f:    call 0x00007F24675AD7E0
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 31, line = 20
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ('e', stack[60], normal)
0x00007f24675adb94:    mov esi, [rsp+0x38]
0x00007f24675adb98:    sub esi, eax
0x00007f24675adb9a:    mov eax, [rsp+0x34]
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 37, line = 21
0x00007f24675adb9e:    imul eax, esi
0x00007f24675adba1:    mov esi, [rsp+0x20]
            static void topMethod(int, int) @0x00007f246204c790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 37, line = 21
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ('e', stack[60], normal)
0x00007f24675adba5:    mov [rsp+0x3C], eax
0x00007f24675adbaf:    call 0x00007F24675AE080
0x00007f24675adbb4:    vmovsd xmm0, qword ptr [0x00007F24675ADB00]
0x00007f24675adbbf:    call 0x00007F24675AE4C0
0x00007f24675adbc4:    add rsp, 0x50
0x00007f24675adbc8:    pop rbp
0x00007f24675adbc9:    test [0x00007F247EDB0100], eax
0x00007f24675adbcf:    ret

Note for instance the subtraction which incorrectly appears between
descriptors for lines 20 and 21. The local variable info is also incorrect
with the value of "d" for instance supposedly available on the stack whilst
it is still being computed. By contrast this is what the info looks like
with "a" instead of "7.0":

[Verified Entry Point]
0x00007f1fc910ea40:    mov [rsp-0x14000], eax
0x00007f1fc910ea47:    push rbp
0x00007f1fc910ea48:    sub rsp, 0x50
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 0, line = 17
0x00007f1fc910ea4c:    mov [rsp+0x20], esi
0x00007f1fc910ea50:    mov [rsp+0x24], edx
0x00007f1fc910ea54:    mov rdi, rsi
0x00007f1fc910ea57:    inc edi
0x00007f1fc910ea59:    mov rbx, rsi
0x00007f1fc910ea5c:    mov rsi, rbx
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 5, line = 18
0x00007f1fc910ea5f:    mov [rsp+0x28], edi
0x00007f1fc910ea67:    call 0x00007F1FC910E460
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 5, line = 18
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ([3], illegal) ([4], illegal)
0x00007f1fc910ea6c:    mov esi, [rsp+0x20]
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 9, line = 18
0x00007f1fc910ea70:    mov [rsp+0x2C], eax
0x00007f1fc910ea77:    call 0x00007F1FC910E720
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 9, line = 18
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ([3], illegal) ([4], illegal)
            expressions ([0], stack[44], normal)
0x00007f1fc910ea7c:    mov edi, [rsp+0x2C]
0x00007f1fc910ea80:    add edi, eax
0x00007f1fc910ea82:    mov esi, [rsp+0x20]
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 15, line = 19
0x00007f1fc910ea86:    mov [rsp+0x30], edi
0x00007f1fc910ea8f:    call 0x00007F1FC910E460
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 15, line = 19
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ([4], illegal)
0x00007f1fc910ea94:    mov esi, [rsp+0x24]
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 19, line = 19
0x00007f1fc910ea98:    mov [rsp+0x34], eax
0x00007f1fc910ea9f:    call 0x00007F1FC910E460
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 19, line = 19
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ([4], illegal)
            expressions ([0], stack[52], normal)
0x00007f1fc910eaa4:    mov esi, [rsp+0x24]
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 23, line = 19
0x00007f1fc910eaa8:    mov [rsp+0x38], eax
0x00007f1fc910eaaf:    call 0x00007F1FC910E720
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 23, line = 19
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ([4], illegal)
            expressions ([0], stack[52], normal) ([1], stack[56], normal)
0x00007f1fc910eab4:    mov esi, [rsp+0x38]
0x00007f1fc910eab8:    sub esi, eax
0x00007f1fc910eaba:    mov eax, [rsp+0x34]
0x00007f1fc910eabe:    imul eax, esi
0x00007f1fc910eac1:    mov esi, [rsp+0x20]
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 31, line = 20
0x00007f1fc910eac5:    mov [rsp+0x3C], eax
0x00007f1fc910eacf:    call 0x00007F1FC910EF80
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 31, line = 20
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ('e', stack[60], normal)
0x00007f1fc910ead4:    mov esi, [rsp+0x20]
0x00007f1fc910ead8:    vcvtsi2sd xmm0, xmm0, esi
0x00007f1fc910eadf:    call 0x00007F1FC910F3C0
            static void topMethod(int, int) @0x00007f1fc4553790 of public
class io.undo.test.GetStateTest @0x0000000100060028 @ bci = 36, line = 21
            locals ('a', stack[32], normal) ('b', stack[36], normal) ('c',
stack[40], normal) ('d', stack[48], normal) ('e', stack[60], normal)
0x00007f1fc910eae4:    add rsp, 0x50
0x00007f1fc910eae8:    pop rbp
0x00007f1fc910eae9:    test [0x00007F1FE124F100], eax
0x00007f1fc910eaef:    ret

As you can see the scope info is completely different.

I'm just looking to understand what changed after Java 8 to fix this? Any
pointers, no matter how vague, would be helpful!

PS: this is for our java reverse debugger -
https://undo.io/solutions/products/java/ - which works ok but would be even
better if we could recreate compiled code state in an interpreter. That's
why I'm messing with ScopeDescs.

Thanks,

David
-- 
David Griffiths, Senior Software Engineer

Undo <https://undo.io> | Accelerate software defect resolution by
eliminating the guesswork in failure diagnosis


More information about the hotspot-runtime-dev mailing list