关键词:coredump、maps、objdump、gdb等等。
最近遇到一个应用异常问题定位(Floating point exception/SIGFPE),说明是一个算术运算异常。
这种问题通常也比较简单:通过ulimit -c打开coredump;SIGFPE会触发coredump;然后gdb分析coredump文件即可。
这里的目的是记录一下过程,以及过程中做的一些优化点。主要就是增加了记录maps,更准确定位问题点。
1. 设置coredump
将coredump文件输出到/tmp目录下:
echo "/tmp/core-%e-%p" > /proc/sys/kernel/core_pattern
打开coredump选项:
ulimit -c unlimited
在系统遇到异常之后,就可能在/tmp/core-<comm>-<pid>文件。
2. 程序中记录maps信息
coredump文件中的backtrace能精确到函数,然后给一个发生问题的确切地址。
但是这个地址是在进程中的虚拟地址,这个地址和可执行文件的对应关系,需要maps来确定。
所以如果在程序开始记录自身的maps信息,能更有效定位问题。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/prctl.h> #define SELF_MAPS "/proc/self/maps" void dump_self_maps(void) { FILE *self_fd, *output_fd; char buf[1024], output_name[32], process_name[16]; memset(buf, 0x0, sizeof(buf)); memset(output_name, 0x0, sizeof(output_name)); prctl(PR_GET_NAME, process_name); snprintf(output_name, sizeof(output_name), "/tmp/maps_%s_%d.txt", process_name, getpid()); self_fd=fopen(SELF_MAPS,"r"); if(self_fd==NULL) { perror("open file"); exit(0); } output_fd=fopen(output_name,"w"); if(output_fd==NULL) { perror("open file"); exit(0); } while(fgets(buf,sizeof(buf),self_fd)!=NULL) { fputs(buf,output_fd); //printf("%s", buf); } fclose(output_fd); fclose(self_fd); } void main(void) { dump_self_maps(); }
就会生成/tmp/maps-<comm>-<pid>.txt文件,通过pid可以和core文件匹配上。
3. 定位实例
定位过程如下记录。
3.1 执行程序
执行程序后发现如下异常:
Floating point exception
这个信息说明收到了信号SIGFPE,是一种算术运算错误。
3.2 查看coredump文件
sudo gdb <bin> <core>
然后查看backtrace信息:
从#2可以看出是div0错误,然后最接近的是#3,说明问题出在IFMS_USBHostSend()中。
但是在IFMS_USBHostSend()函数哪里呢?
3.3 结合maps查看最直接问题点
如果想看到更直接的问题点,就需要通过maps信息来确定。
首先看地址0xb6bb4a12,看其落在maps的那个区域:
可以看出这个地址在libusb230.so中,然后计算偏移量:
0xb6bb4a12 - 0xb6bb0000 = 0x4a12
所以最直接问题点在libusb230.so文件的0x4a12处。
通过objdump -d <bin/lib>,然后找到地址0x4a12:
通过反汇编结果可以看出问题点在__aeabi_uidivmod()函数。结合代码就不难发现问底根源了。