組み込み開発

初心者のためのデバッグ入門:ブレークポイントと変数を見る

シリーズ:基礎シリーズ(組み込み開発)
この記事の役割:実験シリーズでは、実際に評価ボードにプログラムを書き込んで動かします。その実験をするために、前提として必要なデバッグの考え方や最低限必要な操作を覚えます。

はじめに

デバッグは、プログラムが思い通りに動かないときに、その原因を探すための方法です。
プログラムを一時停止して、今どこを実行していて、変数などの値がどうやっているかを確認します。
この記事では、難しい機能は扱いません。
プログラムを止める・進める・値を見るの3つだけできるようになれば、実験シリーズのフェーズ0~フェーズ1の実験が楽に進められるようになります。

先に結論(※ここは読み飛ばしてOK)

最初はこれだけでOKです。

  • デバッグ=プログラムを一時停止して、今の状態を見ること
  • 最初に覚えるのは 3つだけ
    1. ブレークポイント:ここで止める
    2. ステップ実行:1行ずつ進める
    3. 変数の値を見る(ウォッチ):値を確認する
  • 「動かない」は、だいたいこの3分類に落ちます
    • 止まらない(そもそも実行されてない)
    • すぐ止まる(例外/Fault/無限ループ)
    • 値が想定と違う(分岐・初期化・型)

ここまで押さえれば、専門用語が分からなくても“切り分け”はできます。

1. デバッグとは何か(ログとの違い)

ログは、「動いているときの動作の履歴」です。
一方、デバッグは、プログラムを停止して、その時点の変数の値などを見ることで、条件が合っているか、変数の値が意図通りかを確認できます。
意図しない動きをしていることはログだけでも追うことができますが、プログラムを止めて中身を見る方が早く解決できることが多いです。

2. まず覚える3つ

ここではデバッグの機能を網羅することではありません。
デバッグは、最初、次の3つを覚えれば十分です。

ブレークポイント

  • ここで止めたいと思うプログラムの行に目印を置く
  • プログラムを実行させて、そこで止まったら「その行までは実行できている」ことが確定

ステップ実行

  • プログラムを1行ずつ進めて、どの分岐に入ったかを確認する
  • 最初はステップオーバー(関数の中には入らない)で進める

変数の値を見る

  • 今の変数の値を見る
  • 期待している値と違うなら、どこで変わったかを追える

3. 最低限の見方

デバッグ中は、次の順で確認します。

  1. 今どこでとまっているか?(停止位置)
  2. 分岐はどっちに行ったか?(if文の条件、フラグ)
  3. 変数などの値は合っているか?(変数、カウンタ、状態)

4. デバッグ例

ここから先は「デバッグで何を見るか」を掴むための例です(e² studioの操作手順は実験シリーズで扱います)。
次のプログラムをデバッグします。(わざとバグを入れています)

static bool led = false;

static void led_write(bool on)
{
  // ここは「GPIOをON/OFFする関数」のつもりでOK
  (void)on;
}

/* main処理 */
void hal_entry(void)
{
  uint32_t count = 0;

  while (1)
  {
    count++; // ←ここにブレークポイント

    // バグ:比較のつもりが代入になっている
    if (count = 1000000)
    {
      // 代入した結果、countは1000000(0でない)なので、毎回trueになり、実行される
      led = !led;
      led_write(led);
      count = 0;
    }
  }
}

4-1. デバッグ内容

  1. count++している行にブレークポイントを貼る
  2. デバッグを開始して、ブレークポイントで止まる
  3. ステップオーバーで進めて、countの値が増えることを確認する
  4. if (count = 1000000)の行をステップオーバーで進める
  5. 比較のつもりでも、代入になっているため、countが1000000に書き換わる
  6. その結果、if文は毎回trueになり、if文の内の処理に入ってしまう ←ここが意図通りでない
  7. 修正:if (count == 1000000)にする

4-2.e2 studioによるデバッグ方法(簡易版)

実際に、4-1章のデバッグ内容を、e² studioを使ってを紹介します。

  1. e² studioでデバッグ実行を開始します。
    「実行」→「デバッグ」を選択する。または、虫アイコンをクリックする。※赤枠のどちらか片方を選択してください。
デバッグ実行開始
  1. ソースコードのcount++; している行にブレークポイントを貼ります。
    count++;の行の行番号の左の灰色の余白(赤枠の箇所)を、ダブルクリックするか、右クリックして、「Toggle Software Breakpoint」を選択する。
ブレークポイント貼り方1
ブレークポイント貼り方2
  1. プログラムを実行(再開)して、ブレークポイントで止まることを確認します。
    赤枠の再生マークか、「実行」→「再開」を選択することで、プログラムが開始されます。
デバッグ開始

プログラムを実行した際、main()関数内にて停止する場合があります。
これは、デバッグ設定によるものです。
停止した場合は、再度、実行(再開)を行えば問題ありません。

  1. ブレークポイントでプログラムが停止したら、countの値を確認します。
    count++;のにカーソルをあてると変数の内容が表示されます。または、変数のウィンドウから表示できます。
    ※もし、変数のウィンドウが表示されていない場合は、「ウィンドウ」→「ビューの表示」→「変数」を選択すると表示されます。
変数の確認
  1. ステップオーバーでプログラムを1行進めます。
    キーボードの「F6」を押すか、ステップ・オーバーアイコンをクリックします。
    ※ステップ・インでプログラムを進めたい場合は、「F5」を押すか、ステップ・インアイコン(ステップ・オーバーの隣にあるアイコン)をクリックします。
  2. count変数の値がプラス1されていることを確認します。
    更に、ステップオーバーでif (count = 1000000)の次の行まで進めると、count変数が1000000になっていることが確認できます。
countが1000000になっている
  1. デバッグを終了する。
    赤い■アイコンをクリックするとデバッグは終了します。これをクリックすると、デバッグの終了と評価ボードからの切断が行われます。
    ※その右側にあるプラグっぽいアイコンは切断ですが、デバッグのままで、ターゲットとは切れているみたいな状態になりますので、今はあまり使いません。

このように「止める → 進める → 値を見る」を繰り返してデバッグしていきます。

e² studioによるデバッグ操作のアイコンの整理

デバッグ操作アイコン一覧

①:デバッグ実行開始(再開)
②:停止
③:デバッグ終了と切断
④:切断※デバッグは終了しない
⑤:ステップ・イン(F6)
⑥:ステップ・オーバー(F5)
⑦:ステップ・リターン(F7)

ステップ・イン、ステップ・オーバー、ステップ・リターンについて
ステップ・イン:関数呼び出しがある行で使用すると、関数の中に入ります
ステップ・オーバー:関数呼び出しがある行で使用しても関数の中に入らず関数内の処理が行われて次のステップに行きます
ステップ・リターン:今いる関数を抜けてたところまで実行して、関数の呼び元に戻ります

5. よくあるつまずき(最低限)

5-1. ブレークポイントで止まらない

よくある原因は次の3つです。

  • Debugで動かしていない
  • 別のビルド、別の設定を実行している(見ているコードが違う)
  • 最適化で行が消える(待ちループや使われない変数)
    ※Debugビルドで Optimization Level を None(-O0) にすると改善することがあります。

5-2. ステップしても値が変化しない

  • その変数が最適化で消えている/スコープ外になっている可能性
    ※まずは、値を見たい変数が確実に使われている状態にする。

5-3. 止まったけど、どこを見ればいいか分からない

  • 停止位置を見る
  • 次にif文などの条件やフラグを見る
  • それでも分からなければ、いつからおかしいかを探す(直前で値が変わる場所を見つける)

6. まとめ

  • デバッグ=止めて、今の状態を見ること
  • 最初は ブレークポイント/ステップ/変数を見る の3つで十分
  • 「動かない」は
    止まらない/すぐ止まる/値が違う に分けると速い

7. 次に読む

  • UARTログ入門:printfで状態を出す(文字化け/ボーレート/配線の要点)
  • 状態遷移(ステートマシン)入門:INIT/IDLE/RUN/ERROR をログに出す
  • (フェーズ1へ)チャタリングとデバウンス入門
  • (フェーズ1へ)割り込みの最小:ISRはフラグだけ

-組み込み開発