在 Linux 開發 kernel module 時,若發生不正常存取 (當掉),一般都可以印出 call trace,可以知道 function 的呼叫流程,最後死在哪個 function 裡。但這麼好用的功能在一般的 C 程式裡,確沒有!?
於是幾年前上網搜了一下怎麼讓一般程式當掉時也會印出來 call trace,還真的可以達到這個功能。但實際在工作上使用時,當掉好像也沒看到印出東西來。但範例是可以用的,就把這古老的回憶留給需要的人了。
這個也可以把它當做 kernel 裡的 dump_stack() 來用,看看是從哪邊呼叫到目前 function 的。
範例程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <execinfo.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> void dump(int signo) { void *array[30]; size_t size; char **strings; size_t i; size = backtrace (array, 30); strings = backtrace_symbols (array, size); fprintf (stderr,"Obtained %zd stack frames.nm", size); for (i = 0; i <= size; i++) fprintf (stderr,"%s\n", strings[i]); free (strings); exit(0); } Debug_Printf_FrameInfos() { signal(SIGSEGV, dump); } void func_c() { * ((volatile char *) 0x0) = 0x999; } void func_b() { func_c(); } void func_a() { func_b(); } int main() { Debug_Printf_FrameInfos(); func_a(); return 0; } |
編譯與測試
x86 環境
gcc -rdynamic a.c
ARM 環境
$(CROSS)gcc -funwind-tables -fasynchronous-unbind-tables a.c
執行結果
1 2 3 4 5 6 7 8 9 |
ubuntu@DESKTOP-J2NVRB7:~$ ./a.out Obtained 8 stack frames.nm./a.out(dump+0x38) [0x7f7af3a54221] /lib/x86_64-linux-gnu/libc.so.6(+0x46210) [0x7f7af3866210] ./a.out(func_c+0xd) [0x7f7af3a5430b] ./a.out(func_b+0x12) [0x7f7af3a54323] ./a.out(func_a+0x12) [0x7f7af3a54338] ./a.out(main+0x1c) [0x7f7af3a54357] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7f7af38470b3] ./a.out(_start+0x2e) [0x7f7af3a5412e] |
程式最後是當在 func_c() 裡面,而呼叫的流程也正確的印了出來 main() –> func_a() –> func_b() –> func_c() –> crash