gdb でおっかけ

see ぐあ間違えてリンクしてた - daily dayflower

ソースのパスの指定の仕方がわかったのでソースコードデバッグをしてみます。

$ gdb test

GNU gdb 6.8-debian
...... snip snip snip ......
This GDB was configured as "i486-linux-gnu"...

(gdb) set environment LD_LIBRARY_PATH=/usr/lib/debug

ソース検索パスを追加するには directory コマンドを使います。

(gdb) directory /home/dayflower/src/samba-3.0.28a/source
Source directories searched: /home/dayflower/src/samba-3.0.28a/source:$cdir:$cwd

個人的に lib/debug.cdebug_add_class() での挙動が怪しいとにらんでいるので breakpoint を設定します。

(gdb) b debug_add_class
Function "debug_add_class" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (debug_add_class) pending.

まだ共有ライブラリがロードされていないので,そのシンボルしらないけど,ペンディングしとく?と聞かれました。y を入力して,breakpoint を予約しておきます。

いざデバッグ

(gdb) start SMB_SERVER_NAME
Breakpoint 2 at 0x80484d5: file test.c, line 25.
Starting program: /home/dayflower/test SMB_SERVER_NAME
[Thread debugging using libthread_db enabled]
[New Thread 0xb78e06d0 (LWP 8393)]
[Switching to Thread 0xb78e06d0 (LWP 8393)]
main (argc=2, argv=0xbf849974) at test.c:25
25	    hp = gethostbyname(argv[1]);

んーなんか thread が生成されてますが,main の最初で止まりました。

breakpoint まで進めるために continue します。

(gdb) cont
Continuing.

Breakpoint 1, debug_add_class (classname=0xb789b6f9 "all") at lib/debug.c:304
304		if (!classname)

おお,無事 breakpoint で止まりました。

ソースはきちんと見れるでしょうか?list コマンドを使います。

(gdb) l
299	int debug_add_class(const char *classname)
300	{
301		int ndx;
302		void *new_ptr;
303	
304		if (!classname)
305			return -1;
306	
307		/* check the init has yet been called */
308		debug_init();

きちんと Samba の対応するソースが読み込めているらしいです。


なお,今回睨んでいる関数の先頭部分を引用しておきます。

int debug_add_class(const char *classname)
{
        int ndx;
        void *new_ptr;

        if (!classname)
                return -1;

        /* check the init has yet been called */
        debug_init();

        ndx = debug_lookup_classname_int(classname);
        if (ndx >= 0)
                return ndx;
        ndx = debug_num_classes;

        new_ptr = DEBUGLEVEL_CLASS;
        if (DEBUGLEVEL_CLASS == &debug_all_class_hack) {
                /* Initial loading... */
                new_ptr = NULL;
        }
        new_ptr = SMB_REALLOC_ARRAY(new_ptr, int, debug_num_classes + 1);

        /******* snip snip snip *******/

憶測としては,Initial loading なのに new_ptr = NULL; が実行されていないのではないか,というところです。なので realloc でエラーがでてしまうのではないか,と。


さてさて。同一ソース内でステップ実行するには next コマンドを使います。

(gdb) n
308		debug_init();

ちなみに,next コマンドで表示される行は,次に実行されるべき行です。

next でステップ実行していきます。

(gdb) n
310		ndx = debug_lookup_classname_int(classname);
(gdb) n
311		if (ndx >= 0)
(gdb) n
310		ndx = debug_lookup_classname_int(classname);
(gdb) n
311		if (ndx >= 0)

あれあれ?とくにループもないのに,行を後戻りしました。

よくわからないですが進めます。

(gdb) n
315		new_ptr = DEBUGLEVEL_CLASS;
(gdb) n
313		ndx = debug_num_classes;
(gdb) n
315		new_ptr = DEBUGLEVEL_CLASS;

あれ?またバックステップが発生しました。

(gdb) n
316		if (DEBUGLEVEL_CLASS == &debug_all_class_hack) {

ここの挙動がとくにあやしいと睨んでいるので,変数を表示させてみます。

print コマンドで変数を表示することができます。

(gdb) print DEBUGLEVEL_CLASS
$1 = (int *) 0xb78b670c
(gdb) print &debug_all_class_hack
$2 = (int *) 0xb78b670c
(gdb) print new_ptr
$3 = (void *) 0xb7ed906c

DEBUGLEVEL_CLASS&debug_all_class_hack は同一になっています。なので new_ptrNULL で初期化されるはずです。

でも new_ptr は現時点で DEBUGLEVEL_CLASS になっているはずなのに,すでにおかしいですね。

さて,next してみます。

(gdb) n
320		new_ptr = SMB_REALLOC_ARRAY(new_ptr, int, debug_num_classes + 1);

見事に new_ptr = NULL; がすっとばされました(泣)。

進めます。

(gdb) n
316		if (DEBUGLEVEL_CLASS == &debug_all_class_hack) {
(gdb) n
320		new_ptr = SMB_REALLOC_ARRAY(new_ptr, int, debug_num_classes + 1);
(gdb) n
*** glibc detected *** /home/dayflower/test: realloc(): invalid pointer: 0xb7ed906c ***
======= Backtrace: =========
/usr/lib/debug/libc.so.6[0xb7bf8c37]

...... snip snip snip ......

Program received signal SIGABRT, Aborted.
0xb7bbb1b6 in *__GI_raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64	../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
	in ../nptl/sysdeps/unix/sysv/linux/raise.c

またなぜかバックステップが発生して,その後 SMB_REALLOC_ARRAY() に進み,エラーが発生してしまいました。



おそらく,最適化が強くはたらいておりソースコードと一対一対応できてないのではないかなーと思います。それともスレッドのせい?

結局 libc:gethostbyname() が悪いのか debug_add_class() が悪いのかはわからないままです。

なーんとなく DEBUGLEVEL_CLASSvolatile 宣言すればいいような気もするのですが……