Debug Hacks Conference 2009 (LD_PRELOAD を使った gdb 拡張)
とても面白かった。知らなかったことがあったのでさっそく家で試してみた。
素性の分からないファイルの調べ方
大岩さんの発表で rpm -qf で所属パッケージを調べ、rpm -ql で関連ファイルを調べるという方法が紹介されていた。ubuntu ではどうするのかと調べてみると、
- dpkg-query -S filename-search-pattern
- dpkg-query -L package-name
でいけた。
LD_PRELOAD を使った gdb 拡張
安部さんの発表では、GDB スクリプトではなく、C で複雑なことを記述する方法が紹介されていた。以下、ちょっとしたサンプルで試してみた(C++ で)。
data.h
#ifndef DATA_H_ #define DATA_H_ struct DATA { int a; int b; int c; }; #endif // DATA_H_
main.cpp
#include <cstdio> #include "data.h" int main() { DATA v; v.a = 1; v.b = 2; v.c = 3; v.c = v.a * v.b; printf("a = %d, b = %d, c = %d\n", v.a, v.b, v.c); }
debug_util.cpp
#include <cstdio> #include "data.h" void debug_start() __attribute__((constructor)); void debug_end() __attribute__((destructor)); void debug_start() { printf("debug_start\n"); } void debug_end() { printf("debug_end\n"); } void debug_info(DATA* p) { printf("debug_info: DATA %p = (a = %d, b = %d, c = %d)\n", p, p->a, p->b, p->c); }
これらを、
$ gcc -shared -fpic debug_util.cpp -o debug_util.so -lstdc++ $ gcc main.cpp -O0 -lstdc++ -o a.out.release $ strip a.out.release $ gcc main.cpp -O0 -lstdc++ -g -o a.out.debug
としてビルドした。
- a.out.release は -g 無しでリリースビルドされ strip されている
- -g ありでデバッグビルドした a.out.debug がある
- デバッグ用のユーティリティ関数を debug_util.so にまとめた
という想定。
$ LD_PRELOAD=debug_util.so gdb a.out.release ★ debug_util.so をプリロードして a.out.release をデバッグ debug_start ★ gdb の開始で発生したもの GNU gdb 6.8-debian Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu"... (no debugging symbols found) (gdb) b main Function "main" not defined. ★ strip されているのでブレイクポイントが張れない Make breakpoint pending on future shared library load? (y or [n]) n (gdb) symbol-file a.out.debug ★ debug ビルドの実行ファイルのシンボル情報を参照する Reading symbols from /home/iwagaki/dev/preload/a.out.debug...done. (gdb) b main ★ ブレイクポイントが張れるようになる Breakpoint 1 at 0x400570: file main.cpp, line 14. (gdb) run Starting program: /home/iwagaki/dev/preload/a.out.release debug_start ★ どちらかが a.out.release の開始で発生したもの (子プロセスに LD_PRELOAD が継承されている) debug_start ★ 2つ出る理由は分かりません Breakpoint 1, main () at main.cpp:14 14 v.a = 1; (gdb) n 15 v.b = 2; (gdb) n 16 v.c = 3; (gdb) n 18 v.c = v.a * v.b; (gdb) p v $1 = {a = 1, b = 2, c = 3} (gdb) call debug_info(&v) ★ PRE_LOAD した自前関数を呼び出して表示 debug_info: DATA 0x7fff69d25cb0 = (a = 1, b = 2, c = 3) $2 = 56 (gdb) c Continuing. a = 1, b = 2, c = 2 debug_end ★ a.out.release の終了で発生したもの Program exited normally. (gdb) quit debug_end ★ gdb の終了で発生したもの
複雑な構造体を表示したり、リスト構造をダンプしたり、色々とログファイルに残すなど、C++ でそのまま書けそう。ところで、symbol-file ではなく起動時に -s とかで指定してもうまくいかないのだけどなんでだっけ?