-
backtrace
-
簡介
- 有的系統沒有實現
backtrace
這個函數. - 常見的就有
linux
還有一些docker
的庫.
- 有的系統沒有實現
-
判斷是否支持
backtrace
#include<stdio.h> #include<dlfcn.h> int main() { void * bt = dlsym(NULL,"backtrace"); if(NULL != bt) { printf("support\n"); } else { printf("not support\n"); } return 0; }
-
輸出
[root@localhost bt_support]# gcc test.c -ldl -g [root@localhost bt_support]# ./a.out support
-
-
堆棧
-
說明
- 查看某一個時刻,函數調用棧.
- 在 崩潰 的時候輸出,可以精確的查看在什麼地方出的問題.
-
常見工具
pstack -p pid
查看正在執行的進程某一時刻的堆棧.gdb
.
-
案例
(gdb) bt #0 show () at test.c:3 #1 0x0000000000400501 in cool () at test.c:7 #2 0x0000000000400511 in main () at test.c:12
-
注意
- 必須要有調試信息.
- 否則結果是
??:00
這類無意義數據.
-
-
獲取堆棧
-
說明
- 堆棧一般是代碼地址.
- 也可能是共享庫的映射地址.
-
獲取地址
#include <execinfo.h> void show() { void * s[30] = {0}; backtrace(s,30); return; } void cool() { show(); } int main() { cool(); }
輸出
(gdb) bt #0 show () at test.c:6 #1 0x0000000000400587 in cool () at test.c:11 #2 0x0000000000400597 in main () at test.c:16 (gdb) x /3xg s 0x7fffffffe320: 0x0000000000400576 0x0000000000400587 0x7fffffffe330: 0x0000000000400597 (gdb) p show $2 = {void ()} 0x40053d <show>
backtrace
獲取到的地址和gdb bt
指令獲取到的一樣.
addr2line
[root@localhost bt_support]# addr2line -fap 0x0000000000400576 0x0000000000400587 0x0000000000400597 0x0000000000400576: show at /root/cfile/bt_support/test.c:6 0x0000000000400587: cool at /root/cfile/bt_support/test.c:12 0x0000000000400597: main at /root/cfile/bt_support/test.c:17
- 得到的結果基本相同.
-
-
動態庫
-
獲取堆棧
test.c
#define _GNU_SOURCE #include <dlfcn.h> #include <execinfo.h> void show() { void * s[30] = {0}; int n = backtrace(s,30); int i; Dl_info info; for(i = 0 ; i < n; i++) { dladdr(s[i],&info); i+=0; } return; } void cool() { show(); }
main.c
extern void cool(); int main() { cool(); }
編譯
gcc -fPIC -shared -o test.so test.c -g gcc ./test.so main.c -g -ldl
調試輸出
(gdb) p info $1 = {dli_fname = 0x7ffff7ff9640 "./test.so", dli_fbase = 0x7ffff7bd9000, dli_sname = 0x7ffff7bd942d "show", dli_saddr = 0x7ffff7bd9785 <show>} (gdb) printf "%x\n",info.dli_saddr - info.dli_fbase 785 (gdb) !addr2line -e ./test.so 806 /root/cfile/bt_support/test.c:21 (gdb) bt #0 show () at test.c:10 #1 0x00007ffff7bd9806 in cool () at test.c:20 #2 0x000000000040063b in main () at main.c:4
addr
信息需要藉助dladdr
.獲取某個地址的信息.
-
-
實現
backtrace
-
參考鏈接
-
應用場景
- 有的會沒有這個函數,比如
android,docker
.
- 有的會沒有這個函數,比如
-
堆棧
#include <unwind.h> struct BacktraceState { void** current; void** end; }; static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) { BacktraceState* state = static_cast<BacktraceState*>(arg); uintptr_t pc = _Unwind_GetIP(context); if (pc) { if (state->current == state->end) { return _URC_END_OF_STACK; } else { *state->current++ = reinterpret_cast<void*>(pc); } } return _URC_NO_REASON; } } size_t captureBacktrace(void** buffer, size_t max) { BacktraceState state = {buffer, buffer + max}; _Unwind_Backtrace(unwindCallback, &state); return state.current - buffer; }
struct BacktraceState
,頭尾指針的數組.存放void*
類型數據的指針數組._Unwind_Reason_Code
回調函數返回值.struct _Unwind_Context* context
回調函數第一參數.void* arg
用戶用於存放數據的結構體,任意類型.unwindCallback
自定義的回調函數,傳入堆棧信息content
,和用戶的容器指針._Unwind_GetIP
獲取棧指針._Unwind_Backtrace
實際函數,回調和容器.
-
pipeline
-
-
backtrace
實現-
原型
-
實現
#include<stdio.h> #include <unwind.h> struct BacktraceState { void** start; void** current; void** end; }; static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) { BacktraceState* state = (BacktraceState*)(arg); _Unwind_Ptr pc = _Unwind_GetIP(context); if (pc) { if (state->current == state->end) { return _URC_END_OF_STACK; } if(NULL != state->start) { *state->current = (void*)(pc); } state->current++; } return _URC_NO_REASON; } int Backtrace(void** buffer, int size) { BacktraceState state = {buffer, buffer, buffer + size}; _Unwind_Backtrace(unwindCallback, &state); return state.current - state.start; } int main() { void * s[10] = {NULL}; Backtrace(s,10); return 0; }
- 編譯
gcc -g backtrace_demo.c
-
-
backtrace + addr2line
-
說明
- 將地址轉成可讀.
-
案例
#include<stdio.h> #include<unwind.h> typedef struct BacktraceState { void** start; void** current; void** end; } BacktraceState; static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) { BacktraceState* state = (BacktraceState*)(arg); _Unwind_Ptr pc = _Unwind_GetIP(context); if (pc) { if (state->current == state->end) { return _URC_END_OF_STACK; } if(NULL != state->start) { *state->current = (void*)(pc); } state->current++; } return _URC_NO_REASON; } int Backtrace(void** buffer, int size) { BacktraceState state = {buffer, buffer, buffer + size}; _Unwind_Backtrace(unwindCallback, &state); return state.current - state.start; } int show() { void * s[10] = {NULL}; int i = 0 , n = Backtrace(s,10); for( i = 0 ; i < n ;i ++) { printf("%p\n",s[i]); } return 0; } int main() { show(); return 0; }
編譯
gcc backtrace_demo.c -g -o demo.out ./demo.out | addr2line -fapCe ./demo.out
-
-
最終版本
-
簡介
- 利用
Backtrace
獲取堆棧,再通過dladdr
辨別是否爲可執行文件代碼. - 最後通過
addr2line
將結果輸出.
- 利用
-
案例
#define _GNU_SOURCE #include<stdio.h> #include <unistd.h> #include<stdlib.h> #include<unwind.h> #include<dlfcn.h> typedef struct BacktraceState { void** start; void** current; void** end; } BacktraceState; static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) { BacktraceState* state = (BacktraceState*)(arg); _Unwind_Ptr pc = _Unwind_GetIP(context); if (pc) { if (state->current == state->end) { return _URC_END_OF_STACK; } if(NULL != state->start) { *state->current = (void*)(pc); } state->current++; } return _URC_NO_REASON; } int Backtrace(void** buffer, int size) { BacktraceState state = {buffer, buffer, buffer + size}; _Unwind_Backtrace(unwindCallback, &state); return state.current - state.start; } void * main_base; extern int main(); void __attribute__((constructor)) ms() { Dl_info info; dladdr(main,&info); main_base = info.dli_fbase; } int show() { void * s[10] = {NULL}; int i = 0 , n = Backtrace(s,10); for( i = 0 ; i < n ;i ++) { char buf[100]={0}; Dl_info info; if(dladdr(s[i],&info)) { sprintf(buf,"addr2line -fpia -e %s %p",info.dli_fname,info.dli_fbase == main_base ? (unsigned long)s[i] : s[i] - info.dli_fbase); } //printf("%s %p\n",buf,s[i]); system(buf); } printf("\n"); return 0; } int cool() { show(); return 0; }
test.c
extern int cool(); int main() { cool(); return 0; }
-
編譯
gcc -fPIC backtrace_demo.c -shared -o backtrace_demo.so -ldl -g gcc ./backtrace_demo.so test.c -ldl -g
-
輸出
[root@localhost chap01]# ./a.out 0x0000000000000ab0: Backtrace at xxxxxx/backtrace_demo.c:31 0x0000000000000b2f: show at xxxxxx/backtrace_demo.c:46 0x0000000000000c1a: cool at xxxxxx/backtrace_demo.c:64 0x000000000040063b: main at xxxxxx/test.c:5 0x0000000000022555: __libc_start_main at /usr/src/debug/glibc-2.17-c758a686/csu/../csu/libc-start.c:300 0x0000000000400569: _start at ??:?
-
anddoird 實現backtrace
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.