glibc中提供了backtrace()和backtrace_symbols()两个函数来输出和解析程序的call stack,
输出程序运行时调用栈信息
可以通过命令man backtrace
查看具体帮忙信息。
#include <execinfo.h>
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
- 使用backtrace()函数获取调用栈,返回获取到的调用栈个数,结果放到传入的指针数组buffer里面;
- 调用backtrace_symbols()把获取的指针数组和数组中调用栈个数传递给该函数,
会返回一个新的指针数组,里面是已经转换成符号表的调用栈信息;
用完后记得需要free返回的指针变量指向的内存空间; - 使用这两个函数需要包含execinfo.h头文件;
下面把这个过程封装成了一个宏函数方便以后使用;
#ifndef __DEBUG_HPP__
#define __DEBUG_HPP__
#include <iostream>
#include <execinfo.h>
/* include <execinfo.h> to use this macro */
#define DBG_ASSERT(x) do { \
if (x) { break; } \
std::cout << "\r\n------ file:" << __FILE__ << " line:" << __LINE__ << " ------" << std::endl;\
void *pptrace_raw[32] = {0}; \
char **pptrace_str = NULL; \
int trace_num = 0, iloop = 0; \
trace_num = backtrace(pptrace_raw, 32); \
pptrace_str = (char **)backtrace_symbols(pptrace_raw, trace_num); \
for (iloop=0; iloop<trace_num; iloop++) { std::cout << pptrace_str[iloop] << std::endl; }\
if (pptrace_str) { delete pptrace_str; } \
} while (0);
#endif
当应用出现异常的时候可以获取一次调用栈,然后再退出,方便问题分析和定位;
例如当出现段错误时往往会收到SIGSEGV信号,这时可以提前注册接收SIGSEGV信号并打印调用栈,然后再恢复默认信号处理;
/* 1. register handler of signal SIGSEGV */
signal(SIGSEGV, sigsegvhandle);
/* 2. define signal handler and reset signal handler to default at then end of handler function */
void sigsegvhandle(int signo) {
std::cout << "sigsegvhandle received signal: " << signo << std::endl;
/* output callstack */
DBG_ASSERT(0);
/* reset signal handle to default */
signal(signo, SIG_DFL);
/* will receive SIGSEGV again and exit app */
}
应用示例
/*****************************************
* Copyright (C) 2018 * Ltd. All rights reserved.
* Created date: 2018-08-02 19:18:13
*******************************************/
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <execinfo.h>
/* include <execinfo.h> to use this macro */
#define DBG_ASSERT(x) do { \
if (x) { break; } \
std::cout << "###### file:" << __FILE__ << " line:" << __LINE__ << " ######" << std::endl;\
void *pptrace_raw[32] = {0}; \
char **pptrace_str = NULL; \
int trace_num = 0, iloop = 0; \
trace_num = backtrace(pptrace_raw, 32); \
pptrace_str = (char **)backtrace_symbols(pptrace_raw, trace_num); \
for (iloop=0; iloop<trace_num; iloop++) { std::cout << pptrace_str[iloop] << std::endl; } \
if (pptrace_str) { delete pptrace_str; } \
} while (0);
void sigsegv_test()
{
std::cout << __func__ << " begin" << std::endl;
char *buff = NULL;
buff[1] = buff[1]; /* will crash here */
std::cout << __func__ << " end" << std::endl;
}
void sigsegvhandle(int signo) {
std::cout << "sigsegvhandle received signal: " << signo << std::endl;
/* output callstack */
DBG_ASSERT(0);
/* reset signal handle to default */
signal(signo, SIG_DFL);
/* will receive SIGSEGV again and exit app */
}
int main() {
/* register handler of signal SIGSEGV */
signal(SIGSEGV, sigsegvhandle);
sleep(5);
sigsegv_test();
return 0;
}
编译执行
$ g++ vec.cpp -o vec
$ ./vec
后会有以下输出
sigsegv_test begin
sigsegvhandle received signal: 11
###### file:backtrace.cpp line:63 ######
./vec(+0xca4) [0x55e418312ca4]
/lib/x86_64-linux-gnu/libc.so.6(+0x3f040) [0x7f48cf8df040]
./vec(+0xb5d) [0x55e418312b5d]
./vec(+0xd95) [0x55e418312d95]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f48cf8c1bf7]
./vec(+0xa2a) [0x55e418312a2a]
Segmentation fault (core dumped)
根据以上调用栈并结合程序的符号表就可以查出具体的调用信息;
解析函数调用栈
首先使用nm命令导出编译符号表
注意nm命令对应编译时使用的gcc/g++工具链;
比如以上程序编译为vec
$ nm -n vec > vec.symbol.txt
$ cat vec.symbol.txt
U backtrace@@GLIBC_2.2.5
U backtrace_symbols@@GLIBC_2.2.5
U __cxa_atexit@@GLIBC_2.2.5
w __cxa_finalize@@GLIBC_2.2.5
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@@GLIBC_2.2.5
U signal@@GLIBC_2.2.5
U sleep@@GLIBC_2.2.5
U __stack_chk_fail@@GLIBC_2.4
U _ZdlPvm@@CXXABI_1.3.9
U _ZNSolsEi@@GLIBCXX_3.4
U _ZNSolsEPFRSoS_E@@GLIBCXX_3.4
U _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
U _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@@GLIBCXX_3.4
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4
0000000000000918 T _init
0000000000000a00 T _start
0000000000000a30 t deregister_tm_clones
0000000000000a70 t register_tm_clones
0000000000000ac0 t __do_global_dtors_aux
0000000000000b00 t frame_dummy
0000000000000b0a T _Z12sigsegv_testv
0000000000000b9d T _Z13sigsegvhandlei
0000000000000d71 T main
0000000000000d9c t _Z41__static_initialization_and_destruction_0ii
0000000000000de5 t _GLOBAL__sub_I__Z12sigsegv_testv
0000000000000e00 T __libc_csu_init
0000000000000e70 T __libc_csu_fini
0000000000000e74 T _fini
0000000000000e80 R _IO_stdin_used
0000000000000e88 r _ZStL19piecewise_construct
0000000000000ee8 r _ZZ12sigsegv_testvE8__func__
0000000000000ef8 r __GNU_EH_FRAME_HDR
00000000000010dc r __FRAME_END__
0000000000201d40 t __frame_dummy_init_array_entry
0000000000201d40 t __init_array_start
0000000000201d50 t __do_global_dtors_aux_fini_array_entry
0000000000201d50 t __init_array_end
0000000000201d58 d _DYNAMIC
0000000000201f58 d _GLOBAL_OFFSET_TABLE_
0000000000202000 D __data_start
0000000000202000 W data_start
0000000000202008 D __dso_handle
0000000000202010 B __bss_start
0000000000202010 D _edata
0000000000202010 D __TMC_END__
0000000000202020 B _ZSt4cout@@GLIBCXX_3.4
0000000000202130 b completed.7698
0000000000202131 b _ZStL8__ioinit
0000000000202138 B _end
由于C++有符号修饰(Name Decoration)或符号改编(Name Mangling)的机制;
可以使用-C
选项来导出更易读的符号表
$ nm -nC vec > vec.symbol.txt
$ cat vec.symbol.txt
U backtrace@@GLIBC_2.2.5
U backtrace_symbols@@GLIBC_2.2.5
U __cxa_atexit@@GLIBC_2.2.5
w __cxa_finalize@@GLIBC_2.2.5
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@@GLIBC_2.2.5
U signal@@GLIBC_2.2.5
U sleep@@GLIBC_2.2.5
U __stack_chk_fail@@GLIBC_2.4
U operator delete(void*, unsigned long)@@CXXABI_1.3.9
U std::ostream::operator<<(int)@@GLIBCXX_3.4
U std::ostream::operator<<(std::ostream& (*)(std::ostream&))@@GLIBCXX_3.4
U std::ios_base::Init::Init()@@GLIBCXX_3.4
U std::ios_base::Init::~Init()@@GLIBCXX_3.4
U std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)@@GLIBCXX_3.4
U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@@GLIBCXX_3.4
0000000000000918 T _init
0000000000000a00 T _start
0000000000000a30 t deregister_tm_clones
0000000000000a70 t register_tm_clones
0000000000000ac0 t __do_global_dtors_aux
0000000000000b00 t frame_dummy
0000000000000b0a T sigsegv_test()
0000000000000b9d T sigsegvhandle(int)
0000000000000d71 T main
0000000000000d9c t __static_initialization_and_destruction_0(int, int)
0000000000000de5 t _GLOBAL__sub_I__Z12sigsegv_testv
0000000000000e00 T __libc_csu_init
0000000000000e70 T __libc_csu_fini
0000000000000e74 T _fini
0000000000000e80 R _IO_stdin_used
0000000000000e88 r std::piecewise_construct
0000000000000ee8 r sigsegv_test()::__func__
0000000000000ef8 r __GNU_EH_FRAME_HDR
00000000000010dc r __FRAME_END__
0000000000201d40 t __frame_dummy_init_array_entry
0000000000201d40 t __init_array_start
0000000000201d50 t __do_global_dtors_aux_fini_array_entry
0000000000201d50 t __init_array_end
0000000000201d58 d _DYNAMIC
0000000000201f58 d _GLOBAL_OFFSET_TABLE_
0000000000202000 D __data_start
0000000000202000 W data_start
0000000000202008 D __dso_handle
0000000000202010 B __bss_start
0000000000202010 D _edata
0000000000202010 D __TMC_END__
0000000000202020 B std::cout@@GLIBCXX_3.4
0000000000202130 b completed.7698
0000000000202131 b std::__ioinit
0000000000202138 B _end
注意:调用栈的地址是实际运行时映射的虚拟地址;
获取运行时进程内存映射关系
可以使用 cat /proc/(程序运行时的PID号)/maps
获取运行时内存映射关系;
每个调用栈地址解析的时候需要减去对应段映射地址的首地址,然后再到导出的符号表中查找;
如下为运行时的maps信息
$ ps -ef | pgrep vec |xargs -i cat /proc/{}/maps
55e418312000-55e418314000 r-xp 00000000 08:71 1048919 /mnt/home_tmp/tmp/vec
55e418513000-55e418514000 r--p 00001000 08:71 1048919 /mnt/home_tmp/tmp/vec
55e418514000-55e418515000 rw-p 00002000 08:71 1048919 /mnt/home_tmp/tmp/vec
55e419d3e000-55e419d5f000 rw-p 00000000 00:00 0 [heap]
7f48cf2ea000-7f48cf301000 r-xp 00000000 08:01 21495879 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f48cf301000-7f48cf500000 ---p 00017000 08:01 21495879 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f48cf500000-7f48cf501000 r--p 00016000 08:01 21495879 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f48cf501000-7f48cf502000 rw-p 00017000 08:01 21495879 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f48cf502000-7f48cf69f000 r-xp 00000000 08:01 21496080 /lib/x86_64-linux-gnu/libm-2.27.so
7f48cf69f000-7f48cf89e000 ---p 0019d000 08:01 21496080 /lib/x86_64-linux-gnu/libm-2.27.so
7f48cf89e000-7f48cf89f000 r--p 0019c000 08:01 21496080 /lib/x86_64-linux-gnu/libm-2.27.so
7f48cf89f000-7f48cf8a0000 rw-p 0019d000 08:01 21496080 /lib/x86_64-linux-gnu/libm-2.27.so
7f48cf8a0000-7f48cfa87000 r-xp 00000000 08:01 21496033 /lib/x86_64-linux-gnu/libc-2.27.so
7f48cfa87000-7f48cfc87000 ---p 001e7000 08:01 21496033 /lib/x86_64-linux-gnu/libc-2.27.so
7f48cfc87000-7f48cfc8b000 r--p 001e7000 08:01 21496033 /lib/x86_64-linux-gnu/libc-2.27.so
7f48cfc8b000-7f48cfc8d000 rw-p 001eb000 08:01 21496033 /lib/x86_64-linux-gnu/libc-2.27.so
7f48cfc8d000-7f48cfc91000 rw-p 00000000 00:00 0
7f48cfc91000-7f48cfe0a000 r-xp 00000000 08:01 31458490 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f48cfe0a000-7f48d000a000 ---p 00179000 08:01 31458490 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f48d000a000-7f48d0014000 r--p 00179000 08:01 31458490 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f48d0014000-7f48d0016000 rw-p 00183000 08:01 31458490 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f48d0016000-7f48d001a000 rw-p 00000000 00:00 0
7f48d001a000-7f48d0043000 r-xp 00000000 08:01 21495852 /lib/x86_64-linux-gnu/ld-2.27.so
7f48d01f7000-7f48d01fd000 rw-p 00000000 00:00 0
7f48d0243000-7f48d0244000 r--p 00029000 08:01 21495852 /lib/x86_64-linux-gnu/ld-2.27.so
7f48d0244000-7f48d0245000 rw-p 0002a000 08:01 21495852 /lib/x86_64-linux-gnu/ld-2.27.so
7f48d0245000-7f48d0246000 rw-p 00000000 00:00 0
7fff0e3a6000-7fff0e3c7000 rw-p 00000000 00:00 0 [stack]
7fff0e3d8000-7fff0e3db000 r--p 00000000 00:00 0 [vvar]
7fff0e3db000-7fff0e3dd000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
计算偏移地址并到符号表中查找
然后根据调用栈的地址到符号表地址中查找,
注意符号表中的地址表示某个符号(函数)开始的位置;
如对于 ./vec(+0xca4) [0x55e418312ca4] 地址对应的符号应该是
0000000000000b9d T sigsegvhandle(int)
因为 ./vec(+0xca4) [0x55e418312ca4] 实际对应的符号表地址应该是 0x55e418312ca4-0x55e418312000 即 0xca4 地址落在这两个
0000000000000b9d T sigsegvhandle(int)
0000000000000d71 T main
之间,说明0xca4是属于 sigsegvhandle(int) 函数内的;
以此类推可以得到调用关系,和我们代码的实际调用关系是一致的(由于C++编译时符号表函数名会被修改添加一些参数类型信息);
sigsegv_test begin
sigsegvhandle received signal: 11
###### file:backtrace.cpp line:63 ######
./vec(+0xca4) [0x55e418312ca4] sigsegvhandle(int)
/lib/x86_64-linux-gnu/libc.so.6(+0x3f040) [0x7f48cf8df040]
./vec(+0xb5d) [0x55e418312b5d] sigsegv_test()
./vec(+0xd95) [0x55e418312d95] main
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f48cf8c1bf7]
./vec(+0xa2a) [0x55e418312a2a] _start
Segmentation fault (core dumped)
解析库文件的符号表
如果需要解析库文件中的符号,也可以使用nm命令获取库文件对应的符号表;
也需要有程序运行时的内存映射关系;
如果需要解析 /lib/x86_64-linux-gnu/libc.so.6(+0x3f040) [0x7f48cf8df040]
则需要首先减去对应的偏移地址即 0x7f48cf8df040 - 0x7f48cf8a0000 然后再到 libc.so.6 对应的符号表中查询;
由于 libc.so.6 不是我们自己编译的库,因此在这里不再继续解析了;
如果是自己编译的库文件则可以继续;
自动获取进程的maps信息
下面增加一个函数 getmapsinfo(),用于自动获取运行时的 maps 信息
需要包含以下头文件
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
用于获取pid及读文件操作;
/*****************************************
* Copyright (C) 2018 * Ltd. All rights reserved.
* Created date: 2018-08-02 19:18:13
*******************************************/
#include <iostream>
#include <signal.h>
#include <execinfo.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
/* include <execinfo.h> to use this macro */
#define DBG_ASSERT(x) do { \
if (x) { break; } \
std::cout << "###### file:" << __FILE__ << " line:" << __LINE__ << " ######" << std::endl;\
void *pptrace_raw[32] = {0}; \
char **pptrace_str = NULL; \
int trace_num = 0, iloop = 0; \
trace_num = backtrace(pptrace_raw, 32); \
pptrace_str = (char **)backtrace_symbols(pptrace_raw, trace_num); \
for (iloop=0; iloop<trace_num; iloop++) { std::cout << pptrace_str[iloop] << std::endl; } \
if (pptrace_str) { delete pptrace_str; } \
} while (0);
void sigsegv_test()
{
std::cout << __func__ << " begin" << std::endl;
char *buff = NULL;
buff[1] = buff[1]; /* will crash here */
std::cout << __func__ << " end" << std::endl;
}
void getmapsinfo()
{
pid_t pid = getpid();
std::string mapsfile = "/proc/";
mapsfile.append(std::to_string(pid));
mapsfile.append("/maps");
std::cout << "##############################################" << std::endl;
std::cout << "pid: " << pid << "maps: " << mapsfile << std::endl;
int fd = open(mapsfile.c_str(), O_RDONLY);
if (fd == -1) {
std::cout << "open error: " << strerror(errno) << std::endl;
return;
}
int len = 0;
char buff[200] = {0};
while ((len = read(fd, buff, sizeof(buff)-1)) > 0) {
buff[len] = '\0';
std::cout << buff;
}
std::cout << "##############################################" << std::endl;
}
void sigsegvhandle(int signo) {
std::cout << "sigsegvhandle received signal: " << signo << std::endl;
/* output callstack */
DBG_ASSERT(0);
getmapsinfo();
/* reset signal handle to default */
signal(signo, SIG_DFL);
/* will receive SIGSEGV again and exit app */
}
int main() {
/* register handler of signal SIGSEGV */
signal(SIGSEGV, sigsegvhandle);
sleep(5);
sigsegv_test();
return 0;
}
新的符号表如下
$ nm -nC vec > vec.symbol.txt
$ cat vec.symbol.txt
U backtrace@@GLIBC_2.2.5
U backtrace_symbols@@GLIBC_2.2.5
U __cxa_atexit@@GLIBC_2.2.5
U __cxa_begin_catch@@CXXABI_1.3
U __cxa_end_catch@@CXXABI_1.3
w __cxa_finalize@@GLIBC_2.2.5
U __cxa_rethrow@@CXXABI_1.3
U __errno_location@@GLIBC_2.2.5
U getpid@@GLIBC_2.2.5
w __gmon_start__
U __gxx_personality_v0@@CXXABI_1.3
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@@GLIBC_2.2.5
U open@@GLIBC_2.2.5
U read@@GLIBC_2.2.5
U signal@@GLIBC_2.2.5
U sleep@@GLIBC_2.2.5
U __stack_chk_fail@@GLIBC_2.4
U strerror@@GLIBC_2.2.5
U _Unwind_Resume@@GCC_3.0
U vsnprintf@@GLIBC_2.2.5
U operator delete(void*, unsigned long)@@CXXABI_1.3.9
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::c_str() const@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_data() const@@GLIBCXX_3.4.21
U std::allocator<char>::allocator()@@GLIBCXX_3.4
U std::allocator<char>::~allocator()@@GLIBCXX_3.4
U std::allocator<char>::~allocator()@@GLIBCXX_3.4
U std::ostream::operator<<(int)@@GLIBCXX_3.4
U std::ostream::operator<<(std::ostream& (*)(std::ostream&))@@GLIBCXX_3.4
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_capacity(unsigned long)@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Alloc_hider::_Alloc_hider(char*, std::allocator<char> const&)@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_local_data()@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_set_length(unsigned long)@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_S_copy_chars(char*, char*, char*)@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::append(char const*)@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::append(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_data(char*)@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)@@GLIBCXX_3.4.21
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@@GLIBCXX_3.4.21
U std::ios_base::Init::Init()@@GLIBCXX_3.4
U std::ios_base::Init::~Init()@@GLIBCXX_3.4
U std::__throw_logic_error(char const*)@@GLIBCXX_3.4
U std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)@@GLIBCXX_3.4
U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@@GLIBCXX_3.4.21
U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@@GLIBCXX_3.4
00000000000015a0 T _init
0000000000001850 T _start
0000000000001880 t deregister_tm_clones
00000000000018c0 t register_tm_clones
0000000000001910 t __do_global_dtors_aux
0000000000001950 t frame_dummy
000000000000195a T sigsegv_test()
00000000000019ed T getmapsinfo()
0000000000001ccf T sigsegvhandle(int)
0000000000001ea8 T main
0000000000001ed3 t __static_initialization_and_destruction_0(int, int)
0000000000001f1c t _GLOBAL__sub_I__Z12sigsegv_testv
0000000000001f31 W std::__cxx11::to_string(int)
0000000000001f93 W std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned long, char const*, __va_list_tag*), unsigned long, char const*, ...)
0000000000002134 W std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Alloc_hider::~_Alloc_hider()
0000000000002134 W std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Alloc_hider::~_Alloc_hider()
0000000000002150 W std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<char*, void>(char*, char*, std::allocator<char> const&)
0000000000002150 W std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<char*, void>(char*, char*, std::allocator<char> const&)
00000000000021c8 W void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*)
0000000000002224 W void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct_aux<char*>(char*, char*, std::__false_type)
0000000000002280 W void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag)
00000000000023b7 W bool __gnu_cxx::__is_null_pointer<char>(char*)
00000000000023c9 W std::iterator_traits<char*>::iterator_category std::__iterator_category<char*>(char* const&)
00000000000023d3 W std::iterator_traits<char*>::difference_type std::distance<char*>(char*, char*)
0000000000002435 W std::iterator_traits<char*>::difference_type std::__distance<char*>(char*, char*, std::random_access_iterator_tag)
0000000000002460 T __libc_csu_init
00000000000024d0 T __libc_csu_fini
00000000000024d4 T _fini
00000000000024e0 R _IO_stdin_used
00000000000024e8 r std::piecewise_construct
00000000000025d8 r sigsegv_test()::__func__
00000000000025e8 r __GNU_EH_FRAME_HDR
00000000000029f4 r __FRAME_END__
0000000000202c48 t __frame_dummy_init_array_entry
0000000000202c48 t __init_array_start
0000000000202c58 t __do_global_dtors_aux_fini_array_entry
0000000000202c58 t __init_array_end
0000000000202c60 d _DYNAMIC
0000000000202e70 d _GLOBAL_OFFSET_TABLE_
0000000000203000 D __data_start
0000000000203000 W data_start
0000000000203008 D __dso_handle
0000000000203010 V DW.ref.__gxx_personality_v0
0000000000203018 B __bss_start
0000000000203018 D _edata
0000000000203018 D __TMC_END__
0000000000203020 B std::cout@@GLIBCXX_3.4
0000000000203130 b completed.7698
0000000000203131 b std::__ioinit
0000000000203138 B _end
运行信息如下
sigsegv_test begin
sigsegvhandle received signal: 11
###### file:backtrace.cpp line:63 ######
./vec(+0x1dd6) [0x55c4c441add6]
/lib/x86_64-linux-gnu/libc.so.6(+0x3f040) [0x7fdfa940c040]
./vec(+0x19ad) [0x55c4c441a9ad]
./vec(+0x1ecc) [0x55c4c441aecc]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7fdfa93eebf7]
./vec(+0x187a) [0x55c4c441a87a]
##############################################
pid: 6784maps: /proc/6784/maps
55c4c4419000-55c4c441c000 r-xp 00000000 08:71 1048638 /mnt/home_tmp/tmp/vec
55c4c461b000-55c4c461c000 r--p 00002000 08:71 1048638 /mnt/home_tmp/tmp/vec
55c4c461c000-55c4c461d000 rw-p 00003000 08:71 1048638 /mnt/home_tmp/tmp/vec
55c4c5650000-55c4c5671000 rw-p 00000000 00:00 0 [heap]
7fdfa902f000-7fdfa91cc000 r-xp 00000000 08:01 21496080 /lib/x86_64-linux-gnu/libm-2.27.so
7fdfa91cc000-7fdfa93cb000 ---p 0019d000 08:01 21496080 /lib/x86_64-linux-gnu/libm-2.27.so
7fdfa93cb000-7fdfa93cc000 r--p 0019c000 08:01 21496080 /lib/x86_64-linux-gnu/libm-2.27.so
7fdfa93cc000-7fdfa93cd000 rw-p 0019d000 08:01 21496080 /lib/x86_64-linux-gnu/libm-2.27.so
7fdfa93cd000-7fdfa95b4000 r-xp 00000000 08:01 21496033 /lib/x86_64-linux-gnu/libc-2.27.so
7fdfa95b4000-7fdfa97b4000 ---p 001e7000 08:01 21496033 /lib/x86_64-linux-gnu/libc-2.27.so
7fdfa97b4000-7fdfa97b8000 r--p 001e7000 08:01 21496033 /lib/x86_64-linux-gnu/libc-2.27.so
7fdfa97b8000-7fdfa97ba000 rw-p 001eb000 08:01 21496033 /lib/x86_64-linux-gnu/libc-2.27.so
7fdfa97ba000-7fdfa97be000 rw-p 00000000 00:00 0
7fdfa97be000-7fdfa97d5000 r-xp 00000000 08:01 21495879 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fdfa97d5000-7fdfa99d4000 ---p 00017000 08:01 21495879 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fdfa99d4000-7fdfa99d5000 r--p 00016000 08:01 21495879 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fdfa99d5000-7fdfa99d6000 rw-p 00017000 08:01 21495879 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fdfa99d6000-7fdfa9b4f000 r-xp 00000000 08:01 31458490 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7fdfa9b4f000-7fdfa9d4f000 ---p 00179000 08:01 31458490 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7fdfa9d4f000-7fdfa9d59000 r--p 00179000 08:01 31458490 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7fdfa9d59000-7fdfa9d5b000 rw-p 00183000 08:01 31458490 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7fdfa9d5b000-7fdfa9d5f000 rw-p 00000000 00:00 0
7fdfa9d5f000-7fdfa9d88000 r-xp 00000000 08:01 21495852 /lib/x86_64-linux-gnu/ld-2.27.so
7fdfa9f3c000-7fdfa9f42000 rw-p 00000000 00:00 0
7fdfa9f88000-7fdfa9f89000 r--p 00029000 08:01 21495852 /lib/x86_64-linux-gnu/ld-2.27.so
7fdfa9f89000-7fdfa9f8a000 rw-p 0002a000 08:01 21495852 /lib/x86_64-linux-gnu/ld-2.27.so
7fdfa9f8a000-7fdfa9f8b000 rw-p 00000000 00:00 0
7ffec92d2000-7ffec92f3000 rw-p 00000000 00:00 0 [stack]
7ffec9389000-7ffec938c000 r--p 00000000 00:00 0 [vvar]
7ffec938c000-7ffec938e000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
##############################################
Segmentation fault (core dumped)
然后根据编译导出的符号表和maps信息解析即可得到对应的调用关系。
打印调用栈的时候即打印符号名
-rdynamic
是一个连接选项,它指示连接器把所有符号(而不仅仅只是程序已使用到的外部符号)都添加到动态符号表(即.dynsym表)里,以便那些通过 dlopen() 或 backtrace() (这一系列函数使用.dynsym表内符号)这样的函数使用。
加-rdynamic
选项编译程序,然后使用 readelf 命令查看:
$ g++ vec.cpp -o vec -rdynamic
$ readelf -s vec
Symbol table '.dynsym' contains 78 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno_location@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSaIcED2Ev@GLIBCXX_3.4 (3)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strerror@GLIBC_2.2.5 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_begin_catch@CXXABI_1.3 (5)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNKSt7__cxx1112basic_str@GLIBCXX_3.4.21 (4)
7: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZSt4endlIcSt11char_trait@GLIBCXX_3.4 (3)
9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND vsnprintf@GLIBC_2.2.5 (2)
11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZSt19__throw_logic_error@GLIBCXX_3.4 (3)
13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND open@GLIBC_2.2.5 (2)
14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.2.5 (2)
15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
16: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
17: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@GLIBC_2.2.5 (2)
18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZStlsIcSt11char_traitsIc@GLIBCXX_3.4.21 (4)
19: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZStlsISt11char_traitsIcE@GLIBCXX_3.4 (3)
20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZdlPvm@CXXABI_1.3.9 (6)
21: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSolsEPFRSoS_E@GLIBCXX_3.4 (3)
22: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
23: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSaIcED1Ev@GLIBCXX_3.4 (3)
24: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNKSt7__cxx1112basic_str@GLIBCXX_3.4.21 (4)
25: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (7)
26: 0000000000000000 0 FUNC GLOBAL DEFAULT UND signal@GLIBC_2.2.5 (2)
27: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
28: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
29: 0000000000000000 0 FUNC GLOBAL DEFAULT UND read@GLIBC_2.2.5 (2)
30: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_rethrow@CXXABI_1.3 (5)
31: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getpid@GLIBC_2.2.5 (2)
32: 0000000000000000 0 FUNC GLOBAL DEFAULT UND backtrace@GLIBC_2.2.5 (2)
33: 0000000000000000 0 FUNC GLOBAL DEFAULT UND backtrace_symbols@GLIBC_2.2.5 (2)
34: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt8ios_base4InitC1Ev@GLIBCXX_3.4 (3)
35: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_end_catch@CXXABI_1.3 (5)
36: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __gxx_personality_v0@CXXABI_1.3 (5)
37: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSolsEi@GLIBCXX_3.4 (3)
38: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
39: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _Unwind_Resume@GCC_3.0 (8)
40: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSaIcEC1Ev@GLIBCXX_3.4 (3)
41: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
42: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
43: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
44: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
45: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
46: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
47: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt7__cxx1112basic_stri@GLIBCXX_3.4.21 (4)
48: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4 (3)
49: 0000000000002558 43 FUNC GLOBAL DEFAULT 14 main
50: 0000000000002b84 0 FUNC GLOBAL DEFAULT 15 _fini
51: 0000000000001c58 0 FUNC GLOBAL DEFAULT 11 _init
52: 000000000000237f 473 FUNC GLOBAL DEFAULT 14 _Z13sigsegvhandlei
53: 0000000000204018 0 NOTYPE GLOBAL DEFAULT 25 __bss_start
54: 0000000000002b90 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used
55: 0000000000204138 0 NOTYPE GLOBAL DEFAULT 25 _end
56: 0000000000002800 120 FUNC WEAK DEFAULT 14 _ZNSt7__cxx1112basic_stri
57: 00000000000027e4 27 FUNC WEAK DEFAULT 14 _ZNSt7__cxx1112basic_stri
58: 0000000000204000 0 NOTYPE GLOBAL DEFAULT 24 __data_start
59: 0000000000002a79 10 FUNC WEAK DEFAULT 14 _ZSt19__iterator_category
60: 00000000000027e4 27 FUNC WEAK DEFAULT 14 _ZNSt7__cxx1112basic_stri
61: 0000000000002643 416 FUNC WEAK DEFAULT 14 _ZN9__gnu_cxx12__to_xstri
62: 0000000000204018 0 NOTYPE GLOBAL DEFAULT 24 _edata
63: 0000000000002b80 2 FUNC GLOBAL DEFAULT 14 __libc_csu_fini
64: 0000000000002a67 18 FUNC WEAK DEFAULT 14 _ZN9__gnu_cxx17__is_null_
65: 0000000000001f00 43 FUNC GLOBAL DEFAULT 14 _start
66: 0000000000002b10 101 FUNC GLOBAL DEFAULT 14 __libc_csu_init
67: 000000000000209d 738 FUNC GLOBAL DEFAULT 14 _Z11getmapsinfov
68: 00000000000025e1 98 FUNC WEAK DEFAULT 14 _ZNSt7__cxx119to_stringEi
69: 0000000000002800 120 FUNC WEAK DEFAULT 14 _ZNSt7__cxx1112basic_stri
70: 0000000000204000 0 NOTYPE WEAK DEFAULT 24 data_start
71: 000000000000200a 147 FUNC GLOBAL DEFAULT 14 _Z12sigsegv_testv
72: 0000000000002ae5 28 FUNC WEAK DEFAULT 14 _ZSt10__distanceIPcENSt15
73: 0000000000002878 91 FUNC WEAK DEFAULT 14 _ZNSt7__cxx1112basic_stri
74: 0000000000204020 272 OBJECT GLOBAL DEFAULT 25 _ZSt4cout@GLIBCXX_3.4 (3)
75: 00000000000028d4 91 FUNC WEAK DEFAULT 14 _ZNSt7__cxx1112basic_stri
76: 0000000000002930 311 FUNC WEAK DEFAULT 14 _ZNSt7__cxx1112basic_stri
77: 0000000000002a83 98 FUNC WEAK DEFAULT 14 _ZSt8distanceIPcENSt15ite
可以看到添加 -rdynamic 选项后,.dynsym表就包含了所有的符号,不仅是已使用到的外部动态符号,还包括本程序内定义的符号等。
.dynsym表里的数据并不能被strip掉,即强制strip将导致程序无法执行.
然后重新运行,输出如下
sigsegv_test begin
sigsegvhandle received signal: 11
###### file:vec.cpp line:61 ######
./vec(_Z13sigsegvhandlei+0x107) [0x5623de873486]
/lib/x86_64-linux-gnu/libc.so.6(+0x3f040) [0x7f3658699040]
./vec(_Z12sigsegv_testv+0x53) [0x5623de87305d]
./vec(main+0x24) [0x5623de87357c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f365867bbf7]
./vec(_start+0x2a) [0x5623de872f2a]
##############################################
可以发现backtrace打印的调用栈已经包含了对应的符号名,
如 ./vec(_Z13sigsegvhandlei+0x107) [0x5623de873486]
但是这个符号名是C++运行时标识名与C++源码中的符号名并不一样;
这是由于符号改编(Name Mangling)的机制造成的。
demangle
将C++ ABI标识符(C++ ABI identifier)转换成C++源程序标识符(original C++ source identifier)的过程称为demangle。
更简单的说,识别C++编译以后的函数名的过程,就叫demangle。
c++filt以及nm,objdump等命令具有demangle的能力。如c++filt命令
$ c++filt _Z13sigsegvhandlei
sigsegvhandle(int)
在libstdc++里关于abi命名空间的文档中( https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/namespaces.html ),
介绍了GCC所使用的跨厂商(cross-vendor) C++ ABI,
其中暴露的一个函数 abi::__cxa_demangle 就是用于demangling。
//#include <cxxabi.h>
std::string demangle(const char* symbol)
{
std::string result;
char temp[128] = { 0 };
do {
//try to demangle a c++ name
if (1 <= sscanf(symbol, "%*[^(]%*[^_]%127[^)+]", temp)) {
//#include <cxxabi.h>
size_t size;
int status;
char* demangled;
demangled = abi::__cxa_demangle(temp, NULL, &size, &status);
if (NULL != demangled) {
result.assign(demangled);
free(demangled);
break;
}
}
//try to get a regular c symbol
if (1 <= sscanf(symbol, "%127s", temp)) {
result.assign(temp);
break;
}
//just return the symbol
result.assign(symbol);
} while (0);
return result;
}