« Jenkinsのjobが正常終了に見えてDirectoryNotEmptyException を出してた・・・ | トップページ | Effekseerのエフェクト描画を一時停止する »

2016.03.05

VC++の__debugbreak()が中々便利だ

| |コメント (0)|トラックバック (0)

 このエントリーをはてなブックマークに追加

ゲームエンジン・アーキテクチャ 第2版を読んでて
「3.3.3.3 アサート」に書かれている部分が役立ちそうに思った。
(この本は、分厚いので、気になるところからちょこっとずつ読んでいる)

アサート自体は、自動テストをする仕事だと使うんだけど
初音ミク冒険記では全然使っていなかったんだよね。

特に「デバッガへ処理を移すインラインのアセンブリ」が
便利そうなので調べてみた。

どうやらVC++だと以下の命令が該当するみたい。

DebugBreak();
__debugbreak();

この命令をソースの中に書いておくと
デバッガのブレイクポイントを指定していなくても
ブレイクポイントに到達したと判断されるので
Visual Studioから初音ミク冒険記を起動していると
そこでステップ実行のデバッグができるようになる。

Visual Studioを起動していない場合に発生したら
エラーのメッセージボックスが表示されるので
その間にVisual Studioを起動し
メニュー「デバッグ」-「プロセスにアタッチ」で
デバッグ対象のアプリケーションをHatsuneMikuBoukenki.exeにして
アタッチすればデバッグできるようになる。

ちなみに上の2つの命令はどちらも似たような動きだが
__debugbreak();が32ビット、64ビットどちらにも対応しており
ARMにも対応していると明記されているので
かげさんは__debugbreak();を使うことにした。

ということで、
本にある記載を初音ミク冒険記で使うとこんな感じになる。

#if ASSERTIONS_ENABLED
	// 式をチェックしてfalseであれば失敗する
	#define ASSERT(expr) \
		if (expr) { } \
		else \
		{ \
			debugOut(DOUT_Console, DLV_NormalError, DCAT_Etc, "Assertion failed! in SourceFile=[%s] Line=%d\n", __FILE__, __LINE__); \
			__debugbreak(); \
		}
#else
	#define ASSERT(expr)	// 何も起こらない
#endif

これで#define ASSERTIONS_ENABLED 1
と書けば条件のthenブロックが動き、
#define ASSERTIONS_ENABLED 1をコメントアウトすれば
elseブロックが動き、何も起こらないようになる。

debugOutは、以前、かげさんが作ったデバッグ用の関数だ。
これも上述の本の9.1 ロギングとトレースを参考に作っている。

デバッグ出力をもう少し改良しようかな?

__debugbreak()の話ではないけど
debugOutと初音ミク冒険記のログの仕組みについても
書いておこう。

■第1引数:コンソールに出力
 初音ミク冒険記のデバッグでは
 「早速脱線してデバッグ用コンソールを表示してみた
 に書いたコンソールが自動的に起動するようにしている。
 ここに出力する。

■第2引数:通常のエラー
 アサートは、式のチェック結果が、真か偽かチェックするので
 エラーとは限らないのだけど、debugOutではエラー系の情報は、
 第1引数の出力先の他にも
 ログファイルにミラーリングするようになってるから
 後から情報を確認するときにも役に立つ。

■第3引数:ログ出力カテゴリ
 ログファイルは、目的のログを探しやすいようカテゴリ分けしている。
 今回は、カテゴリをその他としている。
 初音ミク冒険記では、下図のようなカテゴリに分けてる。
 
Photo

 ちょっと、多すぎな気もするが、複数カテゴリにまたがる確認の時は
 カテゴリ分けされていると便利なのだ。
 上図だとその他にチェックが付いていないので普段だと出力されないが、
 エラー系の情報は、別のデバッグオプションで
 カテゴリのチェックの有無にかかわらず出力されるようにしてる。

Photo_3

 ログが分割されていると時系列で追いにくくなるが
 そこはカテゴリログの他にサマリーログというのを作っているので
 カテゴリ別のログに書かれたものは
 全体のサマリーログに時系列で書き込まれるって寸法になっている。

■第4引数以降:ログ出力する内容
 ここはprintfとかと同じ書式で設定できる。
 ポイントは__FILE__と__LINE__で
 それぞれ、ソースファイル名と行番号を出力する。
 今回作ったASSERTはマクロ関数なので
 ASSERT(チェックしたい式);と書いたソースと行番号が勝手に入ってくれる。

こんな感じでコンソールやログファイルに情報を出力した上で
デバッガで止まってくれる。

なので、想定していない条件になっていないかチェックしたいところで
ASSERT(チェックしたい式);と書けば
条件に引っかかった時にデバッグができるようになるので便利だ。

早速、マップ描画で時々変になるパターンで
もしかしたらと思いついたチェック式を書いて実行してみたら
該当するパターンでデバッガに切り替えれた。

ブレイクポイントを貼って止めるのも良いんだけど
ブレイクポイントが多くなってくると
鬱陶しくて全部のブレイクポイントを外すことがあるので
ブレイクポイントをつけてなくても気になる判定で
デバッガに移れるというのは、再現性が低いテストをしやすくなるので
とても便利そうに思った。


 このエントリーをはてなブックマークに追加

| |コメント (0)|トラックバック (0)

このエントリーへのリンク

このエントリーのリンクを入れるHTML:

トラックバック

この記事へのトラックバックの一覧です: VC++の__debugbreak()が中々便利だ:

コメント

このブログの新着コメントをRSSリーダに登録する為のxml




←名前とメールアドレスは必須です。
URLも記入すれば、URLのみが公開されます。
メールアドレスのみですと、メールアドレスが公開されてしまいますので、御注意ください。

↓コメント本文では、「a href」「b」「i」「br/」「p」「strong」「em」「ul」「ol」「li」「blockquote」「pre」のタグが使えます。絵文字をクリックすると、本文にタグを挿入できます。