首页 > *nix技术, 跟踪调试 > 能否实现获取指定进程的调用堆栈信息?

能否实现获取指定进程的调用堆栈信息?

2012年6月30日 发表评论 阅读评论 9,574 次浏览

我们知道调用backtrace()可以获取当前进程的调用堆栈信息,那如果想要获取另外一个进程(我们当然知道这个进程的pid)的调用堆栈信息,是否有办法呢?

stackoverflow上,就有人提出了这个问题,有网友给出的答案是向该进程发出一个信号,然后由该进程在信号处理函数里获取自身的调用堆栈,然后再发回来…。先不管这种方法行或不行,但至少不适合一般情况,因为它需要在指定进程里进行信号处理。

想想gdb工具,gdb几乎可以attach到任何进程上,在无需其它进程做任何额外工作的情况下,获取它的当前调用堆栈信息,所以说,回到题目问题,能够实现获取指定进程的调用堆栈信息,而恰好就有这么一个特定的工具pstack:

[root@localhost ~]# pstack 4468
Thread 2 (Thread 0x4166f940 (LWP 4469)):
#0  0x00007fbcaf6e6b99 in pthread_cond_wait@@GLIBC_2.3.2 ()
#1  0x00007fbcb071e9fb in ?? () from /proc/4468/exe
#2  0x00007fbcaf6e24a7 in start_thread () from /lib64/libpthread.so.0
#3  0x00007fbcaf9cac2d in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7fbcb06f46e0 (LWP 4468)):
#0  0x00007fbcaf9cb018 in epoll_wait () from /lib64/libc.so.6
#1  0x00007fbcb072800c in ?? () from /proc/4468/exe
#2  0x00007fbcb0726fec in ?? () from /proc/4468/exe
#3  0x00007fbcb071d319 in main () from /proc/4468/exe
[root@localhost ~]# 

从上实例可以看到,pstack可以获取指定进程(通过进程id)的所有线程调用堆栈信息,怎么做到的呢?其实pstack就是一个利用gdb实现的shell脚本:

[root@localhost ~]# ls -l `which pstack`
lrwxrwxrwx 1 root root 6 Jul 14  2010 /usr/bin/pstack -> gstack
[root@localhost ~]# file /usr/bin/gstack 
/usr/bin/gstack: Bourne shell script text executable
[root@localhost ~]# cat !$
cat /usr/bin/gstack
#!/bin/sh

if test $# -ne 1; then
    echo "Usage: `basename $0 .sh` <process-id>" 1>&2
    exit 1
fi

if test ! -r /proc/$1; then
    echo "Process $1 not found." 1>&2
    exit 1
fi

# GDB doesn't allow "thread apply all bt" when the process isn't
# threaded; need to peek at the process to determine if that or the
# simpler "bt" should be used.

backtrace="bt"
if test -d /proc/$1/task ; then
    # Newer kernel; has a task/ directory.
    if test `/bin/ls /proc/$1/task | /usr/bin/wc -l` -gt 1 2>/dev/null ; then
	backtrace="thread apply all bt"
    fi
elif test -f /proc/$1/maps ; then
    # Older kernel; go by it loading libpthread.
    if /bin/grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; then
	backtrace="thread apply all bt"
    fi
fi

GDB=${GDB:-/usr/bin/gdb}

if $GDB -nx --quiet --batch --readnever > /dev/null 2>&1; then
    readnever=--readnever
else
    readnever=
fi

# Run GDB, strip out unwanted noise.
$GDB --quiet $readnever -nx /proc/$1/exe $1 <<EOF 2>&1 | 
$backtrace
EOF
/bin/sed -n \
    -e 's/^(gdb) //' \
    -e '/^#/p' \
    -e '/^Thread/p'
[root@localhost ~]# 

转载请保留地址:http://www.lenky.info/archives/2012/06/1765http://lenky.info/?p=1765


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

  1. 2012年7月6日06:45 | #1

    凯哥的博客写的很用心啊,格式清晰、写东西娓娓道来,学习!
    这里面的的最后用的是GDB的bt命令查看进程的堆栈,这个实现还可以深入研究下。

  1. 本文目前尚无任何 trackbacks 和 pingbacks.