生产环境用的都是release build,C++代码产生的报错堆栈里没有函数名,很难像Java报错堆栈那样方便定位问题。下面是一个常见的启动报错,一般在CLASSPATH设置有误时发生:
I0619 19:13:00.951988 5279 status.cc:129] Failed to find JniUtil class.
@ 0xfceac4
@ 0x1a63a65
@ 0xfb206f
@ 0x1767faa
@ 0xdb91f4
@ 0x7f1de9361c87
@ 0xe5956a
这里好在报错信息说了是JniUtil类找不到,大概能定位问题。但有时候报错信息有限,还是得知道堆栈才好定位。如上的堆栈其实提供了代码地址,只要把可执行文件反汇编出来,很多函数名还是可以找到的。下面就以这个堆栈为例,介绍如何手动解析函数名。
首先把impalad可执行文件反汇编,直接用 objdump 指令的 -d 选项:
objdump -d be/build/release/service/impalad > impalad.asm
然后按堆栈里的地址在汇编代码里搜索。比如上面第一个地址是 0xfceac4,把前缀 0x 去掉,可以搜到如下代码:
这里的第一列就是代码地址,0xfceac4的上一行是个 callq 指令,栈里保存的其实就是函数调用后的返回地址,也就是 callq 指令的下一行地址。因此我们得知,最上一层是在调用 _ZN6impala13GetStackTraceB5cxx11Ev 函数。
用同样的方法可以手动解析出好几个函数名:
_ZN6impala13GetStackTraceB5cxx11Ev
_ZN6impala6StatusC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
_ZN6impala7JniUtil4InitEv
_ZN6impala17InitCommonRuntimeEiPPcbNS_8TestInfo4ModeEb
_Z11ImpaladMainiPPc
但到 0x7f1de9361c87 会发现找不到代码了,这个是动态链接的地址,只有在运行时把 so 文件加载进来才有。另外类似的还有 codegen 函数的地址,在反汇编代码里也是找不到的,因为 codegen 代码是运行时生成的。
跳过0x7f1de9361c87,下一个地址是 0xe5956a,这个在反汇编代码里还能找到:
从这里可以看到,当前函数是 _start,调用了 __libc_start_main@GLIBC_2.2.5。因此 0x7f1de9361c87 其实就是 __libc_start_main@GLIBC_2.2.5。
把手动解析的函数名堆起来,我们得到
_ZN6impala13GetStackTraceB5cxx11Ev
_ZN6impala6StatusC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
_ZN6impala7JniUtil4InitEv
_ZN6impala17InitCommonRuntimeEiPPcbNS_8TestInfo4ModeEb
_Z11ImpaladMainiPPc
__libc_start_main@GLIBC_2.2.5
_start
这些是符号,还可以解析成更可读的形式。网上搜 “demangle online”,随便找一个网站,如 http://demangler.com/
把上面的堆栈贴进去解析,就得到:
impala::GetStackTrace[abi:cxx11]()
impala::Status::Status(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
impala::JniUtil::Init()
impala::InitCommonRuntime(int, char**, bool, impala::TestInfo::Mode, bool)
ImpaladMain(int, char**)
__libc_start_main@GLIBC_2.2.5
_start
这基本上就跟debug build得到的堆栈一样了。另外,其实release build也可以显示解析好函数的堆栈,只需要在启动函数里加上 --symbolize_stacktrace=true 就行了(这其实是 glog 的一个参数)。打开 symbolize_stacktrace 后,使用 release build 产生的报错也就跟 debug build 一样了:
I0619 19:15:49.289554 6652 status.cc:129] Failed to find JniUtil class.
@ 0xfceac4 impala::Status::Status()
@ 0x1a63a65 impala::JniUtil::Init()
@ 0xfb206f impala::InitCommonRuntime()
@ 0x1767faa ImpaladMain()
@ 0xdb91f4 main
@ 0x7f749db4fc87 __libc_start_main
@ 0xe5956a _start
总结
Impala C++代码的报错堆栈是使用glog打印的,可以从函数地址手动解析出函数名,也可以在启动参数里加 --symbolize_stacktrace=true 让 glog 打印带符号(函数名)的堆栈。