自分がデバッグするときの考え方について、メモ書き程度ですが記録しておきます。
1. 記録
- 不具合の発生方法・発生手順を記録する
- どの画面から遷移したか
- どの処理・どの機能を使ったときか
- どのデータを使ったときか(環境に設定されている条件や、対象データのIDなどをメモする)
- 並列で別の処理が流れていないか
- エラーが発生した時の動作を記録する
- 例外が出力された時、その内容は何か
- ダイアログが出力されたら、その内容は何か
- ステータスコードは出力されているか。その値に意味が隠されていないか
- どんな値がDBに保存されたか
2. 再現
- 最新バージョンのリリース媒体を用意する
- DBに事象が再現するデータを登録する
- A5M2 (A5:SQL Mk2) で select した結果をコピーするときに「Insert形式でコピー」をすると、他環境にすぐに適用できて便利です
- 注意点として、本番環境からデータを持ってくるときは、個人情報をマスク化してから利用すること
- 日付や時間に起因するバグであれば、動作環境の時刻を変更する
- 基本的に、システム日付に依存する処理はテストが大変なので、画面の値やDBのシステム制御用の情報などで日付を切り替えられるようにする
- 処理の内部で乱数が使われているときは、シード値を固定できるようにする
3. 観察と仮説
- ログの観察
- ログの出力からどこまで正常に動作したかを確認する。エラーコードが出ていればそれを検索する
- スタックトレースが出力されていれば、その処理の経路を追いかける。
- printfデバッグを使うときは、環境変数などでログのレベルを制御できる仕組みを使う。
- 解析手法
- 全部のプログラムの動きを把握するのは困難なので、まずは静的解析をして怪しい部分の当たりをつける
-
静的解析:
- プログラムコードを読んで動きを追いかけて、不具合に対する仮説を立てる。また、この時点で大まかなプログラムの流れを把握しておく
- IDEの機能である「定義へ移動」や「すべての参照箇所を探す」で効率よく定義箇所や使用箇所を検索する。
-
動的解析:
- 怪しいところにブレイクポイントを立てて、実際の動作を確認しながら、不具合に対する仮説を立てる。
- ブレイクポイントを立てた箇所での変数の格納値なども確認する。
- デバッグ中に変数の内容を改ざんして、動作が改善するか確認する。
- 複数環境の比較
- 2つの環境を用意し、問題なく動作している環境と、問題のある環境に差があるか確認する(データの積み方や設定項目の差異など)
4. 修正と検証
- 仮説を元にソースコードに対して修正を行う。
- 修正済みのアプリケーション媒体を使って、不具合が改善したかをテストして確認する。
- 影響調査が不十分だと修正によって二次被害が起きてしまうので注意
- エラーの推定方法
- 不正値の混入
- Nullなどの不正値が混入する可能性があるか。初期化されずに使われる可能性はないか
- 自システムの入力画面で登録されるデータなのか、他のシステムから連携されるデータなのか
- アプリケーションのバージョンアップ時に、適切なSQLパッチが当てられないまま放置されていないか
- 制御フロー
- 想定外の値が来たときに、どのif文にも入らないケースは存在しないか(if文のelse、switch文のdefaultなどが存在しないとき期待通りの挙動になるか)。
- try-catchで例外を握りつぶしていないか。
- 依存関係
- 感染源を特定する。先行の処理でどのように値が設定されてくるのか確認する
- 感染先を特定する。後続の処理にどう影響を与えているか確認する
- 不正値の混入
5. 収束判定
- 仮説と検証結果に矛盾がなければ、不具合修正を完了とし、チケットの消し込みを行う。
- 仮説と検証結果に矛盾があれば、再び観察と仮説から繰り返し行う。
- 他に同様の処理を行っていないか調査し、修正が必要かを判断する。
以上です。