もくじ
https://tera1707.com/entry/2022/02/06/144447
やりたいこと
C#やりはじめのころ、
- なぜかははっきりとはわからないが「例外」がおきる
- でもそれでプログラムがクラッシュすると困る
- だからtry catchで囲んで、なかったことにしてしまおう(握りつぶしてしまおう)
ということが正直あった。
ただそれは自分だけではなく、製品コードでも多くの個所にそういうのがあったりする。
そうなると、実は致命的なエラーが起きていても、握りつぶされてしまいプログラムがクラッシュすることもなく、 時間がたってから、(直接的にはその握りつぶした例外が原因なのに)見た目全然違う現象になって現れたりする。
そうならないように、日々握りつぶした例外を目視したい。
日々まで見なくても、なんかあったときに、実は握りつぶした例外があるのでは?ということを見たい。
※また、検知した例外は一個一個ダンプが出力されては大変なので、ダンプは取らずに例外があったことだけを知りたい。
使うもの
ProcDumpを使う。
ProcDumpは、「例外が起きてアプリがクラッシュしたときにダンプを取ることで、クラッシュ時のデバッグを行えるようにする」が王道の使い道だと思っているが、 クラッシュしない、try catchで握りつぶしたような例外を検知することもできる。
例外がthrowされたときを、ファーストチャンス、
例外がcatchされなかったとき(クラッシュしたとき)をセカンドチャンス、
というらしい。
-e 1
は、そのファーストチャンス例外を検知せよ、というパラメータ。
アプリクラッシュダンプを出力していた時のパラメータ-ma
は、今回ダンプは出力しないので付けない。(つけなくてもダンプはとる。デフォルトで-mm
(ミニダンプ)扱いになる。)
やったこと
ProcDumpを下記のようなコマンドで起動する。(ConsoleApp17.exeは今回使った例外握りつぶしサンプルアプリ=例外を見たい対象アプリ)
procdump -w -e 1 -f "" ConsoleApp17.exe
これを実行すると、
こういう表示が出て待ちに入る。
で、例外を握りつぶすようなプログラムを走らせると、下記のような表示がでる。
赤マークを付けた部分が、握りつぶした例外表示。
「例外を握りつぶすようなプログラム」はこちら。
namespace ConsoleApp17; internal class Program { static void Main(string[] args) { Thread.Sleep(1000); try { // 1. NullReferenceException string nullString = null; Console.WriteLine(nullString.Length); } catch (NullReferenceException ex) { Console.WriteLine($"NullReferenceException: {ex.Message}"); } try { // 2. IndexOutOfRangeException int[] numbers = { 1, 2, 3 }; Console.WriteLine(numbers[5]); } catch (IndexOutOfRangeException ex) { Console.WriteLine($"IndexOutOfRangeException: {ex.Message}"); } try { // 3. DivideByZeroException int zero = 0; int result = 10 / zero; } catch (DivideByZeroException ex) { Console.WriteLine($"DivideByZeroException: {ex.Message}"); } try { // 6. FormatException int invalidNumber = int.Parse("NotANumber"); } catch (FormatException ex) { Console.WriteLine($"FormatException: {ex.Message}"); } try { // 7. OverflowException int tooLarge = int.MaxValue; int overflow = checked(tooLarge + 1); } catch (OverflowException ex) { Console.WriteLine($"OverflowException: {ex.Message}"); } Console.WriteLine("例外のサンプルが終了しました。"); } }
※実験コードを作るうえでだけの注意だが、このコードの冒頭にある「Thread.Sleep(1000)」で待ちを入れておかないと、 起動後一瞬で終わってしまうようなプログラムだと、ProcDumpがアタッチ?する前にプログラムが終わってしまい、うまくいかなかった。 実験時は、1秒程度Sleepしたほうがよさそう。
気になったこと(-f "" はなぜ必要か?)
今回、ProcDumpのパラメータのサンプルを参考に、
procdump -w -e 1 -f "" ConsoleApp17.exe
で試してうまくいったのだが、-f ""
がなぜ必要なのか?がよくわからなかった。
MSDOCでの-f
の説明は下記。
例外名称や例外Msgなどに、-fで指定した文字列が含まれているとダンプを行うらしい。
色々試した結果、-f ""
は、「ダンプファイルを出力させない」ために付けているっぽい。
付けないと、1個目のファーストチャンス例外時にダンプが出力されて、procdumpが終了した。 (終了するのは、デフォルトの設定でダンプ回数が1になってるからっぽい?未確認)
で、-f ""
を付けると、フィルター文字列に引っかからなくなることで、ダンプが出力されなくなり、引っかかった旨の文言だけprocdump上に出てくるようになるものと思われる。
※ためしに-f "AAAAAAAAAAAAAAAA"
とかすると、これも引っかからなくなって、-f ""
と同じ結果になった。
-f に付けるパラメータ実験
-f "" を付けなかったときのprocdumpのアタッチ時の出力
(Excludeに
*`が入っている=すべての例外を除外する=ダンプを出力しない)
`-f "" を付けなかったときのprocdumpのアタッチ時の出力
(Excludeに何も入っていない=除外する例外なし=すべての例外でダンプを出力する)
`-f "AAAAAAAAAAAAAA" を付けたときのprocdumpのアタッチ時の出力
(Includeに"AAAAAAAAAAAA"が入っている=AAAAAAAAAAAという文言を含む例外でダンプを出力する=そんな例外はないので、実質ダンプを出力しない)
参考
ProcDump
https://learn.microsoft.com/en-us/sysinternals/downloads/procdump
ファーストチャンス例外とは?MS開発ブログ
https://learn.microsoft.com/en-us/archive/blogs/davidklinems/what-is-a-first-chance-exception