純規の暇人趣味ブログ

広くて浅い知識の湖

[VisualStudio]さまざまな「ブレークポイント」の使い方

      HimaJyun

ブレークポイント、あるのが当たり前なので当たり前に使っていますけど、ブレークポイントが使えない時とか捗らないったらありゃしない。

動作が怪しい箇所をブレークポイントで挟んで前後の変数の変化を見るとか言う普通のやり方も、ブレークポイントが無ければprintデバッグする羽目に……

今回はVisualStudioのブレークポイントをより便利に使いこなすいくつかのテクニックを集めてみました。

ブレークポイント

IDEが持つ機能の一つ、そう「ブレークポイント」、コード内の所定の位置に到達した際に意図的に動作を一時停止させるアレ

もはや説明の必要はありませんね。(と言うのをブレークポイントの説明記事で言う矛盾)

普通に使う

「デバッグ->ブレークポイントの設定/解除」からも一応操作出来ますけど「F9」キーでやった方が早いです。
visualstudio-breakpoint-001設定されてあるブレークポイントを削除する時も同じように「F9」

作成されたブレークポイントにカーソルを合わせて「ブレークポイントを無効にする」を選べば一時的に無効に出来ます。
visualstudio-breakpoint-002

でもって、設定した位置までプログラムが進行すると、ピタッと停止する訳です。(どう言う仕組みかは知りませんが)
visualstudio-breakpoint-003

実は実行中にも設定/解除出来る

小ネタと言うか、単に自分が知らなかっただけなんでしょうけど、ブレークポイントは実行(デバッグ実行)中にも設定/解除が出来ます。

実行中にポチっと、いつも通りF9で設定すれば、プログラムがそこを通りかかった時にキチンと作用します。(当然ですが、一度しか通らないコード(例えば初期化処理とか)はそこに差し掛かる前に設定しておかないと無意味ですよ)

一体どう言う仕組みで実現しているのかは良く分かりませんが、止めたい時に止めたい場所で止められるので便利です。

てっきりexeに何かを仕込む物だと勘違いして設定するたびにリビルドしていた過去の私……

条件付きブレークポイント

ここからはいつもと違うちょっと特殊なブレークポイントの使い方。

まずは基本的(?)な条件付きブレークポイントの使い方から

trueになる時だけ止める

例えばぐるぐる回ってる知らないforの中身で何をやってるのか知りたい時、100回に1回だけ止めたい、なんてシチュエーションはあるでしょう。

そりゃ、もちろん、普通にセットして99回無視する(手で続行ボタンを押す)事も出来るには出来ますけど……非効率ですよね。

それじゃあまるで道具に使われているみたいな印象になっちゃいます。

そこで登場するのが条件付きブレークポイント!!

設定したブレークポイントにマウスカーソルを合わせ、歯車マークの「設定」をクリックすると
visualstudio-breakpoint-004

こんな感じで、ブレークポイントに条件を設定する画面が現れます。
visualstudio-breakpoint-005

こんな感じで、いつもの様に条件を記載すると……
visualstudio-breakpoint-006

条件に一致する時(今回の場合は変数i(ループカウンタ)が99回目の時)にだけ、ブレークポイントがヒットします。
visualstudio-breakpoint-007

もちろん比較演算子も使えますし、&&(AND)や||(OR)も利用出来ます。(bool値を返すものなら何でも良い?)
visualstudio-breakpoint-008ただし関数は「副作用があり……」とか言われて呼び出せません、それは仕方ない。

C++の様なコンパイル型言語でも、スクリプト的に動作します(デバッグ実行中に条件式を変更する事が出来る)、一体どういう原理なのでしょう……?

変更された場合

こちらがちょっとコツと言うか感覚が分かりづらいですが、条件式の結果が変化した場合にヒットさせる方法です。

先程まで「trueの場合」だった部分を「変更された場合」に設定します。
visualstudio-breakpoint-009

例えば、条件式に「i == 10 || (i >= 80 && i <90)」を設定していた場合は以下の4回ヒットします。

  1. iが10の時(FALSE -> TRUE)
  2. iが11の時(TRUE -> FALSE)
  3. iが80の時(FALSE -> TRUE)
  4. iが90の時(TRUE -> FALSE)

この場合は返すものがbool値である必要はないみたいで、とにかく返す値が変化すればヒットします。

例えば、不動(と仮定されているはず)の変数の中身が変わったりした場合に、どこで変わったのかを見つけ出すのに有効かと思われます。

ヒットカウント

先程まではforループ内でiカウンタの値を利用してブレークポイントをヒットさせていました。

では、複数回呼び出される関数などで特定の回数の時だけ止める場合……別途カウンタ用の変数を置いても良いのですが、その必要はありません。

ブレークポイントが内部的にカウンタを持つ「ヒットカウント」を利用しましょう。

利用

この「ヒットカウント」ではブレークポイントが内部的にカウンタを保持しており、ヒットする(そのブレークポイントを通過する)度に内部のカウンタを+1して条件式を評価します。

使い方は簡単、条件を「ヒットカウント」にするだけ。
visualstudio-breakpoint-010

条件式も適当に書きましょ、>=があって<=が無いのがちょっぴり謎ですけど……
visualstudio-breakpoint-011

3の倍数でヒットするようにすればカウンタが3の倍数の時だけヒットするナベアツ的ブレークポイントになります。

カウンタをリセットする

ちなみにカウンタはブレークポイント設定を開いて、「現在:nリセット」とある所のリセットをクリックすれば0に戻せます。
visualstudio-breakpoint-016

そもそも設定画面が開きづらい(開こうとしてブレークポイントごと消してしまったり)するので、載せようか迷ったのですが、一応紹介しておきます。

ヒットした際の動作

ヒットした際にヒットした事は分かるのですが、追加で他の情報も欲しい場合があります。

そんなときは「アクション」を利用しましょう。
visualstudio-breakpoint-012

メッセージを出す

「出力ウインドウにメッセージを記録する」に表示したい文字を入れればOK!!
visualstudio-breakpoint-013変数は{}で囲めば利用可能です。

設定したメッセージはデバッグウインドウに表示されます。
visualstudio-breakpoint-014

デフォルトだとブレイクポイントをスルーしてメッセージだけ表示するのですが、ここで「出力を続行します」からチェックを外せばいつも通り停止するようになります。
visualstudio-breakpoint-015

std::stringstreamとOutputDebugStringでやっていたような事がブレークポイントだけで出来てしまう訳ですね!!

特殊な変数を利用する

更に、ここでは特殊な変数を利用する事で呼び出しに関する情報が取得できます。

利用できるのは以下の8つです。

  • $ADDRESS(関数名 + 関数のアドレス)
  • $COLLER(この関数を呼び出した関数の名前)
  • $COLLSTACK(コールスタック)
  • $FUNCTION(この関数の名前)
  • $PID(プロセスID)
  • $PNAME(プロセス名)
  • $TID(スレッドID)
  • $TNAME(スレッド名)

実際の出力のサンプルは以下の通りです。

$ADDRESS:
Sample.Form1.button1_Click(object, System.EventArgs) + 0x00000043

$COLLER:
System.Windows.Forms.Control.OnClick

$COLLSTACK:
Sample.exe!Sample.Form1.button1_Click
	System.Windows.Forms.dll!System.Windows.Forms.Control.OnClick
	System.Windows.Forms.dll!System.Windows.Forms.Button.OnClick
	System.Windows.Forms.dll!System.Windows.Forms.Button.OnMouseUp
	System.Windows.Forms.dll!System.Windows.Forms.Control.WmMouseUp
	System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc
	System.Windows.Forms.dll!System.Windows.Forms.ButtonBase.WndProc
	System.Windows.Forms.dll!System.Windows.Forms.Button.WndProc
	System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage
	System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc
	System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback
	[ネイティブからマネージへの移行]
	[マネージからネイティブへの移行]
	System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop
	System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner
	System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop
	System.Windows.Forms.dll!System.Windows.Forms.Application.Run
	Sample.exe!Sample.Program.Main
	[ネイティブからマネージへの移行]
	[マネージからネイティブへの移行]
	mscorlib.dll!System.AppDomain.ExecuteAssembly
	Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly
	mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context
	mscorlib.dll!System.Threading.ExecutionContext.RunInternal
	mscorlib.dll!System.Threading.ExecutionContext.Run
	mscorlib.dll!System.Threading.ExecutionContext.Run
	mscorlib.dll!System.Threading.ThreadHelper.ThreadStart

$FUNCTION:
Sample.Form1.button1_Click(object, System.EventArgs)

$PID:
0x35D0

$PNAME:
E:\Documents\Visual Studio 2015\Projects\Sample\bin\Debug\Sample.vshost.exe

$TID:
0x3214

$TNAME:
メイン スレッド

最後に

たかがブレークポイント如きに異常なまでの豊富な機能が搭載されていますね。

実際は使わないでしょうし説明を省いた機能もありますし(うそ、本当は眠たかっただけ)、私が気付いていない様な機能もあるでしょう。

ざっと紹介しただけでこれだけとは……多機能すぎる

 - プログラミング