1 |
__asm int 3; |
これを見てピンと来た人は何人いるだろうか。
ああ、x86系CPU環境でブレークポイントと同じ効果をもたらすアセンブラコードだなーと思った人。正解ですが本質的ではありません。
ああ、闇夜のc++とか言いながらついにネタ切れでc++の話以外を書き始めたなーと思った人。正解だし本質的です。
xcodeやVisual Studioで以下のコードを実行してみましょう。
1 2 3 4 5 6 7 8 |
#include <cstdio> int main() { std::printf("abc"); __asm int 3; std::printf("def"); return 0; } |
int 3というアセンブラ命令はデバッガへのトラップ用の割り込みを発生させるので、5行目で勝手にデバッガがブレークします。
そこからステップトレースも、再実行も可能です。
マクロを作っておくと何かと便利です。
1 2 |
// 条件が成立したらブレークするブレークポイントマクロ #define BREAKPOINT_IF(exp) do {if (exp) {__asm int 3;} } while(0) |
より汎用的に使うためにglibでは環境毎にマクロ定義を切り替えてます。
glibのgbacktrace.hを覗いてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * G_BREAKPOINT: * * Inserts a breakpoint instruction into the code. * * On x86 and alpha systems this is implemented as a soft interrupt * and on other architectures it raises a <literal>SIGTRAP</literal> signal. */ #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 (__DMC__)) && defined (_M_IX86) # define G_BREAKPOINT() G_STMT_START{ __asm int 3h }G_STMT_END #elif defined (_MSC_VER) # define G_BREAKPOINT() G_STMT_START{ __debugbreak(); }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__ */ |
環境依存ついでに、より環境依存な話をしておくとmsvc向けに__debugbreak();という命令もあります。やってることはint 3と一緒なので覚える必要はないでしょう。
環境依存なので3DS向けゲームの開発しかしてない人には無用の知識です。
まとめ
- c++について書くことが思いつかなくなった
- __asm int 3;はブレークポイント発生コード
- マクロ化しておくと使い勝手がいい
- 汎用的な実装がglibで提供されている