MacOS X (Big Sur) + Virtual BOX + Vagrant + VSCode で ArduPilot をデバッグする

Mac OS X (Big Sur) で ArduPilot のデバッグを行おうとしたときにいろいろハマったので、最終的に辿り着いた手順を自分用のメモとして残しておきます。
同じような環境をお使いの方のお役に立てれば嬉しいです。

# 1. ツール類のインストール
下記のツールを公式サイトから最新版をダウンロードしてインストールします。

* [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
* [Vagrant](https://www.vagrantup.com/downloads)
* [XQuartz](https://www.xquartz.org/)
* [Visual Studio Code](https://code.visualstudio.com/download)

VirtualBox は、OS X hosts を、Visual Studio Code は Mac 用を選択します。
![virtualbox.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/a3536246-bd3e-638c-7402-e19752c32d1a.png)
![vscode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/42f15945-c9ce-79dd-9f21-85e372f44fa4.png)

# 2. zsh ターミナルで実行すること
## 2.1 ardupilot のクローン
公式の最新版をそのまま使う場合

```
% git clone https://github.com/ArduPilot/ardupilot
```

公式からフォークした独自のリポジトリを使う場合

```
% git clone https://github.com/自分のアカウント/ardupilot
```

## 2.2 vagrant による ubuntu 20.04 LTS の設定と起動

```
% cd ardupilot/Tools/vagrant
% vagrant up focal
```

`focal` をつけない場合、18.04 LTS (bionic64) がインストールされます。 Big Sur と bionic64 は相性が悪いのか、インストール中に MacOS ごとハングしたり、SITLを動かしたときに `--console` がうまく動かない「ことがある」など、弊害が多いのでおすすめできません。

はじめて `vagrant up focal` を実行する場合、ubuntu のインストールや ardupilot をビルドするための環境セットアップも自動的に行われるので、しばらく時間がかかります。じっくり待ちましょう。

## 2.3 ssh の config 設定

vagrant の SSH 設定を VSCode から使えるように引き出します。

```
% vagrant ssh-config focal >> ~/.ssh/config
```

# 3. VSCode で実行すること
次に、VSCode 側の設定を行います。

## 3.1 SSH ログイン
VSCode のリモートエクスプローラから focal/vagrant を右クリックして SSH ホストとの接続をオープンします。
![vagraht_ssh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/7306294b-521f-f576-27fe-e8bea20cab8d.png)

画面左下に `SSH:focal` が表示されていれば成功です。
![vagrant_login.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/a644de71-f1f8-d50a-db68-65e2cb71f908.png)

## 3.2 GDB サーバとの接続設定
ここから先は Windows の WSL から ubuntu を起動する場合と同様です。

VSCode で `/vagrant/.vscode/launch.json` を開くか、メニューバーの「実行」→「構成を開く」で、GDBサーバの設定を下記のように書き換えます。
![launch_json.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/ac0cae18-b488-f4a5-7af9-6613c17a3ec3.png)

## 3.3 ブレークポイントの設定
使う機種に合わせたメインループにブレークポイントを設定します。
ここでは、 Copter を使う前提で、 `ardupilot/ArduCopter/Copter.cpp` の `Copter::fast_loop()` にブレークを設定しました。
![breakpoint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/29bdcfad-dfe8-a057-fe72-49c3f5348586.png)

## 3.4 コンソールの表示ディスプレイの設定
VSCode のターミナルで、一度だけ下記コマンドを実行します。

```
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
```

## 3.5 シミュレータの起動とデバッグの開始

VSCode のターミナルで、

```
$ sim_vehicle.py -v ArduCopter --console --map -D
```

を実行します。
※ QGroundControl など、地上局アプリが準備できている場合は `--map` は設定しなくてもよいと思います。
![sim_vehicle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/36f2d2f8-560a-67f2-9531-6be92963a862.png)
のような画面になったら成功です。

`sim_vehicle` を実行した時に

```
Connect tcp:127.0.0.1:5760 source_system=255
[Errno 111] Connection refused sleeping
[Errno 111] Connection refused sleeping
[Errno 111] Connection refused sleeping
[Errno 111] Connection refused sleeping
Failed to connect to tcp:127.0.0.1:5760 : [Errno 111] Connection refused
SIM_VEHICLE: Killing tasks
```

のようなエラーが出て終了してしまった場合、

* もう一度 `sim_vehicle` を実行してみる
* 解決しなければ `vagrant halt ; vagrant up focal` で vagrant を再起動してみる
（このあと、 `sim_vehicle` の１回目は同じエラーが起きるので最低２回は実行してみる）

ことで解決しています。

## 3.6 GDB からアタッチ
デバッグメニューから実行中の `arducopter` にアタッチします。
![attach.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/fbfac6cf-183a-0902-4b66-bb5295a5a84e.png)
下記のように、先ほど設定したブレークポイントで止まれば成功です。
![break.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/553439ec-73a2-ea6f-8cc8-54989a75fe87.png)


# おまけ

デフォルトで、仮想OS環境に割り当てるコアは2個に設定されています。
例えば、Mac側のCPUコア数が8個ある場合は、割り当てるコアを4個にすればコンパイル時間の短縮が期待できます。
`ardupilot/Vagrant` ファイルを下記のように変更します。
![mod_VagrantFile.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2012220/420c9622-87d9-affc-8da9-43cce056fb51.png)
VittualBox Manager で設定しても、Vagrant ファイルの内容で上書きされてしまうので注意しましょう。
書き換えた後は、 zsh ターミナルの方で `vagrant halt` と `vagrant up focal` を実行して、仮想OSを再起動します。