JDK-8240987 : Implement lost clhsdb javascript commands by using java instead
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: svc-agent
  • Affected Version: 15
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2020-03-13
  • Updated: 2021-12-22
  • Resolved: 2021-12-22
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 19
19Fixed
Related Reports
Relates :  
Relates :  
Sub Tasks
JDK-8240989 :  
JDK-8240990 :  
JDK-8242142 :  
JDK-8242162 :  
JDK-8244669 :  
JDK-8244670 :  
Description
JDK-8235594 documents issues with the SA javascript support (which is most apparent in clhsdb). Since the javascript support no longer works, it has been disabled and will soon be removed. As a result we have lost the following clhsdb commands, which were implemented in javascript:

class
classes
dumpclass
dumpheap
mem
sysprops
whatis

It would be nice to bring as many of these back as possible by implementing them in java. For some, such as dumpheap and dumpclass, the work is trivial since the only thing the javascript side of the command is doing is invoking some existing java code. We can instead just have a regular clhsdb command (written in java) do the same. I've was easily able to do this for dumpheap already with just a few minutes of work. Other commands will be much more of a challenge since they have a lot of javascript supporting them, and may not be worth the effort.

sa.js is where you will find the javascript implementation of these commands. You'll see something like the following for each command:

    registerCommand("dumpheap", "dumpheap [ file ]", "dumpHeap"); 

The 3rd argument is the implementation. It can either be a javascript method in the sa.js file, or it can be a java method in JSJavaScriptEngine.java. For dumpHeap it is the latter, and the implementation is simple:

    public Object dumpHeap(Object[] args) {
        String fileName = "heap.bin";
        if (args.length > 0) {
            fileName = args[0].toString();
        }
        return new JMap().writeHeapHprofBin(fileName)? Boolean.TRUE: Boolean.FALSE;
    } 

It is very easy to move this code into CommandProcessor.java as a new Command in commandList[].

This CR will be used as a parent CR for each command we want to implement in java, which will be handled in subtasks. We can close this CR when all of the have been implemented or determined to not be worth the effort.
Comments
All subtasks have been resolved, so this issue is now fixed.
22-12-2021

[~poonam] Also, after looking at the implementation of "whatis", it seems that we should be able to get some sort of symbolic information for any address that "whatis" can identify, such as the heap, interpreter, or codecache. PointerLocation.printOn() is a good example of how to get that symbolic information. Also, I noticed that the JS implementation of "whatis" looks identical to the java version of "findpc". Can you try both and see if there is any difference in the output? It should be the same, even after passing a java heap address to findpc.
07-04-2020

Sorry I wasn't clear. I was referring to hotspot native symbols, ie. C/C++ functions and methods. I think mem will display those.
07-04-2020

Here's what else I tried: --------------------------------- Disassembly for compiled method [public java.lang.String methodA() @0x00007fba00681438 ] @0x00007fb9ed2b7690 Method public java.lang.String methodA() @0x00007fba00681438 of public class TestClass @0x0000000800060028 Compiled Code [Entry Point] 0x00007fb9ed2b7820: mov 0x8(%rsi),%r10d 0x00007fb9ed2b7824: mov $0x800000000,%r12 0x00007fb9ed2b782e: add %r12,%r10 0x00007fb9ed2b7831: xor %r12,%r12 0x00007fb9ed2b7834: cmp %rax,%r10 0x00007fb9ed2b7837: jne 0x00007fb9ed045ba0 0x00007fb9ed2b783d: xchg %ax,%ax [Verified Entry Point] 0x00007fb9ed2b7840: mov %eax,-0x14000(%rsp) 0x00007fb9ed2b7847: push %rbp 0x00007fb9ed2b7848: sub $0x50,%rsp 0x00007fb9ed2b784c: mov %rsi,%rbx hsdb> mem 0x00007fba00681438 10 0x00007fba00681438: 0x00007fba04160028 0x00007fba00681440: 0x00007fba006813d8 0x00007fba00681448: 0x00007fba00681860 0x00007fba00681450: 0x00007fba00681820 0x00007fba00681458: 0x0000000580000001 0x00007fba00681460: 0x000000000000000b 0x00007fba00681468: 0x00007fb9ed0133e0 0x00007fba00681470: 0x00007fb9fc083a00 0x00007fba00681478: 0x00007fb9ed2b7840 0x00007fba00681480: 0x00007fb9ed2b7690 hsdb> mem 0x00007fb9ed2b7690 10 0x00007fb9ed2b7690: 0x00007fba040dcb10 0x00007fb9ed2b7698: 0x00007fba03d4555b 0x00007fb9ed2b76a0: 0x0000012800000658 0x00007fb9ed2b76a8: 0x0000019000000050 0x00007fb9ed2b76b0: 0x0000002c00000190 0x00007fb9ed2b76b8: 0x0000000c00000520 0x00007fb9ed2b76c0: 0x00007fb9300095c0 0x00007fb9ed2b76c8: 0x0000000000000000 0x00007fb9ed2b76d0: 0x00007fba00681438 0x00007fb9ed2b76d8: 0x00000000ffffffff hsdb> mem 0x00007fba00681438 10 0x00007fba00681438: 0x00007fba04160028 0x00007fba00681440: 0x00007fba006813d8 0x00007fba00681448: 0x00007fba00681860 0x00007fba00681450: 0x00007fba00681820 0x00007fba00681458: 0x0000000580000001 0x00007fba00681460: 0x000000000000000b 0x00007fba00681468: 0x00007fb9ed0133e0 0x00007fba00681470: 0x00007fb9fc083a00 0x00007fba00681478: 0x00007fb9ed2b7840 0x00007fba00681480: 0x00007fb9ed2b7690 hsdb> mem 0x00007fb9ed2b7820 10 0x00007fb9ed2b7820: 0x0000bc4908568b44 0x00007fb9ed2b7828: 0x034d000000080000 0x00007fb9ed2b7830: 0x0fd03b4ce4334dd4 0x00007fb9ed2b7838: 0x906666ffd8e36385 0x00007fb9ed2b7840: 0x55fffec000248489 0x00007fb9ed2b7848: 0x48de8b4850ec8348 0x00007fb9ed2b7850: 0x007fba00681860ba 0x00007fb9ed2b7858: 0x83000000dcb28b00 0x00007fb9ed2b7860: 0x000000dcb28908c6 0x00007fb9ed2b7868: 0x7fba00681438ba48 hsdb> mem 0x00007fb9ed2b7840 10 0x00007fb9ed2b7840: 0x55fffec000248489 0x00007fb9ed2b7848: 0x48de8b4850ec8348 0x00007fb9ed2b7850: 0x007fba00681860ba 0x00007fb9ed2b7858: 0x83000000dcb28b00 0x00007fb9ed2b7860: 0x000000dcb28908c6 0x00007fb9ed2b7868: 0x7fba00681438ba48 0x00007fb9ed2b7870: 0x00001ff8e6810000 0x00007fb9ed2b7878: 0x0001f4840f00fe83 0x00007fb9ed2b7880: 0x03e2c14810538b00 0x00007fb9ed2b7888: 0x1860ba4800fa8348 ---------------------------------------------------- 0x00007fb9ed2b7aa8: callq 0x00007fb9ed06eaa0 public java.lang.String methodA() @0x00007fba00681438 of public class TestClass @0x0000000800060028 @ bci = 24, line = 21 locals ([0], rbx, oop) expressions ([0], rbx, oop) hsdb> mem 0x00007fb9ed2b7aa8 10 0x00007fb9ed2b7aa8: 0xfedbe9ffdb6ff3e8 0x00007fb9ed2b7ab0: 0x480824748948ffff 0x00007fb9ed2b7ab8: 0xe8ffffffff2404c7 0x00007fb9ed2b7ac0: 0xffff0be9fff61fdc 0x00007fb9ed2b7ac8: 0xc7480824748948ff 0x00007fb9ed2b7ad0: 0xc5e8ffffffff2404 0x00007fb9ed2b7ad8: 0xffffff3be9fff61f 0x00007fb9ed2b7ae0: 0x499090ffdb8e1be8 0x00007fb9ed2b7ae8: 0xba49000002a8878b 0x00007fb9ed2b7af0: 0x0000000000000000 ------------------------------------ Java Stack Trace for main Thread state = BLOCKED - public static native void sleep(long) @0x00007fba002aea40 @bci = 0, pc = 0x00007fb9ed2b7f41 (Compiled; information may be imprecise) - public static void main(java.lang.String[]) @0x00007fba006815f0 @bci = 53, line = 55, pc = 0x00007fb9ed0080a1 (Interpreted) hsdb> whatis 0x00007fb9ed0080a0 Address 0x00007fb9ed0080a0: In interpreter codelet "invoke return entry points" invoke return entry points [0x00007fb9ed007778, 0x00007fb9ed008180) 2568 bytes hsdb> mem 0x00007fb9ed0080a0 10 0x00007fb9ed0080a0: 0x45c748f0658b48da 0x00007fb9ed0080a8: 0x6d8b4c00000000f0 0x00007fb9ed0080b0: 0xb70f41d0758b4cc8 0x00007fb9ed0080b8: 0xe1c1d85d8b48014d 0x00007fb9ed0080c0: 0xffe38128cb5c8b02 0x00007fb9ed0080c8: 0x41dc248d48000000 0x00007fb9ed0080d0: 0x03c58349035db60f 0x00007fb9ed0080d8: 0x7fba041c44c0ba49 0x00007fb9ed0080e0: 0x8b48da24ff410000 0x00007fb9ed0080e8: 0x0000f045c748f065
07-04-2020

function loadObjectContainingPC(addr) { if (sa.cdbg == null) { // no CDebugger support, return null return null; } return sa.cdbg.loadObjectContainingPC(addr); } // returns the ClosestSymbol or null function closestSymbolFor(addr) { var dso = loadObjectContainingPC(addr); if (dso != null) { return dso.closestSymbolToPC(addr); } return null; } So there are a couple of possible failure points, but I think maybe the issue is that the address has to be a PC, and it will find the nearest method/function to that PC. extern and static symbols are usually in a different section of the dso, and probably are not visible to this loadObjectContainingPC(). Can you try a function/method name and see if it works?
07-04-2020

I tried with a few more addresses. For example: (gdb) print *((ConstantPool*)0x00007f9fec1c9960) $4 = {<Metadata> = {<MetaspaceObj> = {<No data fields>}, _vptr.Metadata = 0x7fa009292398 <vtable for ConstantPool+16>}, _tags = 0x7f9fec1c9940, _cache = 0x7f9fec31c800, _pool_holder = 0x800005450, _operands = 0x0, _resolved_references = 0x0, _reference_map = 0x0, _flags = 0, _length = 21, _saved = {_resolved_reference_length = 0, _version = 0}, _lock = 0x7fa0000651b0} (gdb) x/10gx 0x00007f9fec1c9960 0x7f9fec1c9960: 0x00007fa009292398 0x00007f9fec1c9940 0x7f9fec1c9970: 0x00007f9fec31c800 0x0000000800005450 0x7f9fec1c9980: 0x0000000000000000 0x0000000000000000 0x7f9fec1c9990: 0x0000000000000000 0x0000001500000000 0x7f9fec1c99a0: 0x0000000000000000 0x00007fa0000651b0 (gdb) x/2gx 0x00007fa009292398 0x7fa009292398 <_ZTV12ConstantPool+16>: 0x00007fa008671310 0x00007fa0087d3650 hsdb> mem 0x00007f9fec1c9960 10 0x00007f9fec1c9960: 0x00007fa009292398 0x00007f9fec1c9968: 0x00007f9fec1c9940 0x00007f9fec1c9970: 0x00007f9fec31c800 0x00007f9fec1c9978: 0x0000000800005450 0x00007f9fec1c9980: 0x0000000000000000 0x00007f9fec1c9988: 0x0000000000000000 0x00007f9fec1c9990: 0x0000000000000000 0x00007f9fec1c9998: 0x0000001500000000 0x00007f9fec1c99a0: 0x0000000000000000 0x00007f9fec1c99a8: 0x00007fa0000651b0 hsdb> examine 0x00007f9fec1c9960/10 0x00007f9fec1c9960: 0x00007fa009292398 0x00007f9fec1c9940 0x00007f9fec31c800 0x00007f9fec1c9978: 0x0000000800005450 0x0000000000000000 0x0000000000000000 0x00007f9fec1c9990: 0x0000000000000000 0x0000001500000000 0x0000000000000000 0x00007f9fec1c99a8: 0x00007fa0000651b0 As we can see, 'mem' does not print symbol names. So, looks like 'examine' can safely replace the 'mem' command.
06-04-2020

The code for 'mem' does indicate that it should print symbols for addresses, if found. But, from my experiments with 'mem' so far (on linux-x64), it does not print any symbol information. It just dumps raw hex values present at a given address. hsdb> symbol 0x00007fa009e4c720 #java/security/ProtectionDomain hsdb> mem 0x00007fa009e4c720 10 0x00007fa009e4c720: 0x6b3e205fffff001e 0x00007fa009e4c728: 0x6365732f6176616a 0x00007fa009e4c730: 0x72502f7974697275 0x00007fa009e4c738: 0x6e6f69746365746f 0x00007fa009e4c740: 0x00006e69616d6f44 0x00007fa009e4c748: 0x0000000000000000 0x00007fa009e4c750: 0x3bbf73faffff001f 0x00007fa009e4c758: 0x6365732f6176616a 0x00007fa009e4c760: 0x65532f7974697275 0x00007fa009e4c768: 0x73616c4365727563 hsdb> examine 0x00007fa009e4c720/10 0x00007fa009e4c720: 0x6b3e205fffff001e 0x6365732f6176616a 0x72502f7974697275 0x00007fa009e4c738: 0x6e6f69746365746f 0x00006e69616d6f44 0x0000000000000000 0x00007fa009e4c750: 0x3bbf73faffff001f 0x6365732f6176616a 0x65532f7974697275 0x00007fa009e4c768: 0x73616c4365727563 hsdb> mem 0x0000000800003890 10 0x0000000800003890: 0x00007fa0093174c8 0x0000000800003898: 0x0000003000000028 0x00000008000038a0: 0x00007fa009e4c720 0x00000008000038a8: 0x0000000000000000 0x00000008000038b0: 0x00007f9fec1a2088 0x00000008000038b8: 0x0000000800000f28 0x00000008000038c0: 0x0000000800003890 0x00000008000038c8: 0x0000000000000000 0x00000008000038d0: 0x0000000000000000 0x00000008000038d8: 0x0000000000000000 hsdb> examine 0x0000000800003890/10 0x0000000800003890: 0x00007fa0093174c8 0x0000003000000028 0x00007fa009e4c720 0x00000008000038a8: 0x0000000000000000 0x00007f9fec1a2088 0x0000000800000f28 0x00000008000038c0: 0x0000000800003890 0x0000000000000000 0x0000000000000000 0x00000008000038d8: 0x0000000000000000 hsdb> inspect 0x0000000800003890 Type is InstanceKlass (size of 440) juint Klass::_super_check_offset: 48 Klass* Klass::_secondary_super_cache: Klass @ null Array<Klass*>* Klass::_secondary_supers: Array<Klass*> @ 0x00007f9fec1a2088 Klass* Klass::_primary_supers[0]: Klass @ 0x0000000800000f28 oop Klass::_java_mirror: Oop for java/lang/Class @ 0x000000058a7811a0 Oop for java/lang/Class @ 0x000000058a7811a0 jint Klass::_modifier_flags: 1 Klass* Klass::_super: Klass @ 0x0000000800000f28 Klass* Klass::_subklass: Klass @ null jint Klass::_layout_helper: 40 Symbol* Klass::_name: Symbol @ 0x00007fa009e4c720 AccessFlags Klass::_access_flags: 16777249 markOop Klass::_prototype_header: 5 Klass* Klass::_next_sibling: Klass @ 0x0000000800003538 ... It will try with a few more addresses and will update again.
06-04-2020

I experimented a bit with CLHSDB in JDK 8 and couldn't find an address to pass to "mem" that it computed a symbol name for, so it wasn't really doing anything that is more useful than "examine".
04-04-2020

[~poonam] The 'mem' command looks like it might be very similar to the existing 'examine' command: examine [ address/count ] | [ address,address] mem address [ length ] I think the difference might be that 'examine' is a raw hex dump with multiple addresses dumped per line, whereas 'mem' tries to determine the symbol nearest each address, so each line output is a single address with the symbol name (plus offset) and the value at that address. Is that the case? Can you send me example output of each.
04-04-2020

Thanks Chris! It would be great if 'mem' and 'whatis' commands could be brought back too.
20-03-2020