chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

Ubuntuにtinygo開発環境を導入してgdbでデバッグ可能にする

課題:Ubuntu 上にtinygo開発環境を構築して、gdb+OpenOCDを動くようにしたい
取り組み:Ubuntu 上にtinygoの開発環境をインストール、gdbの最新版をパッケージでインストールする
結論:gdb+OpenOCDによるシンボリックデバッグが可能となった


作業内容:
普段使ってるCygwinにtinygoの環境を入れようとしたが、パスの問題やら、gdbのバージョン不整合の問題が出た。標準的なUbuntu上で環境構築するのが無難ではと思い、Ubuntu に入れなおす
ビルド、gdbを起動してみる。

$ tinygo build  -target pico  blinky1.go

$ tinygo gdb -target pico
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.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /tmp/tinygo9732039/main...

warning: Section .debug_aranges in /tmp/tinygo9732039/main entry at offset 0 debug_info_offset 0 does not exists, ignoring .debug_aranges.
:3333: Connection timed out.
"monitor" command not supported by this target.
You can't do that when your target is `exec'
"monitor" command not supported by this target.
(gdb)

gdbが起動してOpenOCDと通信するところまで進んだ。エラーが出なかったのは、別件のためすでにgdbとOpenOCDがインストール済みだったため。gdbのバージョンは以下(9.2)

$ gdb-multiarch --version
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2

"monitor" command not supported by this target. というエラーが気になるが、、この状態で、RPi上でOpenOCDを起動してgdbから接続してみる。
以下のコマンドでRPi上のOpenOCDを起動

$ /usr/local/bin/openocd -c 'bindto 0.0.0.0'  -f /usr/local/share/openocd/scripts/interface/raspberrypi-swd.cfg -f /usr/local/share/openocd/scripts/target/rp2040.cfg
Open On-Chip Debugger 0.11.0-g228ede4-dirty (2022-12-10-18:01)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
adapter speed: 1000 kHz

Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : BCM2835 GPIO JTAG/SWD bitbang driver
Info : clock speed 1001 kHz
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x00000001
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x10000001
Info : rp2040.core0: hardware has 4 breakpoints, 2 watchpoints
Info : rp2040.core1: hardware has 4 breakpoints, 2 watchpoints
Info : starting gdb server for rp2040.core0 on 3333
Info : Listening on port 3333 for gdb connections
Info : accepting 'gdb' connection on tcp/3333

3333ポートで待ち受けになっているので、Ubuntugdbから接続に行く。接続まではできてターゲットはHALTになった。リストコマンドを打つと、Drawf Errorになっている。なぜだろうか。

(gdb) target remote 192.168.10.100:3333
Remote debugging using 192.168.10.100:3333
0x10001a4c in runtime.scheduler ()
(gdb) b main
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
(gdb) l
Dwarf Error: DW_FORM_strx1 found in non-DWO CU [in module /tmp/tinygo9732039/main]

なお、OpenOCD側では以下の警告が出ている

Warn : Prefer GDB command "target extended-remote 3333" instead of "target remote 3333"

少しぐぐると、、Dwafのバージョンとgdbの整合性の問題のようで、Dwarf5を使っている場合はgdb10を使えということらしい。gdb9のままとする場合はdwaf4に落とせと。
Gdb < 10.1 can't read clang's DWARF v5 - Common Infrastructure - LLVM Discussion Forums
Ubuntu環境のgdbは特にこだわりないので、、最新版にあげてみる。
上げようとしたが、自分のUbuntuは20.04 LTSで、このLTSで入れられる最新版gdbは9.2のようであった。だから、、もっと新しいgdbを入れたかったらUbuntuのバージョンを上げないといけない様だ。あるいは、ソースからコンパイルか。

Ubuntuを24LTSにあげた。おおよそ以下の手順

 1605  apt update
 1606  apt upgrade
 1607  apt full-upgrade
 1608  apt autoremove -y
 1609  apt autoclean -y
 1610  reboot
 1612  do-release-upgrade

参考にした記事
Ubuntu 20.04 LTS を 22.04 LTS にアップグレードする - Uzabase for Engineers

改めて、、RPi側は以下でOpenOCDを実行中

 /usr/local/bin/openocd -c 'bindto 0.0.0.0'  -f /usr/local/share/openocd/scripts/interface/raspberrypi-swd.cfg -f /usr/local/share/openocd/scripts/target/rp2040.cfg

RPiのIPが192.168.10.100なので、tinygo gdbGDBを起動して、192.168.10.100:3333で接続する

$ tinygo gdb --target pico
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 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.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /tmp/tinygo942108089/main...

warning: Section .debug_aranges in /tmp/tinygo942108089/main entry at offset 0 debug_info_offset 0 does not exists, ignoring .debug_aranges.
:3333: Connection timed out.
"monitor" command not supported by this target.
You can't do that when your target is `exec'
"monitor" command not supported by this target.

(gdb) target extended-remote  192.168.10.100:3333
Remote debugging using 192.168.10.100:3333
warning: multi-threaded target stopped without sending a thread-id, using first non-exited thread
0x10001a4c in runtime[scheduler] ()
(gdb) info sources
              *略*
/usr/local/lib/tinygo/src/runtime/scheduler_any.go, /usr/local/lib/tinygo/src/runtime/time.go,
/usr/local/go/src/time/format.go, /usr/local/go/src/time/time.go, /usr/local/go/src/time/zoneinfo.go,
/usr/local/go/src/time/zoneinfo_read.go, /usr/local/go/src/time/zoneinfo_unix.go,
/mnt/c/cygwin64/home/<user_id>/lang/tgo/test/blinky1.go

(gdb) info func
              *略*
0x10001c94  main.main

ソースの情報やシンボル情報は読み込めている。
HardwareBreadkpintを main.mainに設定する

(gdb) hb main.main
Hardware assisted breakpoint 3 at 0x10001c96
(gdb) i b
Num     Type           Disp Enb Address    What
3       hw breakpoint  keep y   0x10001c96 <main.main+2>

実行してみる。止まるには止まったが、ソースと一致しないようだ。
(再ビルドしたので番地が違っている?)

(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/tinygo942108089/main
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00000184 msp: 0x20041f00
[New Thread 1]
[Switching to Thread 1]
Thread 2 hit Breakpoint 3, 0x10001c96 in main.main ()

(gdb) l
1       <unknown>: No such file or directory.

適当に走らせてCtrl-Cで止めるとソースを参照できる

(gdb) l
171                     // will be executed soon.
172                     if sleepQueue != nil && now-sleepQueueBaseTime >= timeUnit(sleepQueue.Data) {
173                             t := sleepQueue
174                             scheduleLogTask("  awake:", t)
175                             sleepQueueBaseTime += timeUnit(t.Data)
176                             sleepQueue = t.Next
177                             t.Next = nil
178                             runqueue.Push(t)
179                     }
180

想像するに、、main.mainで止めずに、main内の有効な行で止めないといけないのでは(スタックがまだ積まれていないから。。とか。。)
blinky1.goの12行目にhardware breakを設定してみる

(gdb) list blinky1.go:10
5       import (
6               "machine"
7               "time"
8       )
9
10      func main() {
11              //led := machine.LED
12              led := machine.GP15
13              led.Configure(machine.PinConfig{Mode: machine.PinOutput})
14              for {
Warning: the current language does not match this frame.
(gdb) hb blinky1.go:12
Hardware assisted breakpoint 1 at 0x10001cb6: file /home/<user_id>/lang/tgo/test/blinky1.go, line 16.
(gdb) i b
Num     Type           Disp Enb Address    What
1       hw breakpoint  keep y   0x10001cb6 in main.main at /home/<user_id>/lang/tgo/test/blinky1.go:16
(gdb)

ソース行内のコードに対して、ブレークポイントを設定すると、停止してリスト表示も行えた。*1

(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/tinygo3924727989/main
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00000138 msp: 0x20041f00
[New Thread 2]

Thread 1 hit Breakpoint 1, main.main () at /home/<user_id>/lang/tgo/test/blinky1.go:16
16                      time.Sleep(time.Millisecond * 500)
(gdb) l
11              //led := machine.LED
12              led := machine.GP15
13              led.Configure(machine.PinConfig{Mode: machine.PinOutput})
14              for {
15                      led.Low()
16                      time.Sleep(time.Millisecond * 500)
17
18                      led.High()
19                      time.Sleep(time.Millisecond * 500)
20              }
(gdb)

まとめ:RPi上でOpenOCDを走らせて、ノートPC上のWSL上のUbuntu でtinygoの開発環境を構築して、その中でgdbを走らせて、gdbからRPi上のOpenOCDとリモート接続することでシンボリックデバッグが可能となった。

[gdb on Ubuntu on WSL]
[Windows10] <--------------LAN----> [OpenOCD on RPi]<------SWD--->[Pico]
以下の写真はRPiとPicoをSWDで接続して動作確認しているところ

■追記
うまくいったといっても、Ubuntu上ではOpenOCDが起動された空振りしている状態なので、これはあくまでもWorkaroundのレベルだ。以下はプロセス一覧を確認したところ。ローカル環境でOpenOCDを走らせるのではなく、リモートのOpenOCDと接続できるように作込みが必要

$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0    904   532 ?        Sl   18:04   0:00 /init
root         8  0.0  0.0    904    96 ?        Ss   18:04   0:00 /init
root         9  0.0  0.0    904    96 ?        S    18:04   0:02 /init
xxx         10  0.0  0.1   9276  5516 pts/0    Ss   18:04   0:00 -bash
xxx         998  0.3  2.4 1014888 115516 pts/0  Tl   19:52   0:10 tinygo gdb --target pico
xxx        1099  0.0  0.0      0     0 pts/0    Z    19:52   0:00 [openocd] <defunct>                           <<<< 無駄に走っているOpenOCDプロセス
xxx        1100  0.0  0.9 395864 42404 pts/0    Tl   19:52   0:02 gdb-multiarch /tmp/tinygo3924727989/main -ex target ext
xxx        1108  0.0  0.0  10456  3168 pts/0    R+   20:48   0:00 ps aux

gdb起動オプション

gdb-multiarch /tmp/tinygo3924727989/main -ex target extended-remote :3333 -ex monitor halt -ex load -ex monitor reset halt
  • ex target extended-remote :3333 で起動しているのを -ex target extended-remote :3333 となればいいのだが、、リモートのOpenOCDのIPを指定するオプションがあればいいのだけど。。見つけられず。

*1:行がずれているのはなぜか。最適化されているから?それとも、ビルドしてデバッグ対象ファイルとFlash上のコードが不一致のため?