Java开源工具在linux上的源码分析(五):-F参数的bug

开发 后端
当使用jmap,jstack是用-F参数的时候,是通过调用系统调用ptrace来取的寄存器的信息,在jdk6u23版本之前你会发现,当你使用jstack -F的时候 经常在logger 里面 看到错误信息,直接抛出异常,根本无法看到堆栈信息。

当使用jmap,jstack是用-F参数的时候,是通过调用系统调用ptrace来取的寄存器的信息,关于linux下的ptrace实现可以参考我的博客(http://blog.csdn.net/raintungli/article/details/6563867)

在jdk6u23版本之前你会发现,当你使用jstack -F的时候 经常在logger 里面 看到错误信息,直接抛出异常,根本无法看到堆栈信息。

  1. Thread 26724: (state = BLOCKED)  
  2. Error occurred during stack walking:  
  3. sun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.debugger.DebuggerException: get_thread_regs failed for a lwp  
  4.         at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal$LinuxDebuggerLocalWorkerThread.execute(LinuxDebuggerLocal.java:152)  
  5.         at sun.jvm.hotspot.debugger..... 

通过查看源码,最后调用的函数是process_get_lwp_regs /ps_proc.c

  1. static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct user_regs_struct *user) {  
  2.   // we have already attached to all thread 'pid's, just use ptrace call  
  3.   // to get regset now. Note that we don't cache regset upfront for processes.  
  4. // Linux on x86 and sparc are different.  On x86 ptrace(PTRACE_GETREGS, ...)  
  5. // uses pointer from 4th argument and ignores 3rd argument.  On sparc it uses  
  6. // pointer from 3rd argument and ignores 4th argument  
  7. #if defined(sparc) || defined(sparcv9)  
  8. #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, addr, data)  
  9. #else 
  10. #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, data, addr)  
  11. #endif  
  12.  
  13. #ifdef _LP64  
  14. #ifdef PTRACE_GETREGS64  
  15. #define PTRACE_GETREGS_REQ PTRACE_GETREGS64  
  16. #endif  
  17. #else 
  18. #if defined(PTRACE_GETREGS) || defined(PT_GETREGS)  
  19. #define PTRACE_GETREGS_REQ PTRACE_GETREGS  
  20. #endif  
  21. #endif /* _LP64 */ 
  22.  
  23. #ifdef PTRACE_GETREGS_REQ  
  24.  if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) {  
  25.    print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid);  
  26.    return false;  
  27.  }  
  28.  return true;  
  29. #else 
  30.  print_debug("ptrace(PTRACE_GETREGS, ...) not supported\n");  
  31.  return false;  
  32. #endif  
  33.  

无法判断究竟是否是因为没有定义参数PTRACE_GETREGS_REQ,还是因为ptrace的调用参数错误所导致的,这样就必须打开print_debug,查看打印的信息。

通过源码,可以查到print_debug函数是通过环境变量LIBSAPROC_DEBUG来控制

设置

export LIBSAPROC_DEBUG=1

运行

jstack -F processid

我们能看到错误中多了一行

  1. Thread 26724: (state = BLOCKED)  
  2. libsaproc DEBUG: ptrace(PTRACE_GETREGS, ...) not supported 

产生的原因就非常清楚了,bug主要是因为宏定义PTRACE_GETREGS_REQ缺失,查看源码

  1. #ifdef _LP64  
  2. #ifdef PTRACE_GETREGS64  
  3. #define PTRACE_GETREGS_REQ PTRACE_GETREGS64  
  4. #endif  
  5. #else 
  6. #if defined(PTRACE_GETREGS) || defined(PT_GETREGS)  
  7. #define PTRACE_GETREGS_REQ PTRACE_GETREGS  
  8. #endif  
  9. #endif /* _LP64 */ 

_LP64 是64位机器的宏定义,而对ptrace的参数PTRACE_GETREGS64,显然Linux kernel 2.6.35里面并没有支持,导致了没有宏定义PTRACE_GETREGS_REQ,这里明显是jvm没有考虑到的情况。

解决办法

a. 因为这是jvm 编译级别的bug,除非你重现修改编译libsaproc.so,覆盖目录/jdk1.6.0_23/jre/lib/amd64

笔者自己编译了这个lib,可以在csdn上下载(http://download.csdn.net/detail/raintungli/4065304),笔者编译的jdk版本是1.6.23 build(19.0)

b. 建议升级jvm到1.6.30版本,该版本已经测试过,已经修复该bug.

后话:

  1. if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) {  
  2.   print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid);  
  3.   return false;  

jvm可以在此处更清楚点,不是简单的判断<0,而是在判断<0的时候把errno打印出来,能更容易的判断出是什么原因无法ptrace 上。

原文链接:http://blog.csdn.net/raintungli/article/details/7245709

【系列文章】

  1. Java开源工具在linux上的源码分析(一):跟踪方式
  2. Java开源工具在linux上的源码分析(二):信号处理
  3. Java开源工具在linux上的源码分析(三):执行的线程vm thread
  4. Java开源工具在linux上的源码分析(四):safe point
  5. Java开源工具在linux上的源码分析(六):符号表的读取
责任编辑:林师授 来源: raintungli的博客
相关推荐

2012-03-02 12:14:19

JavaJstackJmap

2012-03-02 12:20:21

Javajmapjstack

2012-03-02 12:31:50

Javajmapjstack

2012-03-02 12:25:07

Javajmapjstack

2012-03-02 13:29:38

Javajmapjstack

2022-06-26 18:09:43

Linux开源

2022-05-25 16:38:42

sudoLinuxroot 账户

2010-01-27 09:58:59

Linuxunix程序日志

2019-10-16 17:00:51

LinuxUbuntuVMware

2023-06-06 19:01:38

缩写ShellLinux

2021-03-09 11:25:04

Linux开源工具服务器

2012-05-22 00:28:21

JavaJava开源开源工具

2022-06-06 14:20:25

个人财务开源预算

2021-08-31 09:41:57

LinuxiPhone开源工具

2021-09-01 09:47:25

Linux 工具 开发

2020-05-09 12:01:40

Linux开源软件SDN

2022-12-06 17:18:42

2015-12-24 10:10:31

Slack开源工具IRC

2016-01-31 09:31:36

开源项目开源GitHub

2015-10-26 09:38:09

免费安全分析工具
点赞
收藏

51CTO技术栈公众号