2006年2月 5日

Cのプログラムの中でブレークポイントを設定する

Cのプログラムをデバッグする際には GDB などのデバッガが役立ちます。通常、ブレークポイントはデバッガの中から設定しますが、デバッグ対象のCのプログラムの中で設定することもできます。

 

Linux なら #include <signal.h> して、任意の箇所に

raise(SIGTRAP);

を挿入すれば OK です。 raise() 関数を用いて SIGTRAP シグナルを発生させています。

あるいは x86 限定なら

__asm__("int3");

でも OK です。ここでは SIGTRAP を発生させるために int3 (0xcc) 命令を埋め込んでいます。GDB もソフトウェア的にブレークポイントを設定するときは当該箇所に int3 を書き込んでいるので、やっていることは割と似ています (GDBの場合は int3 を書き込む部分の元のコードを保存しておいたりする必要がありますが)。

上のコードを仕込んでコンパイルしたバイナリを GDB 上で動かすと、当該箇所で処理が中断して制御が GDB に移るはずです。

このようなことをしたい場面はそれほど多くないと思いますが、実行時の特定のタイミングで処理を中断してデバッガで状態を見たり、マクロの中などデバッガでブレークポイントを設定しにくいところを調べるのに便利なのではないかと思います。

Glib のマクロ

知人からGlibには G_BREAKPOINT というマクロが定義されているよと教えてもらいました。以下のコードは Glib 2.6.4 の glib/gbacktrace.h から抜粋したものです。

/* Hacker macro to place breakpoints for selected machines.
 * Actual use is strongly discouraged of course ;)
 */
#if (defined (__i386__) || defined (__x86_64__)) && defined (__GNUC__) && __GNUC__ >= 2
#  define G_BREAKPOINT()        G_STMT_START{ __asm__ __volatile__ ("int $03"); }G_STMT_END
#elif defined (_MSC_VER) && defined (_M_IX86)
#  define G_BREAKPOINT()        G_STMT_START{ __asm int 3h }G_STMT_END
#elif defined (__alpha__) && !defined(__osf__) && defined (__GNUC__) && __GNUC__ >= 2
#  define G_BREAKPOINT()        G_STMT_START{ __asm__ __volatile__ ("bpt"); }G_STMT_END
#else   /* !__i386__ && !__alpha__ */
#  define G_BREAKPOINT()        G_STMT_START{ raise (SIGTRAP); }G_STMT_END
#endif  /* __i386__ */