M1 Macでソースコードを見ながら「ゼロからのOS自作入門」のデバッグをする。

#M1 Macでgdbが使えなくて困っている人向けの記事
現在（2021/12/06）、gdbはM1 Macに対応していないようでインストールできません。そのため、lldbを使ってデバッグします。terminalやVS Codeでソースコードを見ながらデバッグできます。

デバッグの手順は[launching-a-locally-built-process-on-the-remote-machine]にも書いてあります。

M1 Macでqemu-system-x86_64を動かし、その上でmikanosを起動しています。
Docker環境ではありません。
mikanosのMakefile等の環境構築は[ここ](https://qiita.com/yamoridon/items/4905765cc6e4f320c9b5)を参考にしてください。

qemu-system-x86_64の起動は著者が公開している、[run_qemu.sh](https://github.com/uchan-nos/mikanos-build/blob/master/devenv/run_qemu.sh)を利用ます。
`$QEMU_OPTS`という環境変数を読み込んでいるので、ターミナルや`~/.zshrc`で環境変数をセットします。
`export QEMU_OPTS="-gdb tcp::12345 -S"`
-Sでデバッガが接続するまで、qemuを止めることができます。

環境変数をセットしたら、run_qemu.shを実行して、qemu-system-x86_64を起動します。
その後、別のターミナルでkernelフォルダに移動して、次のような順番でコマンドを叩いていけば、ソースコードを見ながらデバッグできるようになります。

```terminal
lldb
file kernel.elf
gdb-remote localhost:12345
breakpoint set --name KernelMainNewStack
c
```

章が進んでいなくて、main.cppのエントリポイントがKernelMainの場合は、KernelMainNewStackではなく、KernelMainを指定してください。

実際の画面はこんな感じです。

```terminal
hoge@hogeAir kernel % lldb                               
(lldb) file kernel.elf
Current executable set to '/Users/hoge/workspace/mymikanos/kernel/kernel.elf' (x86_64).
(lldb) gdb-remote localhost:12345
Process 1 stopped
* thread #1, stop reason = signal SIGTRAP
    frame #0: 0x000000000000fff0
->  0xfff0: addb   %al, (%rax)
    0xfff2: addb   %al, (%rax)
    0xfff4: addb   %al, (%rax)
    0xfff6: addb   %al, (%rax)
(lldb) breakpoint set --name KernelMainNewStack
Breakpoint 1: where = kernel.elf`::KernelMainNewStack(const FrameBufferConfig &, const MemoryMap &) + 19 at main.cpp:136:43, address = 0x0000000000105833
(lldb) c
Process 1 resuming
Process 1 stopped
* thread #1, stop reason = breakpoint 1.1
    frame #0: 0x0000000000105833 kernel.elf`::KernelMainNewStack(frame_buffer_config_ref=0x000000003feac7e0, memory_map_ref=0x000000003feac8f0) at main.cpp:136:43
   133 	KernelMainNewStack(const FrameBufferConfig &frame_buffer_config_ref,
   134 	                   const MemoryMap &memory_map_ref)
   135 	{
-> 136 	    FrameBufferConfig frame_buffer_config{frame_buffer_config_ref};
   137 	    MemoryMap memory_map{memory_map_ref};
   138 	
   139 	    switch (frame_buffer_config.pixel_format)
(lldb)  
```

lldbを起動してから実行するコマンドを、テキストファイルに書いて、それをlldb起動時に指定することもできます。リモート接続等の毎回やる作業を自動化できます。
例えば、lldb.txtに次のようにコマンドを書いて、

```terminal
file kernel.elf
gdb-remote localhost:12345
breakpoint set --name KernelMainNewStack
```

このようにファイル指定できます。

```terminal
lldb -s ./lldb.txt
```

lldbのコマンドはこの辺りが参考になるかもしれません。
[lldb で使えるコマンド一覧](https://yokaze.github.io/2018/01/06/)

# VS Codeでデバッグする方法
まず、[CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)をインストールします。
[公式サイト](https://github.com/vadimcn/vscode-lldb/blob/v1.6.10/MANUAL.md#connecting-to-a-gdbserver-style-agent)を参考にlaunch.jsonに次のように記述していきます。(自分はmikanosフォルダをvscodeで開いてるので、mikanos/.vscode/launch.jsonというファイル構成です。)

```launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "remote launch",
            "type": "lldb",            
            "request": "custom",
            "targetCreateCommands": ["target create ${fileDirname}/kernel.elf"],
            "processCreateCommands": ["gdb-remote localhost:12345"],
        }
    ]
}
```
上記と同じように環境変数を設定してrun_qemu.shを起動します。
その後、main.cppに適当にブレイクポイントを設定してF5を押せば、デバッグできます。
![Screen Shot 2021-12-09 at 22.02.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102550/1b00a5b3-ab9a-275c-2421-d6f680ea0f5d.png)
アセンブリが続くので、コンティニューして、main.cppのブレイクポイントまで行くと、デバッグの時間です🎉
![Screen Shot 2021-12-09 at 21.57.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102550/1fa73f50-ec64-97f6-1be7-aea835adbf82.png)


# 感想
lldbのコマンドを知ってしまえば簡単なんですが、英語力の問題か想像以上に時間がかかってしまいました。
[Testing LLDB using QEMU](https://lldb.llvm.org/use/qemu-testing.html)という記事もあったのですが、Ubuntuを動かしたりしているので、自作OSのデバッグに利用したい場合はどうすればいいんだろうかと思ってしましました。今思うと、[launching-a-locally-built-process-on-the-remote-machine](https://lldb.llvm.org/use/remote.html#launching-a-locally-built-process-on-the-remote-machine)を初手で試してみるのが正解ぽいでした。
qemuの起動時に./disk.imgを指定して、lldbの起動時にもkernel.elfを指定する必要があるのが、少し不思議です。自動で決定されないのかとうい気持ちになります。

lldbのことをよくわかっていないので、便利になるカスタム等あったら教えてください！
後は、プログラムをバグらせて、頻繁にKernelMainNewStackに飛ばされています。現状適当に当たりをつけたり、ブレークポイント2分探索してるんですが、いい感じにデバッグする方法ないんですかね。

ちなみにDockerの利用を検討したんですが、makeが遅かったり、`mount: ./mnt: mount failed: Operation not permitted.`のエラーが消せなかったり、qemu起動時の`start pxe over ipv4`のエラーが消せなかったのでDockerを使わない方法になりました。

最後になりますが、面白い本を書いてくださりありがとうございます！