- コードを見て「こう動いているはず」「こう処理しているはず」と考えるだけだと、その推測が間違ってる時には正しく動作しない。デバッグ実行して動作確認しよう。
- 参考
- <目次>
- どんな時にデバッガを使うか
- プログラムは動くが、想定通りの動作をしてくれない時。(動作を確認しながらコードを追えるため、コードを読むために使うことも珍しくない)
- 対応方法
- case 1) ErrorやExceptionが出るなら、該当するコード行と出力内容を参考にしつつ、要因を推定して、対策を検討する。
- case 2) 関連しそうな変数を片っ端からprint出力してみて要因特定する。
- case 3) デバッガを使い、要因を絞り込みつつ対策を検討する。
- 対応方法
- プログラムは動くが、想定通りの動作をしてくれない時。(動作を確認しながらコードを追えるため、コードを読むために使うことも珍しくない)
- デバッガの種類
- ターミナルで操作: jdb
- IDEで操作: jdbのラッパーになっているので、基本的な操作は一緒。コマンドをターミナル上で入力するか、GUIで入力するかが違うぐらい。
- IDEでデバッガを使う際のメリット
- どの行を実行しようとしてるのか確認しやすい。
- 変数の中身をいちいちprintせずとも参照できる。
- IDEでデバッガを使う際のメリット
- step 1: 動作を一時的に止めたい行にブレークポイント(breakpoint)を設定する。
- 思いつかないならmain()メソッド1行目に設定するのも手。ブレークポイントは複数設定できるし、後から削除することも可能なので取り敢えず1箇所は設定しよう。
- step 2: デバッガを起動(デバッグ実行する)。
- VSCodeウィンドウの左端にある「Run」ボタンをクリック。そこから
Run and Debug
を実行。Runボタンから実行することで実行途中の変数やスタックを確認できるようになる。
- VSCodeウィンドウの左端にある「Run」ボタンをクリック。そこから
- step 3: デバッグ実行しながら想定通りに処理が進んでいるかどうかを確認する。
- 基本的な実行継続方法:
- step over: 今いるスタックで停止行のコードを処理し、次の行に移動するまで実行する。次の行に移動したら一時停止する。
- 参考: Pythonでは関数が呼び出される都度新規にスタックを作成。Javaではメソッドが呼び出される都度スタックが生成されると考えて良い。メソッド呼び出しの流れはシーケンス図で書くとわかりやすい。
- step into: 停止行がメソッド呼び出しの場合、そのメソッド内に移動する。
- step into(force): System.out.println()等標準メソッドの場合、そのメソッド内に移動したくないことが多い。このため、デフォルトの step into では、標準メソッドの場合には中に移動しない。それに対し、あらゆるメソッドに対して中に移動したい場合には force すると良い。
- step out: 今いるスタックから抜けるまで実行を続け、抜けたら一時停止する。
- step over: 今いるスタックで停止行のコードを処理し、次の行に移動するまで実行する。次の行に移動したら一時停止する。
- 「想定通りに処理が進んでいるかどうか」は、変数の中身を確認することで検証できるはずである。
- 必要に応じて条件を設定 (conditional breakpoint)する。
- 基本的な実行継続方法:
- 注意
- IDEでデバッグ実行すると、終了するまではデバッガが動いたままになる。そのことに気づかず「一時停止状態のままもう一度デバッグ実行」すると、複数デバッガが動く。実行中の分だけデバッグが走り続けるため、メモリ等リソースを食いまくるので注意。
- 対策
- デバッガで一時停止させてるなら、それを停止させてから新しくデバッグ実行する。
- コード例: noisy_file
- 環境準備+動作確認
- コードの準備
- 上記コード例のリポジトリURLからクローンを作成する。ここでは
~/prog2/noisy_file
としよう。
- 上記コード例のリポジトリURLからクローンを作成する。ここでは
- クローンに移動し、VSCodeで開く。
- ReadCSVFileクラスを実行しよう。エラーが出るはずだ。
- コード概要
- CSVファイル(data/daily_access.csv)が用意されている。1列目は日付。2列目はアクセス数(integer)を想定。
- 上記ファイルを1行ずつ読み込み、String.split(",")でカンマ区切りの分割する。
- 分割した1つ目(日付)をString型として保存。2つ目(アクセス数)をInteger.parseInt()でinteger型に変換して保存。
- コード概要を踏まえ、エラー出力から要因を推測してみよう。
- コード概要
- コードの準備
- 今回のケースは、エラー出力から要因特定は容易。だけどデバッガ練習のためにデバッグ実行してみよう。
- step 1: 動作を一時的に止めたい行にブレークポイント(breakpoint)を設定する。
- 今回はmainメソッドの冒頭行である、16行目に設定してみよう。
- step 2: デバッガを起動(デバッグ実行する)。
- するとブレークポイントに達するまで処理し続け、たどり着いたら一時停止する。今回は16行目で停止するはずだ。
- 停止してる状態で「背景が濃いブルーになってる行」は、次に実行する行。まだその行は実行していない。
- するとブレークポイントに達するまで処理し続け、たどり着いたら一時停止する。今回は16行目で停止するはずだ。
- step 3: デバッグ実行しながら想定通りに処理が進んでいるかどうかを確認する。
- step over してみよう。実行する度に「今いるスタック内で次の行に移動して一時停止」するはずだ。また、変数がどのように更新されているかも確認できるはずだ。
- while文の中を繰り返すことを確認できたら、デバッガを一度停止(左下の赤い四角)して、改めてデバッガを起動しよう。また16行目で停止している状態。
- step into(↓) してみよう。22行目までは単に「次の行」に移動して停止していたはずだが、その後は見知らぬコードに移動するはずだ。
- 補足
- これは「step intoはメソッドが呼ばれたらその中に移動して、一時停止する」から。22行目はFileクラスのコンストラクタを起動してオブジェクトを起動しており、その後にScannerクラスのコンストラクタを起動してオブジェクトを生成している。これらのクラスを読み込む際にClassLoaderが自動的に呼ばれることで該当クラスを読み込んでいる。もしこれらの「今みたいスタックではない」ならば、step out することでそのスタックを抜けるところまで自動実行して再び一時停止してくれる。
- lineProcess()内で一時停止している状態で、次の手続きを溜めそう。
- 補足
- step out(➚) してみよう。次の行ではなく「lineProcess()を呼び出したスタック内の次の行」に移動して一時停止しているはずだ。step outは「今いるスタック内を処理し終えてから停止させたい」時に使用。
- step over してみよう。実行する度に「今いるスタック内で次の行に移動して一時停止」するはずだ。また、変数がどのように更新されているかも確認できるはずだ。
- step 1: 動作を一時的に止めたい行にブレークポイント(breakpoint)を設定する。
- チュートリアル1で、lineProcess()にいる状態で一旦操作を止めよう。面倒ならばブレークポイントを適宜設定し直してからデバッグ実行し直すと早い。
- 通常実行時には、今いるメソッド内の状態(=スコープ範囲内の状態)しか参照できない。
- 例えば、lineProcess()メソッド内13行目で停止している最中だと、引数であるline, countに加え、メソッド内で宣言しているローカル変数items, str, valueの5変数までしか参照できない。
- しかし、デバッガ起動中ならば「ここに辿り着くまでに作成したスタックを覗き見る」ことも可能。
- 左下「Call Stack」メニューには、現在保存しているスタックが列挙されており、今いるスタックが1番上、その下に「lineProcess()を呼び出す直前のスタックであるmain()の状態」が保存されている。
- 具体的には、今は ReadCSVFile.lineProcess()の13行目におり、このメソッドは ReadCSVFile.main()の25行目から呼び出されたことがわかる。
- このFramesメニューをクリックするだけで該当フレームを観察することができる。
- 一度デバッガを終了しよう。(赤い四角ボタン)
- 用意されているコード例で問題のある箇所は、ループ処理を何度か繰り返した後で初めて問題が明確化する。
- ブレークポイントを指定して、そこに辿り着くまでstep in/out実行するのはダルイ。こういう時は「ブレークポイントに条件を設定」しよう。
- まずは現在設定しているブレークポイントを全て削除しよう。
- 12行目にブレークポイントを設置。その後、設置した後で赤丸を「Ctrlキー押しながらクリック」すると、いくつかの条件を設定することができる。
- 「Edit breakpoint」を選ぼう。
- ここでは「Condition」に
str.equals("04/Apr/2015") == true
と入力して、Enterキーを押そう。これは、「直前の行で読み込んだstrの値が指定された文字列(04/Apr/2015
)と等しい時のみ一時停止しろ」という条件を設定した例になっている。 - 上記設定をしたらデバッガを起動しよう。この条件になるまで処理が継続され、指定条件に合致したら一時停止状態になっているはずだ。
- ここでは「Condition」に
- ブレークポイントを指定して、そこに辿り着くまでstep in/out実行するのはダルイ。こういう時は「ブレークポイントに条件を設定」しよう。