🔧
Zigの実行ファイルをgdbで追いかけてみる
gdbでZigはまだサポートされていませんが、今の段階でもけっこう使えます。
zig, gdb のバージョン
$ zig version
0.11.0-dev.3704+729a051e9
$ gdb --version
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
zigのビルドとシンボル情報の確認
使用するソースコードは前回の記事のa3.zigです。
$ zig build-exe a3.zig
$ file ./a3
./a3: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, with debug_info, not stripped
デバッグ情報がついていて、stripされていないことが確認できました。
main
の関数のシンボルを調べてみます。
$ nm ./a3 |grep main
000000000021974c t a3.main
0000000000204788 r builtin.panic_messages.exact_division_remainder
00000000002056a3 r builtin.panic_messages.exact_division_remainder__anon_6900
000000000027c000 b os.linux.tls.main_thread_tls_buffer
どうやら a3.main
がa3のmain関数のようです。
他に a3.
がつくシンボルをみてみると、以下のものが見つかりました。
$ nm ./a3 |grep 'a3\.'
000000000021974c t a3.main
0000000000200e38 r a3.OUTPUT_FILENAME_PATTERN
000000000020509c r a3.OUTPUT_FILENAME_PATTERN__anon_3721
0000000000218ff4 t a3.writeFile
gdbを使ってみる
gdbの起動
$ gdb ./a3
...
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a3...
(gdb)
a3.main
にブレークポイントをかけて、実行
(gdb) break a3.main
Breakpoint 1 at 0x219784: file a3.zig, line 14.
(gdb) run < a.mjpeg
Starting program: /autofs/usbssd1/koba/work/zig/jpegdec/split_jpeg/a3 < a.mjpeg
Breakpoint 1, a3.main () at a3.zig:14
14 var allocator = std.heap.page_allocator;
(gdb)
n(next)
コマンドで、ソースコードの行単位でステップ実行
(gdb) n
15 var buffer: [BUF_SIZE]u8 = undefined;
(gdb) n
16 var frame_num: usize = 0;
(gdb) n
17 var write_buffer = std.ArrayList(u8).init(allocator);
(gdb) n
25 var state: State = State.st0;
(gdb) n
28 const n = try io.getStdIn().read(&buffer);
(gdb) n
29 if (n == 0) break;
(gdb) n
31 var i: usize = 0;
(gdb) n
32 while (i < n) : (i += 1) {
(gdb) n
33 switch (state) {
(gdb) n
35 if (buffer[i] == JPEG_START0) {
(gdb) n
36 state = State.st1;
(gdb) n
32 while (i < n) : (i += 1) {
(gdb) n
32 while (i < n) : (i += 1) {
(gdb) n
33 switch (state) {
(gdb) n
40 if (buffer[i] == JPEG_START1) {
(gdb) n
41 try write_buffer.append(JPEG_START0);
(gdb) n
42 try write_buffer.append(JPEG_START1);
(gdb) n
43 state = State.st2;
(gdb) n
32 while (i < n) : (i += 1) {
(gdb)
変数の値を見てみる。
(gdb) p n
$1 = 65536
(gdb) p i
$2 = 1
(gdb) p buffer
$3 = "\377\330\377\333\000\305\000\003\003\003\003\003\003\004\004\004\004\005\004\003\003\005\a\005\004\004\005\a\a\005\005", '\t' <repeats 16 times>, '\n' <repeats 24 times>, "\001\003\005\005\a\a\a\t\t\t\t\v\t\t\t", '\v' <repeats 18 times>, '\f' <repeats 32 times>, "\002\003\005\005\a\a\a\t\t\t\t\v\t\t\t", '\v' <repeats 18 times>, '\f' <repeats 32 times>...
(gdb)
アセンブラ命令単位でステップ実行する
(gdb) display/i $pc
1: x/i $pc
=> 0x219f9c <a3.main+2144>: ldr x9, [x9, #176]
(gdb) si
0x0000000000219fa0 32 while (i < n) : (i += 1) {
1: x/i $pc
=> 0x219fa0 <a3.main+2148>: adds x10, x9, #0x1
(gdb) si
0x0000000000219fa4 32 while (i < n) : (i += 1) {
1: x/i $pc
=> 0x219fa4 <a3.main+2152>: cset w9, cs // cs = hs, nlast
(gdb) si
0x0000000000219fa8 32 while (i < n) : (i += 1) {
1: x/i $pc
=> 0x219fa8 <a3.main+2156>: str x10, [x8]
(gdb) si
0x0000000000219fac 32 while (i < n) : (i += 1) {
1: x/i $pc
=> 0x219fac <a3.main+2160>: and w9, w9, #0x1
(gdb) si
0x0000000000219fb0 32 while (i < n) : (i += 1) {
1: x/i $pc
=> 0x219fb0 <a3.main+2164>: and w9, w9, #0x1
(gdb) si
0x0000000000219fb4 32 while (i < n) : (i += 1) {
1: x/i $pc
=> 0x219fb4 <a3.main+2168>: strb w9, [x8, #8]
(gdb)
別の関数にブレークポイントをかけて、そこまで実行する
(gdb) b a3.writeFile
Breakpoint 2 at 0x219010: file a3.zig, line 71.
(gdb) c
Continuing.
Breakpoint 2, a3.writeFile (frame_num=0, buf=...) at a3.zig:71
71 var filename_buf: [32]u8 = undefined;
1: x/i $pc
=> 0x219010 <a3.writeFile+44>: mov x10, #0x101010101010101 // #72340172838076673
(gdb)
引数の値を見てみる
(gdb) p frame_num
$2 = 0
(gdb) p buf
$3 = {ptr = 0xfffff7ff3000 "\377\330\377", <incomplete sequence \333>, len = 11537}
(gdb)
bufの型は []const u8
なのですが、ptr
とlen
のフィールドを持つstructと同等の扱いになっているようですね。
バックトレースを見る
(gdb) bt
#0 a3.writeFile (frame_num=0, buf=...) at a3.zig:71
#1 0x0000000000219e30 in a3.main () at a3.zig:57
#2 0x0000000000218f38 in start.callMain ()
at /opt/zig-linux-aarch64-0.11.0-dev.3704+729a051e9/lib/std/start.zig:608
#3 start.initEventLoopAndCallMain ()
at /opt/zig-linux-aarch64-0.11.0-dev.3704+729a051e9/lib/std/start.zig:542
#4 start.callMainWithArgs () at /opt/zig-linux-aarch64-0.11.0-dev.3704+729a051e9/lib/std/start.zig:492
#5 start.posixCallMainAndExit ()
at /opt/zig-linux-aarch64-0.11.0-dev.3704+729a051e9/lib/std/start.zig:455
#6 0x0000000000218a0c in _start ()
at /opt/zig-linux-aarch64-0.11.0-dev.3704+729a051e9/lib/std/start.zig:367
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb)
これだけ使えれば、けっこういいですね。
もちろん、Zigの文法を解釈できないので、p buffer[0..n]
のようなことはできません。
関連
Discussion
VSCodeにCodeLLDB機能拡張入れてデバッグしてる。
プロジェクト直下に
.vscode/launch.json
作り、デバッグ開始するだけなので、ゆるふわレベルな私にも安心(複数のArtifact作る場合はちょっと工夫がいるかも)。
実行ファイルはこれでいけてる。
ユニットテストへのデバッガはちょっとめんどくさい模様。