# 2019q1 Homework4 (smallsys)
contributed by < `0xff07` >
## buildroot 的預設配置
首先先從 github 上面把 [buildroot](https://github.com/buildroot/buildroot) 載下來:
```shell
$ git clone "https://github.com/buildroot/buildroot.git"
```
接著去 [說明手冊](https://buildroot.org/downloads/manual/manual.html#requirement) 看看有哪些套件需要安裝。
接下來到 buildroot 目錄下看看有什麼東西。看了一下 readme:
```
To build and use the buildroot stuff, do the following:
1) run 'make menuconfig'
2) select the target architecture and the packages you wish to compile
3) run 'make'
4) wait while it compiles
5) find the kernel, bootloader, root filesystem, etc. in output/images
You do not need to be root to build or run buildroot. Have fun!
Buildroot comes with a basic configuration for a number of boards. Run
'make list-defconfigs' to view the list of provided configurations.
```
裏面有大致的使用流程,而且也有一些預設的配置。使用:
```shell
$ make list-defconfigs
```
會發現輸出很多配置,以及一些硬體上預設的資訊。比如:
```shell
pine64_sopine_defconfig - Build for pine64_sopine
qemu_aarch64_virt_defconfig - Build for qemu_aarch64_virt
qemu_arm_versatile_defconfig - Build for qemu_arm_versatile
qemu_arm_versatile_nommu_defconfig - Build for qemu_arm_versatile_nommu
qemu_arm_vexpress_defconfig - Build for qemu_arm_vexpress
.
.
.
qemu_riscv32_virt_defconfig - Build for qemu_riscv32_virt
qemu_riscv64_virt_defconfig - Build for qemu_riscv64_virt
.
.
.
```
隨便跑一個看看:
```shell=
$ make qemu_aarch64_virt_defconfig
```
接著看看需要怎麼使用 qemu 來執行編出來的東西:
```c=
$ cat board/qemu/aarch64-virt/readme.txt
```
出現:
```
Run the emulation with:
qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -smp 1 -kernel output/images/Image -append "root=/dev/vda console=ttyAMA0" -netdev user,id=eth0 -device virtio-net-device,netdev=eth0 -drive file=output/images/rootfs.ext4,if=none,format=raw,id=hd0 -device virtio-blk-device,drive=hd0
The login prompt will appear in the terminal that started Qemu.
Tested with QEMU 2.12.0
```
於是執行看看:
```shell
$ qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -smp 1 -kernel output/images/Image -append "root=/dev/vda console=ttyAMA0" -netdev user,id=eth0 -device virtio-net-device,netdev=eth0 -drive file=output/images/rootfs.ext4,if=none,format=raw,id=hd0 -device virtio-blk-device,drive=hd0
```
接著就會出現:
```
Booting Linux on physical CPU 0x0000000000 [0x410fd034]
Linux version 4.19.16 (f@OxB16BOOB5) (gcc version 7.4.0 (Buildroot 2019.05-git-00547-g556ad6c25b)) #1 SMP Wed Apr 3 01:51:53 PDT 2019
Machine model: linux,dummy-virt
...
OK
Welcome to Buildroot
buildroot login:
```
看來這個設定的 kernel 並沒有到 5 以上。接著登入看看:
```shell
Welcome to Buildroot
buildroot login: root
#
```
接就可以進入 shell 了。不過這個就只是預設的一些選項。
## 調整至 Linux 5.0.x
從 `qemu_aarch64_virt_defconfig` 進行調整。首先執行:
```shell
$ make qemu_aarch64_virt_defconfig
```
接著使用:
```shell
$ make menuconfig
```
進一步進行配置:主要是加上 debugger,以及盡可能的加上 debug symbol :
1. 去把 kernel --> Linux Kernel -->Latest version (5.0) 選項調整成 5.0
2. Toolchain --> Build cross gdb for the host
3. Toolchain --> Custom kernel headers series (5.0.x)
4. Target packages ---> Debugging, profiling and benchmark ---> gdbserver 相關選項。會發現可能需要仰賴 toolchain 的一些功能。以我的為例,gdbserver 那邊的選項會出現:
```
*** gdb/gdbserver needs a toolchain w/ threads, threads debug ***
*** gdb/gdbserver >= 8.x needs a toolchain w/ C++, gcc >= 4.8 ***
```
這時去 Toolchain 那邊的選項修改 Toolchain 的設定。選取:
* Toolchain ---> Enable WCHAR support
* Toolchain ---> Thread library debugging
* Toolchain ---> Enable C++ support
然後就會發現 gdb 的選項可以選了:
Toolchain ---> gdb
5. Build options ---> build packages with debugging symbols
6. Build options ---> 停用 strip target binaries
大致上就是把跟 debug 有關的東西通通加進去。另外
1. Toolchain --> Build cross gdb for the host 打開之後,會問後面要不要加一些比較豪華的 gdb 功能(比如說 Python 或 TUI 之類的)。我第一次編的時候是全部打開。
2. 如果加太多東西,有可能會因為 file system image 的大小太小發生問題。這時候去 Filesystem images ---> exact size 中把他從 60M 往上調。
## 開啟 9P File System
### 調整核心編譯選項
依照 [9psetup](https://wiki.qemu.org/Documentation/9psetup) 的說明去對編譯選項進行調整。首先在 buildroot 資料夾下進入核心的配置:
```shell
$ make linux-menuconfig
```
然後使用 `/` 搜尋裡面需要開啟的選項:
* `NET_CONFIG_9P`: 在 Networking support ---> Plan 9 Resource Sharing Support (9P2000) 。開啟之後會發現再下面會有 2 個選項可以用
* 9P Virtio Transport : 這個其實是 `CONFIG_NET_9P_VIRTIO`
* Debug information : 對應的是 ` CONFIG_NET_9P_DEBUG`。說明中說並非必要,但我還是選了
* `CONFIG_9P_FS` : 在 File systems ---> Network Filesystem 。要先開上面那個選項才會出現。出現後底下會出現:
* Plan 9 Resource Sharing Support (9P2000) :勾選後又會出現兩個選項:
* 9P POSIX Access Control Lists : 這是`CONFIG_9P_FS_POSIX_ACL`
* 9P Security Labels: 裏面沒有說要勾選,所以我就沒選了
* `CONFIG_PCI` : 在 Device Drivers ---> PCI support
* `CONFIG_VIRTIO_PCI` : 在 Device Drivers ---> PCI support ---> PCI driver for virtio devices
接著下面有說要另外兩個:
* `CONFIG_VIRTIO_PCI` : 上面開過了
* `CONFIG_PCI_HOST_GENERIC` : Device Drivers --> PCI support ---> PCI controller drivers ---> Generic PCI host controller
都設定完之後重新 `make`。
### 掛載 9P File System
接著使用 QEMU 開編完的東西。如果是使用 `board/qemu/aarch64-virt/` 下預設的選項的話,本來預設的啟動選項會是像這樣子的東西:
```shell=
$ qemu-system-aarch64 -M virt \
-cpu cortex-a53 \
-nographic -smp 1 \
-kernel output/images/Image \
-append "root=/dev/vda console=ttyAMA0" \
-netdev user,id=eth0 -device virtio-net-device,netdev=eth0 \
-drive file=output/images/rootfs.ext4,if=none,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
```
在後面加上下面這一串:
```shell=
-fsdev local,security_model=passthrough,id=fsdev0,path=/tmp -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hey
```
也就是說全部會變成:
```shell=
$ qemu-system-aarch64 -M virt \
-cpu cortex-a53 \
-nographic -smp 1 \
-kernel output/images/Image \
-append "root=/dev/vda console=ttyAMA0" \
-netdev user,id=eth0 -device virtio-net-device,netdev=eth0 \
-drive file=output/images/rootfs.ext4,if=none,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-fsdev local,security_model=passthrough,id=fsdev0,path=/tmp \
-device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hey
```
(多後面兩行)
其中需要注意的是:
1. `path` 是在 host 這邊要分享的資料夾的路徑,這邊是用 `/tmp`;
2. 而最後面那個 `mount_tag` 是到時候在 guest 裡面 `mount` 時會用到,這邊設成 `hey`。
登入 guest 之後,首先新增一個資料夾當作 mount point。比如:
```shell=
# mkdir mom_im_here
```
接著 `mount` 他:
```shell=
# mount -t 9p -o trans=virtio hey mom_im_here -oversion=9p2000.L
```
接著進入 `mom_im_here` 再 `ls` 就可以發現底下其實是 host 端的 `tmp` 了。
### 測試:Cross Compile + 9P
在 host 端編寫程式:
```shell
$ vim call_mom.c
```
該程式內容如下:
```c=
#include <stdio.h>
char msg[] = "MOM I'M HERE !\n";
int main ()
{
printf("%s", msg);
}
```
新增之後,在 guest 中執行 `ls /tmp`,會發現編寫的程式碼已經在裡面了:
```shell
# ls
Temp-66bc5633-22f1-414c-8ec6-01edb0d193ff
Temp-f34e4231-2da8-4301-9805-d9fe3c44b719
config-err-oeKaSb
guest_files
call_mom.c
ssh-7x7V7DXkwAdg
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-ModemManager.service-TsB4ar
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-bolt.service-lWGyIE
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-colord.service-CcUNwz
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-fwupd.service-jQ6PiF
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-iio-sensor-proxy.service-BOpCUx
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-rtkit-daemon.service-WbgFJg
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-systemd-resolved.service-9GRQTP
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-systemd-timesyncd.service-RmIzq5
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-upower.service-wiR2Mk
```
在 host 端以 buildroot 生出來的 toolchain 進行編譯。以我的狀況為例,是:
```
$ ~/Workspace/buildroot/output/host/usr/bin/aarch64-buildroot-linux-uclibc-gcc /tmp/call_mom.c -static -o /tmp/call_mom
```
重點是那個跨平台編譯器 `aarch64-buildroot-linux-uclibc-gcc`。接著在 guest 端 `ls`,會發現剛剛編譯出來的程式已經在 `mom_im_here` 資料夾下:
```shell
# ls
Temp-66bc5633-22f1-414c-8ec6-01edb0d193ff
Temp-f34e4231-2da8-4301-9805-d9fe3c44b719
call_mom
call_mom.c
config-err-oeKaSb
guest_files
ssh-7x7V7DXkwAdg
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-ModemManager.service-TsB4ar
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-bolt.service-lWGyIE
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-colord.service-CcUNwz
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-fwupd.service-jQ6PiF
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-iio-sensor-proxy.service-BOpCUx
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-rtkit-daemon.service-WbgFJg
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-systemd-resolved.service-9GRQTP
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-systemd-timesyncd.service-RmIzq5
systemd-private-112f70ac2ef242eebb6c1c1cfecbed8c-upower.service-wiR2Mk
```
試著執行看看 `call_mom` :
```shell
# ./call_mom
MOM I'M HERE !
```
如果在 host 端執行 `readelf -h /tmp/call_mom` 的話,會發現這確實是 ARM 架構的可執行檔:
```c
$ readelf -h call_mom
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: AArch64
Version: 0x1
Entry point address: 0x400180
Start of program headers: 64 (bytes into file)
Start of section headers: 1182768 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 5
Size of section headers: 64 (bytes)
Number of section headers: 27
Section header string table index: 26
```
## QEMU + gdb
### 調整核心編譯選項
首先要確定核心有編譯到 debug info。沒有的話就要把核心編譯選項進行調整。在 buildroot 資料夾底下執行:
```shell
$ make linux-menuconfig
```
接著找到以下選項:
1. Kernel hacking ---> Kernel debugging :把他打勾。打勾之後下面的東西才會出現
2. Kernel hacking ---> Compile-time checks and compiler options ---> Compile the kernel with debug info :把他打勾
> 後面有個 Provide GDB scripts for kernel debugging。但是我自己的 gdb 有用 gef,因為擔心他跟 gef 一起用會讓設定亂掉,所以先沒勾。
### QEMU 選項
參考作業提示中影片的作法。首先在 QEMU 的啟動選項中,加入:
```
-S -gdb tcp::10000
```
所以,加上剛剛的資料夾分享那邊,全部就會變成:
```shell
$ qemu-system-aarch64 -M virt \
-cpu cortex-a53 \
-nographic -smp 1 \
-kernel output/images/Image \
-append "root=/dev/vda console=ttyAMA0" \
-netdev user,id=eth0 -device virtio-net-device,netdev=eth0 \
-drive file=output/images/rootfs.ext4,if=none,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-fsdev local,security_model=passthrough,id=fsdev0,path=/tmp \
-device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hey \
-S \
-gdb tcp::10000
```
這個跑下去後 QEMU 會看起來像是什麼反應都沒有。這時開另外一個終端機視窗,開啟 toolchain 裏面編出來的 gdb
### 啟動 gdb !
首先在(host 的) buildroot 資料夾新增一個 `kernel.gdb` 的 gdb script:
```shell
$ vim kernel.gdb
```
這個 `kernel.gdb` 的 script 寫進如下的內容:
```
target remote localhost:10000
symbol-file ./output/build/linux-5.0.5/vmlinux
b start_kernel
c
```
那個 linux-5.05 可能會因版本而有不同的版本號。依照狀況做出調整即可
> 本來影片中用的 script 中會有一個 `la` 指令,會把 gdb 的 TUI 介面打開。不過我比較習慣用 gef,所以就用不使用他了。
>
> gef 要 gdb 中有 python 功能。在 buildroot 的 `make menuconfig` 時有加入這個選項。
接著在 host 裡面叫 toolchain 中的 gdb 執行 `kernel.gdb` 這個 script。以我的為例,這個動作會像下面這樣:
```
$ ./output/host/usr/bin/aarch64-buildroot-linux-uclibc-gdb -x ./kernel.gdb
```
toolchain 中的 gdb 名字可能會因為使用的 glibc 或是 target 的架構等等選項有所不同,但總之就是會是在 `./output/host/usr/bin/` 那個資料夾下的 gdb。調整相應的名稱即可。然後(在 gdb 有裝 gef 的狀況下)就得到以下的畫面:
```c
--------------------------------------------------------------------- stack ----
0xffffff8010754000|+0x0000: 0x0000000000000000 -> 0x0000000000000000 <-$sp
0xffffff8010754008|+0x0008: 0x0000000000000000 -> 0x0000000000000000
0xffffff8010754010|+0x0010: 0x0000000000000000 -> 0x0000000000000000
0xffffff8010754018|+0x0018: 0x0000000000000000 -> 0x0000000000000000
0xffffff8010754020|+0x0020: 0x0000000000000000 -> 0x0000000000000000
0xffffff8010754028|+0x0028: 0x0000000000000000 -> 0x0000000000000000
0xffffff8010754030|+0x0030: 0x0000000000000000 -> 0x0000000000000000
0xffffff8010754038|+0x0038: 0x0000000000000000 -> 0x0000000000000000
------------------------------------------------------------ code:arm64:ARM ----
0xffffff80106c0920 ret
0xffffff80106c0924 <thread_stack_cache_init+0> ret
0xffffff80106c0928 <mem_encrypt_init+0> ret
->0xffffff80106c092c <start_kernel+0> stp x29, x30, [sp, #-112]!
0xffffff80106c0930 <start_kernel+4> adrp x0, 0xffffff8010763000 <inet6_protos+1976>
0xffffff80106c0934 <start_kernel+8> add x0, x0, #0xd00
0xffffff80106c0938 <start_kernel+12> mov x29, sp
0xffffff80106c093c <start_kernel+16> stp x19, x20, [sp, #16]
0xffffff80106c0940 <start_kernel+20> stp x21, x22, [sp, #32]
---------------------------------------------------- source:init/main.c+538 ----
533 {
534 rest_init();
535 }
536
537 asmlinkage __visible void __init start_kernel(void)
-> 538 {
539 char *command_line;
540 char *after_dashes;
541
542 set_task_stack_end_magic(&init_task);
543 smp_setup_processor_id();
------------------------------------------------------------------- threads ----
[#0] Id 1, Name: "", stopped, reason: BREAKPOINT
--------------------------------------------------------------------- trace ----
[#0] 0xffffff80106c092c->start_kernel()
--------------------------------------------------------------------------------
Breakpoint 1, start_kernel () at init/main.c:538
538 {
gef>
```
接著就可以用 `s` `n` 等等指令去操作 gdb 了。
## 實驗:用 gdb 追蹤 virtio
因為對於 riscv 那份作業不是很確定哪裡的程式實作了 virtio 介面,所以這裡決定用 QEMU + gdb 追蹤看看。首先找到 9P File System 在 Linux 中的原始程式 fd/9p。看看各個 .h 檔,在 [v9fs_vfs.h](https://elixir.bootlin.com/linux/v5.0.5/source/fs/9p/v9fs_vfs.h) 中看到一些很像 VFS 會出現的函數:
```c=
struct inode *v9fs_alloc_inode(struct super_block *sb);
void v9fs_destroy_inode(struct inode *inode);
struct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t);
int v9fs_init_inode(struct v9fs_session_info *v9ses,
struct inode *inode, umode_t mode, dev_t);
void v9fs_evict_inode(struct inode *inode);
ino_t v9fs_qid2ino(struct p9_qid *qid);
void v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
struct super_block *sb, unsigned int flags);
void v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
unsigned int flags);
int v9fs_dir_release(struct inode *inode, struct file *filp);
int v9fs_file_open(struct inode *inode, struct file *file);
void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat);
int v9fs_uflags2omode(int uflags, int extended);
```
還有一些命名感覺很像 9P200.L 中的 .L 。所以猜這個地方可能就是把 9P Filesystem 中的東西包成 VFS 的樣子。隨便找一個來看看,就找 `v9fs_file_open` 好了。
接著猜這個函數會在什麼樣的時機被呼叫到。這時候很直覺的想到可以先在 gdb 中設一個斷點在這個函數,然後在 guest 裏面掛載一個 9P Filesystem,接著對裡面的檔案進行一些與檔案有關的動作(比如說 `ls`),看看 gdb 會跑出什麼東西來。
首先是用上面那一串開 QEMU:
```shell
qemu-system-aarch64 -M virt \
-cpu cortex-a53 \
-nographic -smp 1 \
-kernel output/images/Image \
-append "root=/dev/vda console=ttyAMA0" \
-netdev user,id=eth0 -device virtio-net-device,netdev=eth0 \
-drive file=output/images/rootfs.ext4,if=none,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-fsdev local,security_model=passthrough,id=fsdev0,path=/tmp \
-device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hey \
-S \
-gdb tcp::10000
```
接著編輯 `kernel.gdb` 成下面這個樣子
```
target remote localhost:10000
symbol-file ./output/build/linux-5.0.5/vmlinux
```
就是把 `b start_kernel` 那邊以後都移除,等 gdb 開起來之後再慢慢設中斷點再那邊。
然後在 host 裡面跑他:
```shell
$ ./output/host/usr/bin/aarch64-buildroot-linux-uclibc-gdb -x ./kernel.gdb
```
> 中間過程待補
>
照之前的作法把 9P filesystem mount 起來之後,看看如果對他進行檔案相關操作的話,會發生什麼事。先在 guest 那邊進入 mount point:
```shell
# cd host_files
```
之後換 host 的 gdb 了。這時把 gdb 中斷點設在 `v9fs_file_open`,並且繼續:
```gdb
gef> b v9fs_file_open
Breakpoint 2 at 0xffffff80102a0940: file fs/9p/vfs_file.c, line 60.
gef> c
Continuing.
```
接著在 host 裡面試試看 `ls`:
```shell
# ls
```
然後 gdb 就會在 `v9fs_file_open` 中停下來了。這時候的 call stack 大概會像下面這樣:
```c
---------------------------------------------------------- source:fs/9p/vfs_file.c+60 ----
55 * @file: file being opened
56 *
57 */
58
59 int v9fs_file_open(struct inode *inode, struct file *file)
-> 60 {
61 int err;
62 struct v9fs_inode *v9inode;
63 struct v9fs_session_info *v9ses;
64 struct p9_fid *fid;
65 int omode;
----------------------------------------------------------------------------- threads ----
[#0] Id 1, Name: "", stopped, reason: BREAKPOINT
------------------------------------------------------------------------------- trace ----
[#0] 0xffffff80102a0940->v9fs_file_open(inode=0xffffffc006484040, file=0xffffffc005fae900)
[#1] 0xffffff80101b9a64->do_dentry_open(f=0xffffffc005fae900, inode=0xffffffc006484040, open=0xffffff80102a0940 <v9fs_file_open>)
[#2] 0xffffff80101bb1e8->vfs_open(path=<optimized out>, file=<optimized out>)
[#3] 0xffffff80101ceee4->do_last(op=<optimized out>, file=<optimized out>, nd=<optimized out>)
[#4] 0xffffff80101ceee4->path_openat(nd=0xffffff801092bd20, op=<optimized out>, flags=<optimized out>)
[#5] 0xffffff80101d0d18->do_filp_open(dfd=<optimized out>, pathname=<optimized out>, op=0xffffff801092be50)
[#6] 0xffffff80101bb560->do_sys_open(dfd=0xffffff9c, filename=<optimized out>, flags=<optimized out>, mode=<optimized out>)
[#7] 0xffffff80101bb670->__do_sys_openat(mode=<optimized out>, flags=<optimized out>, filename=<optimized out>, dfd=<optimized out>)
[#8] 0xffffff80101bb670->__se_sys_openat(mode=<optimized out>, flags=<optimized out>, filename=<optimized out>, dfd=<optimized out>)
[#9] 0xffffff80101bb670->__arm64_sys_openat(regs=<optimized out>)
------------------------------------------------------------------------------------------
```
雖然不知道這到底是怎麼一回事,不過至少可以發現 `do_sys_open` 的過程中,會呼叫到跟 VFS 有關的 `vfs_open` ,而 VFS 這個抽象層背後對於 9P Filesystem 最後是再做 `v9fs_file_open`
> 其實如果是 `ls` 的話應該要追 `fstatat` 之類的系統呼叫比較合理
```c
[#0] 0xffffff801029ff08->v9fs_vfs_getattr_dotl(path=0xffffff8010923da8, stat=0xffffff8010923de0, request_mask=<optimized out>, flags=0x0)
[#1] 0xffffff80101c3c84->vfs_getattr_nosec(path=<optimized out>, stat=<optimized out>, request_mask=<optimized out>, query_flags=<optimized out>)
[#2] 0xffffff80101c3e1c->vfs_getattr(query_flags=<optimized out>, request_mask=<optimized out>, stat=<optimized out>, path=<optimized out>)
[#3] 0xffffff80101c3e1c->vfs_statx(dfd=0xffffff9c, filename=0xffffff8010923de0 "\377\a", flags=0x900, stat=0x0, request_mask=0x1029fef0)
[#4] 0xffffff80101c4118->vfs_fstatat(flags=<optimized out>, stat=<optimized out>, filename=<optimized out>, dfd=<optimized out>)
[#5] 0xffffff80101c4118->__do_sys_newfstatat(flag=<optimized out>, statbuf=<optimized out>, filename=<optimized out>, dfd=<optimized out>)
[#6] 0xffffff80101c4118->__se_sys_newfstatat(dfd=<optimized out>, filename=<optimized out>, statbuf=0x7fcc63bd40, flag=<optimized out>)
[#7] 0xffffff80101c4df8->__arm64_sys_newfstatat(regs=<optimized out>)
[#8] 0xffffff8010094e90->__invoke_syscall(syscall_fn=<optimized out>, regs=<optimized out>)
[#9] 0xffffff8010094e90->invoke_syscall(syscall_table=<optimized out>, sc_nr=<optimized out>, scno=<optimized out>, regs=<optimized out>)
```
更進一步可以用 `rbreak v9fs_vfs_*` 來用 regex 找出所有對應的函數。
## eBPF
[來源](https://www.slideshare.net/suselab/ebpf-trace-from-kernel-to-userspace)