關鍵詞: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()函數。結合代碼就不難發現問底根源了。
