Debug Hacks Conference 2009 (LD_PRELOAD を使った gdb 拡張)

http://atnd.org/events/539

とても面白かった。知らなかったことがあったのでさっそく家で試してみた。

素性の分からないファイルの調べ方

大岩さんの発表で 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 とかで指定してもうまくいかないのだけどなんでだっけ?