Impala实践:解析glog打印的 C++ 报错堆栈

Impala实践:解析glog打印的 C++ 报错堆栈

Impala使用glog生成日志。生产环境用的都是release build,glog产生的报错堆栈里没有函数名,很难像Java报错堆栈那样方便定位问题。下面是 Impalad 日志中的一个报错:

I0522 09:07:16.002056 20222 status.cc:128] Snappy: RawUncompress failed
    @           0xae26c9
    @          0x107635b
    @          0x11b1f2d
    @          0x11b23ef
    @          0x11af96f
    @          0x11b096b
    @          0x11b2b31
    @          0x118644b
    @          0x11774c2
    @          0x1178805
    @          0x1100f31
    @          0x1101a79
    @          0x16a3449
    @     0x7f257f7ebea4
    @     0x7f257f5148dc
I0522 09:07:16.002146 20222 hdfs-scan-node.cc:512] Scan node (id=0) ran into a parse error for scan range abfss://xxx

从文本只能看出来是id=0的scan node在读ABFS上一个文件时发生了snappy解压失败,并不清楚具体是怎么发生的。好在如上的堆栈提供了代码地址,只要把可执行文件反汇编出来,很多函数名还是可以找到的。下面就以这个堆栈为例,介绍如何手动解析函数名。

1. 反汇编

首先找到impalad可执行文件,可以查看当前正在运行的impalad进程,在命令中找到可执行文件:

$ ps aux | grep impalad
impala     78554  8.0  0.2 10490904 630020 ?     Sl   04:42  12:48 /opt/cloudera/parcels/CDH-6.3.x-1.cdh6.3.x.p0.34479352/lib/impala/sbin-retail/impalad --flagfile=/var/run/cloudera-scm-agent/process/304-impala-IMPALAD/impala-conf/impalad_flags
root      133407  0.0  0.0  10692   976 pts/0    S+   07:20   0:00 grep --color=auto impalad

上面得到的可执行文件是 /opt/cloudera/parcels/CDH-6.3.x-1.cdh6.3.x.p0.34479352/lib/impala/sbin-retail/impalad。一般在CDH/CDP机器上,impalad可执行文件都在这个目录里: /opt/cloudera/parcels/CDH/lib/impala/sbin-retail/
其中 /opt/cloudera/parcels/CDH 是个软链,指向了当前生效的parcel解压目录。

把impalad可执行文件反汇编,使用 objdump 指令,把反汇编代码输出到 /tmp/impalad.asm

cd /opt/cloudera/parcels/CDH/lib/impala/sbin-retail/
objdump -drwC -Mintel impalad > /tmp/impalad.asm

2. 搜索指令地址

按堆栈里的地址在汇编代码里搜索。比如上面第一个地址是 0xae26c9,把前缀 0x 去掉,查找它+1的地址,即地址为ae26ca的代码(注:十六进制里 9+1=a,如果impala使用的是gcc-10及以上版本则不需要加1):
Impala实践:解析glog打印的 C++ 报错堆栈_第1张图片
这里的第一列就是代码地址,图里的第一行是函数的定义。0xae26ca是一个 lea 指令,其往上一行是个 call 指令,栈里保存的其实就是函数调用后的返回地址,也就是 call 指令的下一行地址0xae26ca。因此我们得知,最上一层是在调用 impala::GetStackTrace() 函数,所在函数是 impala::Status::Status(std::string const&)。

同理处理 0x107635b,它加一的地址为 0x107635c,也能在汇编代码里搜到:
在这里插入图片描述
它的上一个指令确实是调用 impala::Status::Status() 的 call 指令,当前指令所在的函数是

impala::SnappyDecompressor::ProcessBlock(bool, long, unsigned char const*, long*, unsigned char**)

再比如0x11b23ef,它加一的地址是 0x11b23f0,同样可以在反汇编代码中找到:
Impala实践:解析glog打印的 C++ 报错堆栈_第2张图片
所在函数是 impala::HdfsTextScanner::FillByteBuffer(impala::MemPool*, bool*, int)

用这样的方法可以手动解析出好几个函数名,这基本上就跟debug build得到的堆栈一样了:

      0xae26c9 impala::Status::Status(std::string const&)
     0x107635b impala::SnappyDecompressor::ProcessBlock(bool, long, unsigned char const*, long*, unsigned char**)
     0x11b1f2d impala::HdfsTextScanner::FillByteBufferCompressedFile(bool*)
     0x11b23ef impala::HdfsTextScanner::FillByteBuffer(impala::MemPool*, bool*, int)
     0x11af96f impala::HdfsTextScanner::FillByteBufferWrapper(impala::MemPool*, bool*, int)
     0x11b096b impala::HdfsTextScanner::ProcessRange(impala::RowBatch*, int*)
     0x11b2b31 impala::HdfsTextScanner::GetNextInternal(impala::RowBatch*)
     0x118644b impala::HdfsScanner::ProcessSplit()
     0x11774c2 impala::HdfsScanNode::ProcessSplit(std::vector > const&, impala::MemPool*, impala::io::ScanRange*, long*)
     0x1178805 impala::HdfsScanNode::ScannerThread(bool, long)
     0x1100f31 impala::Thread::SuperviseThread(…)
     0x1101a79 boost::detail::thread_data<…>::run()
     0x16a3449 thread_proxy
0x7f257f7ebea4
0x7f257f5148dc

但到 0x7f257f7ebea4 会发现找不到代码了,这个是动态链接的地址,只有在运行时把 so 文件加载进来才有。另外类似的还有 codegen 函数的地址,在反汇编代码里也是找不到的,因为 codegen 代码是运行时生成的。大部分指令还是能找到的,不影响使用。

3. 让 glog 打印函数名

其实release build也可以显示解析好函数的堆栈,只需要在启动参数里加上 --symbolize_stacktrace=true 就行了(这其实是 glog 的一个参数)。打开 symbolize_stacktrace 后,使用 release build 产生的报错也就跟 debug build 一样了:

I0723 21:19:43.712909 229706 status.cc:128] Snappy: RawUncompress failed
    @           0xae26c9  impala::Status::Status()
    @          0x107635b  impala::SnappyDecompressor::ProcessBlock()
    @          0x11b1f2d  impala::HdfsTextScanner::FillByteBufferCompressedFile()
    @          0x11b23ef  impala::HdfsTextScanner::FillByteBuffer()
    @          0x11af96f  impala::HdfsTextScanner::FillByteBufferWrapper()
    @          0x11b096b  impala::HdfsTextScanner::ProcessRange()
    @          0x11b2b31  impala::HdfsTextScanner::GetNextInternal()
    @          0x118644b  impala::HdfsScanner::ProcessSplit()
    @          0x11774c2  impala::HdfsScanNode::ProcessSplit()
    @          0x1178805  impala::HdfsScanNode::ScannerThread()
    @          0x1100f31  impala::Thread::SuperviseThread()
    @          0x1101a79  boost::detail::thread_data<>::run()
    @          0x16a3449  thread_proxy
    @     0x7fc522befe24  start_thread
    @     0x7fc522919bac  __clone

4. 总结

Impala C++代码的报错堆栈是使用glog打印的,可以从指令地址手动解析出函数名,也可以在启动参数里加 --symbolize_stacktrace=true 让 glog 打印带符号(函数名)的堆栈。

关注Apache Impala公众号,及时获取更多Impala资讯

你可能感兴趣的:(Impala,c++,impala)