ZYNQのLinuxアプリをVSCodeでデバッグする方法

前編の **[ZYNQのLinuxアプリをVSCodeでビルドする方法](https://qiita.com/nahitafu/items/5a7b42f9cc2e26ffa3b9)** でビルド方法を紹介したので、次はデバッグできるようにしましょう。

# VSCode側の設定
C++のプログラムを編集している画面で`F5`を押します。Launchのキーだそうですが、launchがまだ設定されていないので下のようなメニューが開きます。GDBかWindowsかはどちらでもいい(どうせ後で書き換えるから)のですが、とりあえずはC++ (GDB/LLDB) を選んでおきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542455/02b65360-c9ac-f16f-9132-b8cd20906cb0.png)
launch.json が作られるので、以下のように書き換えます。

```launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Remote unit test",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}\\${fileBasenameNoExtension}.elf",
            "MIMode": "gdb",
            "miDebuggerPath": "D:/Xilinx/SDK/2018.3/gnu/aarch32/nt/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-gdb.exe",
            "miDebuggerServerAddress": "cszmini:9999",
            "args": [],
            "cwd": "${workspaceFolder}",
            "stopAtEntry": false,
            "setupCommands": [
                {
                    "description": "gdb の再フォーマットを有効にする",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        },
    ]
}
```

環境に応じて書き換えるべきポイントは以下の2点です。
### デバッガのパスの設定
Windows上のARM用GDBのパスを指定します。

```
"miDebuggerPath": "D:/Xilinx/SDK/2018.3/gnu/aarch32/nt/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-gdb.exe",
```

### ターゲットのアドレスの設定
ターゲットのIPアドレス(またはホスト名)と、ポート番号を指定します。

```
"miDebuggerServerAddress": "cszmini:9999",
```

```
"miDebuggerServerAddress": "192.168.1.4:12345",
```

のようにします。


## とりあえず実行
F5を押すとlaunchに書かれたコマンドが実行されます。この時点ではターゲットボードでLinuxは動いていないのでエラーダイアログが出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542455/cd1267f0-4d44-092b-fb4a-ca521a0c1314.png)



# Linux側の準備
`apt install gdbserver`で、gdbserverをインストールしておきます。
Linuxのシェル上で、

```
# gdbserver localhost:9999 ./test.elf
```

と入力します。上のコマンドはgdbserverを起動して、ポート9999で待ち受けてtest.elfをアタッチします。

# VSCodeでいざLaunch
いよいよVSCodeで`F5`を押してデバッグ開始です。

VSCode上でgdbが起動し、リモートのターゲットボードに接続されます。ステップ実行や変数の参照ができ、デバッグできるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542455/6f8f9dad-f672-5afa-fe14-8083d0054acb.png)

対するLinux側ではプログラムがリモート操作されてステップ実行されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542455/3e94df00-6486-cf08-2efd-7501da94e8a5.png)

プログラムが終了するか、VSCodeで <span style="color:#ff0000;">□</span> を押すと終了します。

# もっと便利にしたい
## ユーザ作成のライブラリを使う
ユーザが作成したライブラリを使うには、プロジェクトのフォルダにライブラリを入れ、`tasks.json`のargsに-Lと-lオプションを追加します。次の設定ではcszapiというライブラリが使われるようになります。

```
"args": [
  "-g",
  "${file}",
  "-L${workspaceFolder}",
  "-lcszapi",
  "-o",
  "${fileDirname}\\${fileBasenameNoExtension}.elf"
],
```

また、ヘッダファイルのIntelliSenseが動くようにするには`c_cpp_properties.json`を編集して、インクルードパスを通すようにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542455/c9e9e9ea-a6aa-ccbd-f553-1e655ac3f353.png)
これでユーザ作成のライブラリが使えるようになります。

## ビルドが終わったら自動的にターゲットにコピーしたい
クロスコンパイルなので、出来上がったプログラムはターゲットボード上にコピーしなければなりません。毎回毎回scpや、エクスプローラでCTRL+C,CTRL+Vするのも面倒なので、tasks.jsonに自動化の処理を書いてあげましょう。
tasks.jsonは、"tasks": [で複数のタスクを登録できるようになっていますが、`"group": { "kind": "build",　"isDefault": true}`が付いているタスクが CTRL*SHIFT+B で実行されるようになっています。
また、各タスクには "dependsOn": というパラメータが設定できて、これを使うとタスクの依存関係を設定できます。

つまり、ターゲットボード上にファイルを転送するというDeployというisDefaultなタスクを作り、DeployはBuildに依存するようにすれば、CTRL+SHIFT+Bでビルドとファイルコピーが可能になります。

```(自動Deploy版)tasks.json
{
    // tasks.json 形式の詳細についての資料は、
    // https://go.microsoft.com/fwlink/?LinkId=733558 をご覧ください
    "version": "2.0.0",
    "tasks": [
		{
			"label": "Deploy",
			"type": "shell",
			"command": "cp",
			"args": [
				"${fileDirname}\\${fileBasenameNoExtension}.elf",
				"\\\\cszmini\\Share"
			],
			"problemMatcher": [],
			"dependsOn": "Build by ARM g++",
			"group": {
				"kind": "build",
				"isDefault": true
			}
		},
		{
			"type": "shell",
			"label": "Build by ARM g++",
			"command": "D:/Xilinx/SDK/2018.3/gnu/aarch32/nt/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-g++.exe",
			"args": [
				"-g",
				"${file}",
                "-L${workspaceFolder}",
                "-lcszapi",
				"-o",
				"${fileDirname}\\${fileBasenameNoExtension}.elf"
			],
			"options": {
				"cwd": "D:/Xilinx/SDK/2019.1/gnu/aarch32/nt/gcc-arm-linux-gnueabi/bin"
			},
			"problemMatcher": [
				"$gcc"
			],
			"group": "build"
		}
	]
}
```

dependsOnの参照はlabelを使って行われるようです。

## F5でビルドとgdbの起動ができるようにしたい
launch.jsonのconfigurationsに登録されたタスクに、`"preLaunchTask": `というパラメータを設定しておけば、このタスクが実行される前に実行すべきタスクを指定できます。
したがって、`"preLaunchTask": "Deploy"`と書けば、GDB接続の前にDeployタスクが実行されて、ビルドとファイルの転送が行われます。

しかし、Linux上ではgdbserverプロセスを走らせなければならないのですが、このgdbserverを起動する前にtest.elfはターゲットボード上になければなりません。また、gdbserverを停止するにはリモートで接続してプログラムが終了するかkillするしかなく、ちょっと面倒です。

結論を言うと、preLaunchTaskを書いてビルドを自動実行するのではなく、リモートデバッグの場合は「ビルド＆ファイル転送」と「デバッガ起動」は分けたほうがいいです。

少々面倒ですが、プログラムを書いてからそれを実行するまでに

* `CTRL+SHIFT+B`でビルド＆実行ファイル転送
* Linux上でgdbserver起動
* `F5`でデバッグ開始

と、3ステップ必要です。gdbは巨大なプログラムだから、ちゃんと調べればファイル転送できるのかな？

# GLIBCのバージョンが合わないとどうなる？
GLIBCのバージョンが合わない場合、つまりUbuntu 14のシステムをXSDK 2019.1に入っているarm-linux-gnueabihf-gdbでデバッグしようとすると、

```
readchar: Got EOF
```

というエラーが出てGDBが接続できません。

実際にやってみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542455/acec5c0c-73ec-b6a5-ff81-ff47a036588b.png)
こうなります。

# まとめ
VSCodeでZYNQのLinuxで動くARMのプログラムが書けて、デバッグまでできるようになりました。
これでXSDKを卒業できるかな！？