如何打印Java转储的托管和非托管调用堆栈?

如何打印Java转储的托管和非托管调用堆栈?,java,debugging,windbg,dump,Java,Debugging,Windbg,Dump,在.Net中,我们可以使用打印从转储中打印调用堆栈中的托管和非托管代码方法,我是否可以知道Java中是否有一个等效程序也可以打印托管和非托管调用堆栈?您正在比较两个无法比较的东西 一方面,您指的是!DumpStack,它是调试器的插件。调试器是为特定操作系统编写的,在本例中是WinDbg for Windows。当然,Windows调试器知道如何读取本机调用堆栈 另一方面,您希望从JVM获取该信息。JVM是Java虚拟机的缩写。如果您使用过VMWare等虚拟机,您就会知道虚拟机运行的是不同的“操

在.Net中,我们可以使用打印从转储中打印调用堆栈中的托管和非托管代码方法,我是否可以知道Java中是否有一个等效程序也可以打印托管和非托管调用堆栈?

您正在比较两个无法比较的东西

一方面,您指的是
!DumpStack
,它是调试器的插件。调试器是为特定操作系统编写的,在本例中是WinDbg for Windows。当然,Windows调试器知道如何读取本机调用堆栈

另一方面,您希望从JVM获取该信息。JVM是Java虚拟机的缩写。如果您使用过VMWare等虚拟机,您就会知道虚拟机运行的是不同的“操作系统”。在本例中,操作系统的名称是Java

如果您考虑在Windows主机上运行Linux VM,并且在VM中使用Linux调试器(GDB),它会显示Windows调用堆栈吗?不,不会的-至少当虚拟机设计正确时

将其映射到JVM,您应该无法从Java程序内部看到本机调用堆栈,因为它在VM中运行

那么,为什么
!DumpStack
那么做吗?因为情况正好相反:在Windows主机上运行调试器时,可以看到Linux虚拟机的调用堆栈。同样适用于
!DumpStack
:您正在从.NET虚拟机外部运行它,它被设计用于解释.NET虚拟机的内容

因此,基本上您需要的是WinDbg,它可以读取本机端,再加上一个WinDbg插件,它可以解释JavaVM(JVM)的内容。同样,这被认为是离题的,让我引用我的评论:

不幸的是,没有,至少没有这样的工具


我在WinDbg行业工作了7年,发现了很多用于各种用途的插件,但没有用于显示Java调用堆栈的插件。我很想看到一个,因为我有不止一个使用案例。

首先,我想评论一下,这个问题对我来说似乎很好,我发现结束你之前的问题和一些评论令人震惊,再次证明了这么做的许多错误

如果问这些关于.NET的问题是可以的,问他们关于Java的问题也是可以的,即使答案是“你不能”

(我将加入进来并吹毛求疵:“托管”是Microsoft对在CLR下运行的代码的术语。您想问这样的问题:“如何在WinDbg中获得一个既包含JVM本机代码又包含其中运行的JITted Java代码的可用堆栈跟踪?”)

现在谈谈你的问题:


简介 不幸的是,我不知道有什么工具或WinDbg插件可以做到这一点,但我认为有一种方法可以让你自己制作一个。(我想我可能会去,但我找不到时间,所以我想我至少应该给你信息。既然你问了两次这个问题,我想这可能很重要,所以你会费心采取必要的措施。)

解决您的问题可以分为两部分:

  • 获取与JITted代码地址对应的名称(“生成符号”)
  • 正在使用WinDbg中生成的符号
  • 我将按顺序讨论它们,并在结束时提出一些警告

    生成符号 布伦丹·格雷格有。他需要JITted代码的函数名。用他自己的话说,解决办法是:

    解决上述两个问题的一种方法包括:

  • JVMTI代理,它可以提供一个Java符号表供perf读取(/tmp/perf-PID.map)
  • 修补JDK热点以重新引入帧指针寄存器,该寄存器允许全堆栈遍历
  • JVMTI代表Java虚拟机床接口。JVMTI代理基本上是JVM的插件,它为他感兴趣的JVM事件实现回调。它可以动态加载或链接到JVM二进制文件中(如果您正在定制JVM构建)

    对于perf-map-agent,插件要求了解所有JITted方法和一些其他动态生成的代码。它可以得到他们的名字,地址和尺寸。然后,它将这些信息输出到一个文件中,该文件稍后由LinuxPerf使用

    你应该把它岔开,改变其中任何Linux特定的东西(我没有注意到任何东西,但可能有隐藏的东西),然后在Windows中为HotSpot的Windows版本构建一个DLL

    您还应该更改生成输出文件的函数(在
    src/c/perf map file.c
    中),以使WinDbg能够使用的格式输出信息,这就引出了第二部分

    WinDbg中的符号消费 有几种方法可以让WinDbg使用JVMTI代理生成的符号,您可能可以考虑更多。我会提到我脑海中出现的几个问题:

  • 生成映射文件并将其转换为WinDbg将加载的内容。
    • 可能会将其转换为带有的DBG文件
    • 也许甚至可以尝试使用它来制作PDB
  • 自己生成PDB,可能使用项目中GitHub上Microsoft发布的信息
  • 使用调用的WinDbg扩展插件将符号加载到WinDbg。
    您有一个这样做的代码(由blabb编写,他在windbg标记的SO中处于活动状态)。在www.woodmann.com上曾经有一个二进制代码,但是这个网站现在对我不起作用(而且已经很久没有了)。似乎有一个二进制文件
  • 选项3在我看来是最简单的(从我的经验来看效果最好),但你可以做任何你想做的事

    警告 RBP链 你们可能还记得,在Brendan Gregg的引用中,有一句话是关于修补HotSpot以保持RBP链的。也许HotSpot Dynamicly的Windows实现创建了WinDbg在x64上用于漫游堆栈的展开信息。也许没有。我不知道

    C:\Users\conio\Desktop\code\JNA2>"C:\Program Files\Java\jdk1.8.0_102\bin\jstack.exe" -?
    Usage:
        jstack [-l] <pid>
            (to connect to running process)
        jstack -F [-m] [-l] <pid>
            (to connect to a hung process)
        jstack [-m] [-l] <executable> <core>
            (to connect to a core file)
        jstack [-m] [-l] [server_id@]<remote server IP or hostname>
            (to connect to a remote debug server)
    
    Options:
        -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
        -m  to print both java and native frames (mixed mode)
        -l  long listing. Prints additional information about locks
        -h or -help to print this help message
    
    import com.sun.jna.Library;
    import com.sun.jna.Native;
    import com.sun.jna.Platform;
    
    import com.sun.jna.Pointer;
    
    public class Foo
    {
    public interface CLibrary extends Library {
            CLibrary INSTANCE = (CLibrary)Native.loadLibrary("msvcrt", CLibrary.class);
    
            void printf(String format, Object... args);
            Pointer malloc(int size);
            Pointer memset(Pointer dest, int ch, int count);
            void free(Pointer ptr);
        }
    
    
        public static void main(String[] args)
        {    
            CLibrary.INSTANCE.printf("Hello, World\n");
    
            Pointer buf = CLibrary.INSTANCE.malloc(2);
            CLibrary.INSTANCE.memset(buf, 0, 0x20);
            CLibrary.INSTANCE.free(buf);
    
            CLibrary.INSTANCE.printf("Goodbye, Cruel World\n");
        }
    }
    
    Microsoft (R) Windows Debugger Version 10.0.14321.1024 X86
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    *** wait with pending attach
    <<<snip>>>
    (1f0.14d0): Unknown exception - code c0000374 (!!! second chance !!!)
    eax=011eea00 ebx=77b5c908 ecx=00000001 edx=77b5c8d0 esi=00000002 edi=01251240
    eip=77b29841 esp=011ee9dc ebp=011eea6c iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    ntdll!RtlReportCriticalFailure+0x89:
    77b29841 eb33            jmp     ntdll!RtlReportCriticalFailure+0xbe (77b29876)
    0:003> !error c0000374
    Error code: (NTSTATUS) 0xc0000374 (3221226356) - A heap has been corrupted.
    0:003> k
     # ChildEBP RetAddr  
    00 011eea6c 77b2cfe2 ntdll!RtlReportCriticalFailure+0x89
    01 011eea78 77b2b763 ntdll!RtlpReportHeapFailure+0x32
    02 011eea88 77ad16cf ntdll!RtlpHeapHandleError+0x1c
    03 011eeab8 77ae278b ntdll!RtlpLogHeapFailure+0x9f
    04 011eebc8 77a978aa ntdll!RtlpFreeHeap+0x4aa1b
    05 011eebf4 749d77c5 ntdll!RtlFreeHeap+0xba
    *** WARNING: Unable to verify checksum for C:\Users\conio\AppData\Local\Temp\jna-71761103\jna991038711348528033.dll
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Users\conio\AppData\Local\Temp\jna-71761103\jna991038711348528033.dll - 
    06 011eec40 6e562670 msvcrt!free+0x65
    WARNING: Stack unwind information not available. Following frames may be wrong.
    07 011eec4c 6e55d8e2 jna991038711348528033!Java_com_sun_jna_Native_setDetachState+0x7020
    08 011eec88 6e553c74 jna991038711348528033!Java_com_sun_jna_Native_setDetachState+0x2292
    09 011ef580 6e5548c4 jna991038711348528033!Java_com_sun_jna_Native_invokePointer+0xca4
    0a 011ef5a8 02c4d3b3 jna991038711348528033!Java_com_sun_jna_Native_invokeVoid+0x24
    0b 011ef5ec 02c44854 0x2c4d3b3
    0c 011ef62c 02c447b4 0x2c44854
    0d 011ef678 02c447b4 0x2c447b4
    0e 011ef784 02c40697 0x2c447b4
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files (x86)\Java\jre1.8.0_102\bin\client\jvm.dll - 
    0f 011ef794 6e74a1b2 0x2c40697
    10 011ef838 6e8104fe jvm!JVM_GetThreadStateNames+0x4cc82
    *** ERROR: Module load completed but symbols could not be loaded for C:\Program Files (x86)\Java\jre1.8.0_102\bin\java.exe
    11 011ef9bc 0083229e jvm!JVM_FindSignal+0x63b3e
    12 011ef9d4 02b31310 java+0x229e
    13 011ef9d8 02b31310 0x2b31310
    14 011efa08 0083aeaf 0x2b31310
    15 011efa40 0083af39 java+0xaeaf
    16 011efa4c 751f62c4 java+0xaf39
    17 011efa60 77ab0609 KERNEL32!BaseThreadInitThunk+0x24
    18 011efaa8 77ab05d4 ntdll!__RtlUserThreadStart+0x2f
    19 011efab8 00000000 ntdll!_RtlUserThreadStart+0x1b
    0:003> |
    .  0    id: 1f0 attach  name: C:\Program Files (x86)\Java\jre1.8.0_102\bin\java.exe
    0:003> ? 0x1f0
    Evaluate expression: 496 = 000001f0
    0:003> .dump /ma C:\Users\conio\Desktop\code\JNA2\Foo.dmp
    Creating C:\Users\conio\Desktop\code\JNA2\Foo.dmp - mini user dump
    Dump successfully written
    
    C:\Users\conio\Desktop\code\JNA2>"C:\Program Files (x86)\Java\jdk1.8.0_102\bin\jstack.exe" -F -m 496
    Attaching to process ID 496, please wait...
    Debugger attached successfully.
    Client compiler detected.
    JVM version is 25.102-b14
    Deadlock Detection:
    
    No deadlocks found.
    
    ----------------- 0 -----------------
    <<<snip>>>
    ----------------- 3 -----------------
    0x77b29841      ntdll!RtlpNtSetValueKey + 0x4e1
    0x77b2cfe2      ntdll!RtlpNtSetValueKey + 0x3c82
    0x77ad16cf      ntdll!wcstok_s + 0x5baf
    0x77ae278b      ntdll!LdrSetAppCompatDllRedirectionCallback + 0xff2b
    0x77a978aa      ntdll!RtlFreeHeap + 0xba
    0x749d77c5      msvcrt!free + 0x65
    0x6e562670      jna991038711348528033!_Java_com_sun_jna_Native_setDetachState@20 + 0x7020
    0x6e55d8e2      jna991038711348528033!_Java_com_sun_jna_Native_setDetachState@20 + 0x2292
    0x6e553c74      jna991038711348528033!_Java_com_sun_jna_Native_invokePointer@24 + 0xca4
    0x6e5548c4      jna991038711348528033!_Java_com_sun_jna_Native_invokeVoid@24 + 0x24
    0x02c4d3b3      * com.sun.jna.Native.invokeVoid(long, int, java.lang.Object[]) bci:0 (Interpreted frame)
    0x02c44854      * com.sun.jna.Function.invoke(java.lang.Object[], java.lang.Class, boolean) bci:29 line:374 (Interpreted frame)
    0x02c447b4      * com.sun.jna.Function.invoke(java.lang.reflect.Method, java.lang.Class[], java.lang.Class, java.lang.Object[], java.util.Map) bci:249 line:323 (Interpreted frame)
    0x02c447b4      * com.sun.jna.Library$Handler.invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) bci:348 line:236 (Interpreted frame)
    0x02c447e9      * com.sun.proxy.$Proxy0.free(com.sun.jna.Pointer) bci:16 (Interpreted frame)
    0x02c44889      * Foo.main(java.lang.String[]) bci:41 line:25 (Interpreted frame)
    0x02c40697      <StubRoutines>
    0x6e74a6e5      jvm!JVM_GetThreadStateNames + 0x4d1b5
    0x6e8104fe      jvm!_JVM_FindSignal@4 + 0x63b3e
    0x6e74a77e      jvm!JVM_GetThreadStateNames + 0x4d24e
    0x6e6cc337      jvm!JNI_GetCreatedJavaVMs + 0x6f27
    0x6e6d48cf      jvm!JNI_GetCreatedJavaVMs + 0xf4bf
    0x0083229e      java + 0x229e
    0x0083aeaf      java + 0xaeaf
    0x0083af39      java + 0xaf39
    0x751f62c4      KERNEL32!BaseThreadInitThunk + 0x24
    0x77ab0609      ntdll!RtlSubscribeWnfStateChangeNotification + 0x439
    0x77ab05d4      ntdll!RtlSubscribeWnfStateChangeNotification + 0x404
    ----------------- 4 -----------------
    <<<snip>>>
    
    C:\Users\conio\Desktop\code\JNA2>"C:\Program Files (x86)\Java\jdk1.8.0_102\bin\jstack.exe" -m "C:\Program Files (x86)\Java\jre1.8.0_102\bin\java.exe" Foo.dmp
    Attaching to core Foo.dmp from executable C:\Program Files (x86)\Java\jre1.8.0_102\bin\java.exe, please wait...
    Debugger attached successfully.
    Client compiler detected.
    JVM version is 25.102-b14
    Deadlock Detection:
    
    No deadlocks found.
    
    <<<snip - same output as above>>