若在 Linux Kernel 內,想追蹤每個函式的呼叫流程,第一個會想到的工具應該是 ftrace;那針對 user space 的程式是否有類似的東西?用 gdb,再加上一些簡單 script 就可以完成。
以 xdotool 為例,舉例來說,xdotool getmouselocation 將會得當前鼠標的位置,正常狀況下輸出結果如下:
$ xdotool getmouselocation findclient: 65011716 findclient: 65011716 x:2769 y:656 screen:0 window:65011716
若搭配本文將介紹的方法,輸出結果如下:
(gdb) r Starting program: /usr/bin/xdotool getmouselocation [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". | main at xdotool.c:287 | _start at ??:0 | __libc_start_main at ??:0 | xdotool_main at xdotool.c:290 | xdotool_main at xdotool.c:316 | args_main at xdotool.c:456 | args_main at xdotool.c:493 | xdo_new at xdo.c:89 | xdo_new_with_opened_display at xdo.c:106 | xdo_new_with_opened_display at xdo.c:131 | xdo_enable_feature at xdo.c:2053 | xdo_new_with_opened_display at xdo.c:140 | _xdo_populate_charcode_map at xdo.c:1360 | _keysym_to_char at xdo.c:1390 | args_main at xdotool.c:509 | context_execute at xdotool.c:519 | context_execute at xdotool.c:536 | cmd_getmouselocation at cmd_getmouselocation.c:3 | cmd_getmouselocation at cmd_getmouselocation.c:38 | consume_args at xdotool.c:46 | cmd_getmouselocation at cmd_getmouselocation.c:40 | xdo_mouselocation2 at xdo.c:858 | xdo_mouselocation2 at xdo.c:886 | xdo_window_find_client at xdo.c:1222 | xdo_getwinprop at xdo.c:1541 | xdo_mouselocation2 at xdo.c:889 | xdo_window_find_client at xdo.c:1222 | xdo_getwinprop at xdo.c:1541 | xdo_window_find_client at xdo.c:1241 | xdo_window_find_client at xdo.c:1222 | xdo_getwinprop at xdo.c:1541 | xdo_window_find_client at xdo.c:1241 | xdo_window_find_client at xdo.c:1222 | xdo_getwinprop at xdo.c:1541 findclient: 65011716 findclient: 65011716 | xdo_mouselocation2 at xdo.c:909 | _is_success at xdo.c:1533 | cmd_getmouselocation at cmd_getmouselocation.c:48 | xdotool_output at xdotool.c:583 x:2262 y:689 screen:0 window:65011716 | args_main at xdotool.c:511 | xdo_free at xdo.c:144
是不是整個程式內函式呼叫順序都一目瞭然。
快速開始
先安裝 debug symbol,可以參考這裡將 debug symbol 來源設好。 接著安裝這 2 個套件 libxdo2-dbgsym xdotool-dbgsym
$ sudo apt-get install libxdo2-dbgsym xdotool-dbgsym $ apt-get source xdotool $ cd path/to/xdotool/source $ wget https://gist.github.com/fcwu/7466813/raw/69daa7ad2227eb99e66b87e832e04ad8a8798385/supertrace.gdb $ wget https://gist.github.com/fcwu/7466823/raw/fb9fee4cf987aaa291bef7fb50959e9eb0f5247b/supertrace.py
準備工作完成。把 gdb 跑起來吧!
$ gdb -x supertrace.gdb --args xdotool getmouselocation (gdb) c Starting program: /usr/bin/xdotool getmouselocation [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". | main at xdotool.c:287 | _start at ??:0 | __libc_start_main at ??:0 | xdotool_main at xdotool.c:290 | xdotool_main at xdotool.c:316 | args_main at xdotool.c:456 | args_main at xdotool.c:493 以下略...
深入一點
GDB 指令檔:supertrace.gdb
裡頭會載入 supertrace.py (本文末),supertrace.py 內,提供了 2 個指令 breakall and supertrace 和 1 個函式 lastbp。breakall 會把所有有 debug info 的函式當做追蹤目標,指令 xdotool 因為安裝了 xdotool-dbgsym 確定有 debug info 外,shared library 的部分可以用 info sharedlibrary 來觀察:
(gdb) info sharedlibrary From To Syms Read Shared Object Library 0x00007ffff7ddaaf0 0x00007ffff7df4eba Yes (*) /lib64/ld-linux-x86-64.so.2 0x00007ffff7bd12d0 0x00007ffff7bd5f28 Yes /usr/lib/libxdo.so.2 0x00007ffff78d7570 0x00007ffff793ffe8 Yes (*) /lib/x86_64-linux-gnu/libm.so.6 0x00007ffff75b5a70 0x00007ffff763d6c8 Yes (*) /usr/lib/x86_64-linux-gnu/libX11.so.6 0x00007ffff73971e0 0x00007ffff739a6b8 Yes (*) /lib/x86_64-linux-gnu/librt.so.1 0x00007ffff6ff3fa0 0x00007ffff71385d0 Yes (*) /lib/x86_64-linux-gnu/libc.so.6 0x00007ffff6dd0360 0x00007ffff6dd2d88 Yes (*) /usr/lib/x86_64-linux-gnu/libXtst.so.6 0x00007ffff6bcc9f0 0x00007ffff6bcd318 Yes (*) /usr/lib/x86_64-linux-gnu/libXinerama.so.1 0x00007ffff69b7350 0x00007ffff69c36d8 Yes (*) /usr/lib/x86_64-linux-gnu/libxcb.so.1 0x00007ffff67aade0 0x00007ffff67ab918 Yes (*) /lib/x86_64-linux-gnu/libdl.so.2 0x00007ffff6592740 0x00007ffff659e358 Yes (*) /lib/x86_64-linux-gnu/libpthread.so.0 0x00007ffff637f490 0x00007ffff63894f8 Yes (*) /usr/lib/x86_64-linux-gnu/libXext.so.6 0x00007ffff6179d90 0x00007ffff617aae8 Yes (*) /usr/lib/x86_64-linux-gnu/libXau.so.6 0x00007ffff5f74090 0x00007ffff5f75aa8 Yes (*) /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (*): Shared library is missing debugging information.
值得注意的是若是觀察的 library 太多,會因為 debug info 太大,啟動速度變很慢。接著在 supertrace.gdb 裡用了 lastbp 函式取得最後的 breakpoint number,等會設 breakpoint command 會用。
最後,在 breakpoint command 裡,最重要的就是 supertrace 這個命令,它主要會收集 backtrace 的輸出,並拿來跟之前輸出比較,將最後結果印出。