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解压失败,并不清楚具体是怎么发生的。好在如上的堆栈提供了代码地址,只要把可执行文件反汇编出来,很多函数名还是可以找到的。下面就以这个堆栈为例,介绍如何手动解析函数名。
首先找到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
按堆栈里的地址在汇编代码里搜索。比如上面第一个地址是 0xae26c9,把前缀 0x 去掉,查找它+1的地址,即地址为ae26ca的代码(注:十六进制里 9+1=a,如果impala使用的是gcc-10及以上版本则不需要加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::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 代码是运行时生成的。大部分指令还是能找到的,不影响使用。
其实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
Impala C++代码的报错堆栈是使用glog打印的,可以从指令地址手动解析出函数名,也可以在启动参数里加 --symbolize_stacktrace=true 让 glog 打印带符号(函数名)的堆栈。
关注Apache Impala公众号,及时获取更多Impala资讯