前回の続き。
#もっと単純に
前回の記事で、「修正された実行ファイルをターゲットに送信して、それを起動してデバッグを開始する」というのがgdb内で完結できることが確認できました。しかし、複数のコマンドを間違えないように実行しなければならないという問題がありました。
gdbの起動時間は(CUIで使用している限り)十分短いので、実行ファイルを更新したときには一度gdbを終了して、再度起動しても特にストレスには感じないですね。
それならば、実行ファイルをターゲットに送信して起動するためにコマンドは.gdbinit に書いておけばいい。
set sysroot ../buildroot/buildroot-2016.05/output/host/usr/arm-buildroot-linux-gnueabi/sysroot/
target extended-remote 172.16.129.32:1234
file loop
remote put loop /root/loop
set remote exec-file /root/loop
start
デバッグ対象の実行ファイルはカレントディレクトリのloop
それをターゲットの/root/loop に転送する
それを実行開始して、main関数のところで停止。
#実際にやってみる
ARM Linux側でgdbserverをmultiモードで起動する
ARM# gdbserver --multi :1234
Listening on port 1234
最初に一回行うだけですむはず。(gdbがハングアップしない限り)
デバッグ対象プロセスの標準出力、標準エラー出力がここに出るので、バックグランドで実行させるのでなくて、telnetやsshのttyをひとつ割り当てるのがよいでしょう。
gdbから実行ファイルを転送して実行、デバッグ開始する
gdbに -q オプションをつけると起動メッセージが省略されます。
$ arm-buildroot-linux-gnueabi-gdb -q
Temporary breakpoint 1 at 0x10464: file loop.c, line 23.
Temporary breakpoint 1, main () at loop.c:23
23 func1();
(gdb)
デバッグ対象プロセスがmain関数のところで止まっている状態です。
ARM Linuxのgdbserverでは以下のように表示されています。
Remote debugging from host 172.16.130.16
Process /root/loop created; pid = 488
ステップ実行したり、バックトレースを確認したり
(gdb) step
func1 () at loop.c:7
7 func2();
(gdb)
func2 () at loop.c:16
16 sleep(2);
(gdb) bt
#0 func2 () at loop.c:16
#1 0x0001045c in func1 () at loop.c:7
#2 0x00010468 in main () at loop.c:23
(gdb)
gdbを終了させるとデバッグ対象プロセスも終了する
(gdb) q
A debugging session is active.
Inferior 1 [process 488] will be killed.
Quit anyway? (y or n) y
$
ARM Linuxのgdbserverでは以下のように表示されています。
Remote side has terminated connection. GDBserver will reopen the connection.
Listening on port 1234
gdbserverは引き続き待機中。
ソースファイルを修正してビルドして実行ファイルを更新したら、再度gdbを起動すればよい。
これなら操作ミスでgdbが変な状態になることが防げる。これで十分。
リモートデバッグでありながら、ネイティブデバッグとあまり変わらない操作感。
#ToDo
ターゲットに転送する実行ファイルはstrip済みのものでよい。そのほうが実行ファイルのサイズがずっと小さくなるので転送時間が短くなる。
Makefileの中で、stripしてない実行ファイルとstrip済みの実行ファイルを両方作るようにして、.gdbinit もそれに合わせて修正する。