strace 打印堆栈

时间:2024-05-22 19:17:43

平常初次定位问题时,我一般是先看看 api 调用,再不济就多看看堆栈,如用 gdb 的commands 指令在 每次断点时自动打印堆栈。

 

strace 有个实验性的功能,在打印每个系统调用的同时打印当时的堆栈,有了堆栈简直就是手握问题的钥匙。

strace -h
...
  -k             obtain stack trace between each syscall
...

but,but,but,but,but,but,but,but,but,but,but,but,

公司中开发环境的centos linux 上自带的 strace 没有编译对 -k 功能 的支持。只能自己从源代码中编译。

[strace-4.24]# ./configure --help
...
  --with-libdw            use libdw to implement stack tracing support

  --with-libunwind        use libunwind to implement stack tracing support
  --with-libiberty        use libiberty to demangle symbols in stack trace
...

首次编译时,只加了 --with-libunwind ,--with-libunwind 与 --with-libdw 功能是同样的,选其中一个就可以了,只是我对 libunwind 多点偏好而已。

CFLAGS="-O2 -fPIC" \
./configure \
  --prefix=/usr \
  --mandir=/usr/man \
  --with-libunwind

看看编译后新的 strace -k 去trace mysqld 的打印:

# strace -f -y -k -p 18297

...
[pid 10719] getrusage(RUSAGE_SELF, {ru_utime={tv_sec=102, tv_usec=148630}, ru_stime={tv_sec=39, tv_usec=239462}, ...}) = 0
 > /usr/lib64/libc-2.17.so(getrusage+0x7) [0xf5057]
 > /usr/libexec/mysqld(_ZN16PROF_MEASUREMENTC1EP13QUERY_PROFILEPKc+0x15) [0x42e065]
 > /usr/libexec/mysqld(_ZN13QUERY_PROFILE10new_statusEPKcS1_S1_j+0x46) [0x42e2c6]
 > /usr/libexec/mysqld(_ZN9PROFILING20finish_current_queryEv+0x28) [0x42e598]
 > /usr/libexec/mysqld(_Z16dispatch_command19enum_server_commandP3THDPcj+0x4cc) [0x37a21c]
 > /usr/libexec/mysqld(_Z24do_handle_one_connectionP3THD+0x1c2) [0x42da22]
 > /usr/libexec/mysqld(handle_one_connection+0x4a) [0x42daca]
 > /usr/lib64/libpthread-2.17.so(start_thread+0xc5) [0x7e25]
 > /usr/lib64/libc-2.17.so(clone+0x6d) [0xfebad]
...

不错了,可以启用 -k 功能打印出堆栈,但对于 c++ 程序中的堆栈中的名字没有做到 demangle ,像  _ZN16PROF_MEASUREMENTC1EP13QUERY_PROFILEPKc 这样的名字,可以用下面命令看到 demangle 后的真正函数签名

# c++filt _ZN16PROF_MEASUREMENTC1EP13QUERY_PROFILEPKc
PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE*, char const*)

 

回来,再次 编译 strace ,这次 加上 --with-libiberty

CFLAGS="-O2 -fPIC" \
./configure \
  --prefix=/usr \
  --mandir=/usr/man \
  --with-libunwind \
  --with-libiberty

.
.
.
checking whether to enable stack tracing support... yes, using libunwind
checking demangle.h usability... yes
checking demangle.h presence... yes
checking for demangle.h... yes
checking for cplus_demangle in -liberty... yes
checking libiberty/demangle.h usability... no
checking libiberty/demangle.h presence... no
checking for libiberty/demangle.h... no
configure: error: in `/tmp/strace-4.24':
configure: error: failed to find demangle.h
See `config.log' for more details

# echo $?
1

but, but, but, but, but, but, but, but, but, but, but, 

源码中的编译配置不正确,

怎么回事,failed to find demangle.h  ???

再看上一句,真正找不到的是 checking libiberty/demangle.h presence... no

看了一下我系统中的头文件

strace 打印堆栈

不对呀,这不是有 #include "libiberty.h" ,也有 cplus_demangle 等相关的 C++ demangle 函数。

再看一下源码中的 编译配置

strace 打印堆栈

好家伙,它同时 检查 demangle.h libiberty/demangle.h 这两个头文件存不存在,但上面  /usr/include/demangle.h 已经 include libiberty 相关的东西,由此看来这个 libiberty/demangle.h 是多余的,把它删除就可以了,而且我的centos 上安装的 binutils 包中没有

strace 打印堆栈

这么看来 很旧以前 binutls 包可能有 libiberty/demangle.h 这个文件,但现在的新版本没有了。

也可能由于 很多的 linux 发行版中的 strace 都不编译支持 -k 功能,所以这个管理员只就没有测试到 编译-k 功能的编译配置。

 

看看带 demangle 特性的 -k 功能输出:

# strace -f -y -k -p 18297

...
[pid  3792] getrusage(RUSAGE_SELF, {ru_utime={tv_sec=170, tv_usec=48683}, ru_stime={tv_sec=61, tv_usec=704638}, ...}) = 0
 > /usr/lib64/libc-2.17.so(getrusage+0x7) [0xf5057]
 > /usr/libexec/mysqld(PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE*, char const*, char const*, char const*, unsigned int)+0x28) [0x42e0a8]
 > /usr/libexec/mysqld(QUERY_PROFILE::new_status(char const*, char const*, char const*, unsigned int)+0x148) [0x42e3c8]
 > /usr/libexec/mysqld(set_thd_proc_info+0x2e) [0x341ece]
 > /usr/libexec/mysqld(dispatch_command(enum_server_command, THD*, char*, unsigned int)+0x2c6) [0x37a016]
 > /usr/libexec/mysqld(do_handle_one_connection(THD*)+0x1c2) [0x42da22]
 > /usr/libexec/mysqld(handle_one_connection+0x4a) [0x42daca]
 > /usr/lib64/libpthread-2.17.so(start_thread+0xc5) [0x7e25]
 > /usr/lib64/libc-2.17.so(clone+0x6d) [0xfebad]
...

> /usr/libexec/mysqld(PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE*, char const*, char const*, char const*, unsigned int)+0x28) 这样的打印,好看极了