Java開(kāi)源工具在linux上的源碼分析(五):-F參數(shù)的bug
當(dāng)使用jmap,jstack是用-F參數(shù)的時(shí)候,是通過(guò)調(diào)用系統(tǒng)調(diào)用ptrace來(lái)取的寄存器的信息,關(guān)于linux下的ptrace實(shí)現(xiàn)可以參考我的博客(http://blog.csdn.net/raintungli/article/details/6563867)
在jdk6u23版本之前你會(huì)發(fā)現(xiàn),當(dāng)你使用jstack -F的時(shí)候 經(jīng)常在logger 里面 看到錯(cuò)誤信息,直接拋出異常,根本無(wú)法看到堆棧信息。
- Thread 26724: (state = BLOCKED)
- Error occurred during stack walking:
- sun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.debugger.DebuggerException: get_thread_regs failed for a lwp
- at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal$LinuxDebuggerLocalWorkerThread.execute(LinuxDebuggerLocal.java:152)
- at sun.jvm.hotspot.debugger.....
通過(guò)查看源碼,最后調(diào)用的函數(shù)是process_get_lwp_regs /ps_proc.c
- static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct user_regs_struct *user) {
- // we have already attached to all thread 'pid's, just use ptrace call
- // to get regset now. Note that we don't cache regset upfront for processes.
- // Linux on x86 and sparc are different. On x86 ptrace(PTRACE_GETREGS, ...)
- // uses pointer from 4th argument and ignores 3rd argument. On sparc it uses
- // pointer from 3rd argument and ignores 4th argument
- #if defined(sparc) || defined(sparcv9)
- #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, addr, data)
- #else
- #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, data, addr)
- #endif
- #ifdef _LP64
- #ifdef PTRACE_GETREGS64
- #define PTRACE_GETREGS_REQ PTRACE_GETREGS64
- #endif
- #else
- #if defined(PTRACE_GETREGS) || defined(PT_GETREGS)
- #define PTRACE_GETREGS_REQ PTRACE_GETREGS
- #endif
- #endif /* _LP64 */
- #ifdef PTRACE_GETREGS_REQ
- if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) {
- print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid);
- return false;
- }
- return true;
- #else
- print_debug("ptrace(PTRACE_GETREGS, ...) not supported\n");
- return false;
- #endif
- }
無(wú)法判斷究竟是否是因?yàn)闆](méi)有定義參數(shù)PTRACE_GETREGS_REQ,還是因?yàn)閜trace的調(diào)用參數(shù)錯(cuò)誤所導(dǎo)致的,這樣就必須打開(kāi)print_debug,查看打印的信息。
通過(guò)源碼,可以查到print_debug函數(shù)是通過(guò)環(huán)境變量LIBSAPROC_DEBUG來(lái)控制
設(shè)置
export LIBSAPROC_DEBUG=1
運(yùn)行
jstack -F processid
我們能看到錯(cuò)誤中多了一行
- Thread 26724: (state = BLOCKED)
- libsaproc DEBUG: ptrace(PTRACE_GETREGS, ...) not supported
產(chǎn)生的原因就非常清楚了,bug主要是因?yàn)楹甓xPTRACE_GETREGS_REQ缺失,查看源碼
- #ifdef _LP64
- #ifdef PTRACE_GETREGS64
- #define PTRACE_GETREGS_REQ PTRACE_GETREGS64
- #endif
- #else
- #if defined(PTRACE_GETREGS) || defined(PT_GETREGS)
- #define PTRACE_GETREGS_REQ PTRACE_GETREGS
- #endif
- #endif /* _LP64 */
_LP64 是64位機(jī)器的宏定義,而對(duì)ptrace的參數(shù)PTRACE_GETREGS64,顯然Linux kernel 2.6.35里面并沒(méi)有支持,導(dǎo)致了沒(méi)有宏定義PTRACE_GETREGS_REQ,這里明顯是jvm沒(méi)有考慮到的情況。
解決辦法
a. 因?yàn)檫@是jvm 編譯級(jí)別的bug,除非你重現(xiàn)修改編譯libsaproc.so,覆蓋目錄/jdk1.6.0_23/jre/lib/amd64
筆者自己編譯了這個(gè)lib,可以在csdn上下載(http://download.csdn.net/detail/raintungli/4065304),筆者編譯的jdk版本是1.6.23 build(19.0)
b. 建議升級(jí)jvm到1.6.30版本,該版本已經(jīng)測(cè)試過(guò),已經(jīng)修復(fù)該bug.
后話:
- if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) {
- print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid);
- return false;
- }
jvm可以在此處更清楚點(diǎn),不是簡(jiǎn)單的判斷<0,而是在判斷<0的時(shí)候把errno打印出來(lái),能更容易的判斷出是什么原因無(wú)法ptrace 上。
原文鏈接:http://blog.csdn.net/raintungli/article/details/7245709
【系列文章】