Raspberry PiのクロスコンパイルとVSCodeによるリモートデバッグ開発環境の構築

# この記事について
以前、この記事(https://qiita.com/take-iwiw/items/46119bb7d41c6030d34f )で、お手軽にラズパイ用のC/C++開発環境の構築を行いました。この時は、ビルドやデバッグは全てラズパイ上で行いました。

今回は、Ubuntu上でのクロスコンパイルと、gdbserverを使用したリモートデバッグ方法について記します。最終的には、ホストPC上のVSCodeからGUI操作でラズパイのデバッグをできるようにします。デバッグはUbuntu, Windowsのどちらからもできるようにしました。

なお、本記事ではラズパイを対象に記載していますが、一般的な組み込みLinuxのクロス開発にも適用できると思います。

## 環境
- Target
	- Raspberry Pi (IPアドレスなどは適宜読み替えてください)
		- IPアドレスは192.168.1.89
		- SSHは有効済み
- Host
	- Ubuntu 16.04 on VirtualBox on Windows 10
		- クロスコンパイル用
		- デバッグ用
	- MSYS2 64-bit
		- デバッグ用 (デバッグをWindowsから行いたい人用)

# クロスコンパイル (on Ubuntu)
## クロスコンパイラの取得
基本的に1回だけやればOKです。

```sh:Ubuntu上のターミナル
sudo apt-get update
sudo apt-get install build-essential libncurses-dev git git-core
mkdir ~/raspberry
cd ~/raspberry
git clone https://github.com/raspberrypi/tools
```

## ビルド
下記のような~/work/pi/sample01/sample01.cppをビルドしてみます。

```cpp:sample01.cpp
#include <stdio.h>
int main()
{
	printf("Hello World\n");
	for (int i = 0; i < 10; i++)
		printf("i = %d\n", i);
	return 0;
}
```

```sh:Ubuntu上のターミナル
mkdir ~/work/pi/sample01 && cd ~/work/pi/sample01
code sample01.cpp &
ARCH=arm ~/raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-g++ sample01.cpp
# ARCH=arm ~/raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-g++ sample01.cpp
scp a.out pi@192.168.1.89:.
```

最後のscpコマンドで転送したa.outをラズパイ上で実行できます。
32-bit環境の場合は、`raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin`の方がいいかもしれません。

# リモートデバッグ
こちらの記事を大変参考にさせていただきました。(https://qiita.com/tetsu_koba/items/ebbac47e3fb43c86f412 )。ありがとうございます。

## 準備
基本的に1回だけやればOKです。

### gdbserverのインストール (on ラズパイ)
```sh:ラズパイ上のターミナル
sudo apt-get install gdbserver
```

### ARM用gdbバイナリの作成 (on Ubuntu)
```sh:Ubuntu上のターミナル
mkdir ~/temp && cd ~/temp
wget http://ftp.jaist.ac.jp/pub/GNU/gdb/gdb-8.0.1.tar.gz
tar xf gdb-8.0.1.tar.gz
cd gdb-8.0.1
mkdir build && cd build
sudo apt-get install libexpat1-dev expat
../configure --target=arm-buildroot-linux-gnueabi  --with-expat
make -j4
sudo make install
```

## リモートデバッグする
### gdbserverの起動 (on ラズパイ)
```sh:ラズパイ上のターミナル
gdbserver --multi :5555
```

### gdb実行 (on Ubuntu)
デバッグしたい実行ファイルと同じところに、以下のようなファイル(~/work/pi/sample01/gdb_load)を作っておきます。

```txt:gdb自動実行用スクリプト
target extended-remote 192.168.1.89:5555
file ./a.out
remote put ./a.out /home/pi/a.out
set remote exec-file /home/pi/a.out
start
```

gdb_loadとa.outと同じパスで下記コマンドを実行すると、デバッグが始まります。
デバッグ情報はUbuntu側のターミナルに、printf等の出力はラズパイ側のターミナルに出力されます。

```sh:Ubuntu上のターミナル
arm-buildroot-linux-gnueabi-gdb -x gdb_load
# ↓はgdb内のプロンプト
>>> n
```

#### デバッグをやり直すとき (on Ubuntu)
基本的にラズパイ側で起動したgdbserverは常時起動でOKです。そのため、ラズパイ側での操作は不要です。
Ubuntu側のgdbターミナルで、`q`をして、再度gdbを実行させれば再起動します。


## VSCodeからリモートデバッグする (on Ubuntu)
gdbコマンドによるデバッグでは、よっぽど慣れた人でないと効率が悪いと思います。VisualStudio CodeからGUIによってデバッグできるようにします。一般のアプリケーション開発と同様に、ステップ実行やブレークポインタの設定をGUIからできるようにして、スコープ内の変数なども自動で参照できるようにします。

### VisualStudio Codeの設定 (on Ubuntu)
`~/work/pi/sample01/`ディレクトリをVSCodeから開き、F5キーを押してデバッグしようとします。その後、デバッグ設定を作るように聞かれるので、C++(GDB/LLDB)を選択します。

![01.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/775fd5db-660e-67a3-c96b-57352a891b68.jpeg)

launch.jsonが開かれるので、下記のように編集します。

```json:launch.json
{
	"version": "0.2.0",
	"configurations": [
		{
			"name": "(gdb) Launch",
			"type": "cppdbg",
			"request": "launch",
			"program": "${workspaceFolder}/a.out",	// ★編集
			"args": [],
			"stopAtEntry": true,					// ★編集(お好みで)
			"cwd": "${workspaceFolder}",
			"environment": [],
			"externalConsole": false,
			"MIMode": "gdb",
			"miDebuggerPath": "arm-buildroot-linux-gnueabi-gdb",		// ★追加
			"setupCommands": [											// ★追加
				{"text": "target extended-remote 192.168.1.89:5555"},
				{"text": "file a.out"},
				{"text": "remote put a.out /home/pi/a.out"},
				{"text": "set remote exec-file /home/pi/a.out"}
			]
		}
	]
}
```

### VisualStudio Codeからデバッグする (on Ubuntu)
再度F5キーを押すと、デバッグが始まります。以後、本ディレクトリのプロジェクトをデバッグするときはF5キーを押すだけでOKです。
printf等の出力はラズパイ側のターミナルに出力されます。

![02.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/71984da2-d902-5cfe-cb20-0c52213116fc.jpeg)


# Windowsからリモートデバッグする
一般的に、ターゲットがLinuxの場合には開発用HOST環境もLinuxだと思います。しかし、何らかの理由でコードの編集やデバッグをWindowsPCで行いたいというのはよくあると思います。(例えば、ビルドはLinuxのビルドサーバーで行い、開発者のWindowsPCにビルドされたバイナリをダウンロードして、それをターゲットに書き込む、などはよくある運用だと思います)。
このような環境では、デバッグのためだけにUbuntuを入れるのが面倒だと思いますので、Windows上でもできるようにします。ターミナル操作のために、MSYS2を使用します。

## ARM用gdbバイナリの作成 (on Windows(MSYS2))
下記コマンドでARM用のgdbバイナリを作成します。

```sh:MSYS上のターミナル
mkdir ~/temp && cd ~/temp
wget http://ftp.jaist.ac.jp/pub/GNU/gdb/gdb-8.0.1.tar.gz
tar xf gdb-8.0.1.tar.gz
cd gdb-8.0.1
mkdir build && cd build
../configure --target=arm-buildroot-linux-gnueabi  --with-expat
make -j4
make install
```

_メモ
一度ビルドは成功したのですが、その後pythonのインストール(`pacman -S python2`)をしたり色々してたら、再ビルドが出来なくなってしまいました。。。_

## VisualStudio Codeの設定 (on Windows)
基本的にはUbuntuでやったのと全く同じです。ソースコードとビルド済みARM用バイナリが`C:/Users/tak/Desktop/win_share/sample01/`にあるとします。

launch.jsonを下記のように編集します。

```json:launch.json
{
	"version": "0.2.0",
	"configurations": [
		{
			"name": "(gdb) Launch",
			"type": "cppdbg",
			"request": "launch",
			"program": "${workspaceFolder}/a.out",		// ★編集
			"args": [],
			"stopAtEntry": true,						// ★編集(お好みで)
			"cwd": "${workspaceFolder}",
			"environment": [],
			"externalConsole": false,
			"MIMode": "gdb",
			"miDebuggerPath": "C:/msys64/mingw64/bin/arm-buildroot-linux-gnueabi-gdb.exe",	// ★追加
			"setupCommands": [														// ★追加
				{"text": "target extended-remote 192.168.1.89:5555"},
				{"text": "file C:/Users/tak/Desktop/win_share/sample01/a.out"},
				{"text": "remote put C:/Users/tak/Desktop/win_share/sample01/a.out /home/pi/a.out"},
				{"text": "set remote exec-file /home/pi/a.out"}
			]
		}
	]
}
```

注意点として、自分でビルドしたARM用gdbにはWindows上でのパスを通していないので、フルパス指定する必要があります。また、ロードするバイナリファイルもフルパス指定する必要がありました。`${workspaceFolder}`を使って省略できないか試したのですが、上手くいきませんでした。

## VSCodeからリモートデバッグする (on Windows)
再度F5キーを押すことで、下記のようにデバッグできます。

![03.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/0547c72c-fea3-f3bf-6024-3cc2b899ed11.jpeg)

## ラズパイ用の僕の運用
僕はメインPCがWindowsなので、ビルドだけラズパイで行い、デバッグはWindows上のVSCodeでやっています。

1. Windows上のVSCodeで、コードを書く
2. Windows上のVSCodeで、sftpエクステンション機能でコードをラズパイにアップロード
3. ラズパイ上で、ビルド(./a.outを作成)
	- `g++ -O0 -g3 sample01.cpp`
4. Windows上のVSCodeで、sftpエクステンション機能でバイナリ(./a.out)をダウンロード
5. Windows上のVSCodeで、デバッグ実行

# その他
- gdbserverを終了するときは、ホスト側のgdbプロンプトから`monitor exit`
- 共有ライブラリの場所を指定するときは、`set sysroot ./`
- ビルド環境とgdb実行環境でソース構成やパスが異なるときは、`set substitute-path from-path  to-path`
	- 環境が違っても、一応うまく動いてはくれた。相対パスがあっていたら多少は大丈夫そう。