RFR: 8368787: Error reporting: hs_err files should show instructions when referencing code in nmethods

Aleksey Shipilev shade at openjdk.org
Mon Oct 6 07:36:46 UTC 2025


On Fri, 26 Sep 2025 16:12:14 GMT, Martin Doerr <mdoerr at openjdk.org> wrote:

> We'd like to have a little more information in hs_err files in the following scenario: The VM crashes in code which does something with an nmethod. We often have a register pointing into code of the nmethod, but the nmethod is not disassembled in the hs_err file because the crash happens outside of it.
> 
> We can disassemble some instructions around the address inside the nmethod code. This is tricky on platforms which have variable length instructions (like x86). We need to find correct instruction start addresses. I'm proposing to use relocations for this purpose. There are usually enough of them distributed over the nmethod and they point to instruction start addresses.
> 
> I've tested this proposal by the following code on x86_64:
> 
> diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp
> index a6b4efbe4f2..d715e69c850 100644
> --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp
> +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp
> @@ -646,6 +646,18 @@ void InterpreterMacroAssembler::prepare_to_jump_from_interpreted() {
>  void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) {
>    prepare_to_jump_from_interpreted();
>  
> +  if (UseNewCode) {
> +    Label ok;
> +    movptr(temp, Address(method, Method::from_interpreted_offset()));
> +    cmpptr(temp, Address(method, Method::interpreter_entry_offset()));
> +    je(ok);
> +    movptr(rax, Address(method, Method::from_compiled_offset()));
> +    movptr(rbx, rax);
> +    addptr(rbx, 128);
> +    hlt();
> +    bind(ok);
> +  }
> +
>    if (JvmtiExport::can_post_interpreter_events()) {
>      Label run_compiled_code;
>      // JVMTI events, such as single-stepping, are implemented partly by avoiding running
> 
> 
> The output is (requires hsdis library, otherwise we get a hex dump instead of disassembly):
> 
> RAX=0x00007f3b19000100 is at entry_point+0 in (nmethod*)0x00007f3b19000008
> Compiled method (c1) 2915    1       3       java.lang.Byte::toUnsignedInt (6 bytes)
>  total in heap  [0x00007f3b19000008,0x00007f3b190001f8] = 496
>  main code      [0x00007f3b19000100,0x00007f3b190001b8] = 184
>  stub code      [0x00007f3b190001b8,0x00007f3b190001f8] = 64
>  mutable data [0x00007f3ab401e0b0,0x00007f3ab401e0e0] = 48
>  relocation     [0x00007f3ab401e0b0,0x00007f3ab401e0d8] = 40
>  metadata       [0x00007f3ab401e0d8,0x00007f3ab401e0e0] = 8
>  immutable data [0x00007f3ab401dcd0,0x00007f3ab401dd30] = 96
>  dependencies   [0x00007f3ab401dcd0,0x00007f3ab401dcd8] = 8
>  scopes pcs     [0x00007f3ab401dcd8,0x00007f3ab401dd18] = 64
>  ...

Looks like a useful diagnostic tool. At very least we should dump the raw instruction stream around that pc, like we do for `Instructions:` block. Does Hotspot do that already?

Relocation trick is cute, but it hinges on assumption that relocations are always pointing at instruction boundary. I looked around and I think while most relocs are that way, there are some relocs that do not follow this rule. For example:


// Store Null Pointer
instruct zStorePNull(memory mem, immP0 zero, rRegP tmp, rFlagsReg cr)
%{
  predicate(UseZGC && n->as_Store()->barrier_data() != 0);
  match(Set mem (StoreP mem zero));
  effect(TEMP tmp, KILL cr);

  ins_cost(125); // XXX
  format %{ "movq    $mem, 0\t# ptr" %}
  ins_encode %{
    z_store_barrier(masm, this, $mem$$Address, noreg, $tmp$$Register, false /* is_atomic */);
    // Store a colored null - barrier code above does not need to color
    __ movq($mem$$Address, barrier_Relocation::unpatched);
    // The relocation cant be fully after the mov, as that is the beginning of a random subsequent
    // instruction, which violates assumptions made by unrelated code. Hence the end() - 1
    __ code_section()->relocate(__ code_section()->end() - 1, barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodAfterMov);
  %}
  ins_pipe(ialu_mem_reg);
%}


I am guessing it is still fine to attempt to disassemble in this case.

-------------

PR Comment: https://git.openjdk.org/jdk/pull/27530#issuecomment-3370291722


More information about the hotspot-compiler-dev mailing list