デバッグ
既にデバッグセッションの中に居ます。プログラムをデバッグしてみましょう。
load
コマンドの後、プログラムは、エントリポイントで停止しています。このことは、GDB出力の「Start address 0x8000XXX」という部分からわかります。
エントリポイントは、プロセッサ / CPUが最初に実行するプログラムの一部です。
スタータープロジェクトでは、main
関数の前に実行する追加のコードを提供しています。
ここでは、「mainの前の」部分には興味がないので、main
関数の直前までスキップします。ブレイクポイントを使って、これができます。
(gdb) break main
Breakpoint 1 at 0x800018c: file src/05-led-roulette/src/main.rs, line 10.
(gdb) continue
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.
Breakpoint 1, main () at src/05-led-roulette/src/main.rs:10
10 let x = 42;
ブレイクポイントは、プログラムの通常フローを停止するために使います。continue
コマンドは、ブレイクポイントに到達するまでプログラムを実行します。
今回の場合、main
関数に到達するまでです。なぜなら、そこにブレイクポイントがあるからです。
GDBが「Breakpoint 1」と出力していることに留意して下さい。今回のプロセッサでは、6個のブレイクポイントしか使えないことを思い出して下さい。 これらのメッセージに注意を払うことは大事なことです。
より良いデバッグ経験のために、GDBのテキストユーザーインタフェース(TUI)を使います。 このモードに入るには、次のコマンドをGDBシェルに入力します。
(gdb) layout src
注記 Windowsユーザーの方はごめんなさい。GNU ARM Embedded Toolchainで配布されているGDBはTUIモードをサポートしていません
:-(
。
次のコマンドで、いつでもTUIモードから抜けることができます。
(gdb) tui disable
今、main
の最初に居ます。step
コマンドを使って、プログラムをステートメントごとに実行することができます。
_y = x
ステートメントに到達するために、コマンドを2回使います。一度、step
を入力すると、エンターを押すだけで、同じコマンドを再び実行できます。
(gdb) step
14 _y = x;
TUIモードを使っていない場合、各step
を呼ぶごとに、GDBは現在のステートメントを行番号と一緒に出力します。
現在、_y = x
ステートメントの「上」に居て、まだこのステートメントは実行されていません。
つまり、x
は初期化されていますが、_y
は初期化されていません。print
コマンドを使って、スタック/ローカルな変数を調べてみましょう。
(gdb) print x
$1 = 42
(gdb) print &x
$2 = (i32 *) 0x10001ff4
(gdb) print _y
$3 = -536810104
(gdb) print &_y
$4 = (i32 *) 0x10001ff0
予想通り、x
は42
という値を格納しています。しかし、_y
は、-536810104
(?)という値を格納しています。
_y
は、まだ初期化されていないため、ゴミが入っています。
print &x
コマンドは、変数x
のアドレスを出力します。ここで興味深いことは、GDBが参照の型を出力することです。i32*
は、i32
のポインタ値です。
他におもしろい点は、x
と_y
のアドレスがお互いに非常に近いことです。これらのアドレスは、ちょうど4
バイトだけ離れています。
ローカル変数を1つずつ出力する代わりに、info locals
コマンドを使うこともできます。
(gdb) info locals
x = 42
_y = -536810104
では、もう一度step
を実行すると、loop {}
ステートメントに到達します。
(gdb) step
17 loop {}
そして、_y
は初期化されているはずです。
(gdb) print _y
$5 = 42
loop {}
ステートメント上で再度step
を使うと、プログラムがそのステートメントを抜けることがないため、動けなくなります。
代わりに、layout asm
コマンドで逆アセンブル画面に切り替えます。その上で、stepi
を使って、1命令ずつ前に進めます。
layout src
コマンドを使うことで、いつでもRustソースコード画面に戻ることができます。
注記 間違って
step
を使ってしまい、GDBが動かなくなった場合、Ctrl+C
を打つことで、動けるようになります。
(gdb) layout asm
TUIモードを使っていない場合、disassemble /m
コマンドを使うことで、現在実行中の行周辺のプログラムを逆アセンブルできます。
(gdb) disassemble /m
Dump of assembler code for function main:
7 #[entry]
0x08000188 <+0>: sub sp, #8
0x0800018a <+2>: movs r0, #42 ; 0x2a
8 fn main() -> ! {
9 let _y;
10 let x = 42;
0x0800018c <+4>: str r0, [sp, #4]
11 _y = x;
0x0800018e <+6>: ldr r0, [sp, #4]
0x08000190 <+8>: str r0, [sp, #0]
12
13 // 無限ループ。このスタックフレームから抜けないためのものです。
14 loop {}
=> 0x08000192 <+10>: b.n 0x8000194 <main+12>
0x08000194 <+12>: b.n 0x8000194 <main+12>
End of assembler dump.
左側にある太矢印=>
が見えますか?これは、プロセッサが次に実行する命令を示しています。
TUIモードではない場合、各stepi
コマンドにより、GDBはステートメントと、行番号、およびプロセッサが次に実行する命令のアドレスを表示します。
(gdb) stepi
0x08000194 14 loop {}
(gdb) stepi
0x08000194 14 loop {}
さらに興味深いものに行く前に、最後のトリックがあります。GDBに次のコマンドを入力して下さい。
(gdb) monitor reset halt
Unable to match requested speed 1000 kHz, using 950 kHz
Unable to match requested speed 1000 kHz, using 950 kHz
adapter speed: 950 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000196 msp: 0x10002000
(gdb) continue
Continuing.
Breakpoint 1, main () at src/05-led-roulette/src/main.rs:10
10 let x = 42;
main
の最初に戻ってきます!
monitor reset halt
は、マイクロコントローラをリセットし、プログラムのエントリポイントで停止します。
続くcontinue
コマンドは、ブレイクポイントがあるmain
関数に到達するまで、プログラムを実行します。
このコンボは、間違って調査の対象とするプログラムの一部をスキップしてしまったときに便利です。 プログラムの状態を、最初の状態に、簡単にロールバックすることができます。
ただし書き:
reset
コマンドは、RAMをクリアしたり、触ったりしません。メモリは、前回実行した時の値を持ち続けています。 プログラムが、未定義動作の定義である初期化されていない変数の値に依存しない限り、このことは問題になりません。
このデバッグセッションは完了です。quit
コマンドでデバッグセッションを終了できます。
(gdb) quit
A debugging session is active.
Inferior 1 [Remote target] will be detached.
Quit anyway? (y or n) y
Detaching from program: $PWD/target/thumbv7em-none-eabihf/debug/led-roulette, Remote target
Ending remote debugging.
注記 デフォルトのGDBコマンドラインインタフェースが好みでない場合、gdb-dashboardを確認して下さい。 このツールは、Pythonを使用して、デフォルトのGDBコマンドラインインタフェースを、レジスタやソースコード、アセンブリなどを表示するダッシュボードに変換します。
ただし、OpenOCDは、終了しないで下さい!OpenOCDは、この後繰り返し使用します。 動作したままにしておくほうが良いです。
次は何でしょうか?約束した高レベルのAPIです。