# 常用的除錯技巧
## 課程目標
- 使用 pprof 進行除錯
- 使用 delve 進行除錯
- 認識 eBPF
:::info
延伸閱讀: [Linux 核心設計: 透過 eBPF 觀察作業系統行為](https://hackmd.io/@sysprog/linux-ebpf)
:::
## 使用 pprof 進行除錯
pprof 是由 golang 官方維護的效能分析(profiling)套件,它能夠輸出可視覺化的分析紀錄。讓我們根據這些資訊了解 heap 以及 cpu 的使用量。
```go=
import (
"fmt"
"net/http"
_ "net/http/pprof"
)
func main() {
http.ListenAndServe("localhost:6060", nil)
}
```
在主程式的入口點引入 `_ "net/http/pprof"` 並且使用 `http.ListenAndServe("localhost:6060", nil)`,這樣一來,當程式執行時 pprof 就會被啟用。
程式執行的過程中,我們可以使用瀏覽器訪問 http://localhost:6060/debug/pprof/

*使用 Gthulhu + pprof 觀察記憶體分配狀況*
如果覺得文字難以閱讀,可以使用 go tool pprof 讀取 Count Profile 列出的資訊,例如:
- heap 對應 http://localhost:6060/debug/pprof/heap
- goroutine 對應 http://localhost:6060/debug/pprof/goroutine
```shell
$ go tool pprof http://localhost:6060/debug/pprof/heap
Fetching profile over HTTP from http://localhost:6060/debug/pprof/heap
Saved profile in /home/ian/pprof/pprof.main.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz
File: main
Build ID: c27815f62575d47a5fe5c40c1c7591152986d351
Type: inuse_space
Time: Nov 14, 2025 at 1:09am (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
(pprof) exit
```
若一切順利,你會取得一個經過處理的檔案 `/home/ian/pprof/pprof.main.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz`,接著我們再使用 `go tool pprof` 提供的 WEB GUI 讀取它:
```shell
$ go tool pprof -http=:8080 /home/ian/pprof/pprof.main.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz
```
你就能透過瀏覽器了解 Gthulhu 在運作時的記憶體分配狀況:

換成 goroutine 也是類似的概念:

## 使用 delve 進行除錯
[delve](https://github.com/go-delve/delve) 是一個為 golang 開發的 debbuger,概念與 GDB 並無差別。
在某些情境下,Debugger 可以幫助我們找出程式的非預期行為,例如:
- 網路程式有收到封包,但不確定處理完封包後程式的 context 是否如我們預期
- 確定程式流,尤其是使用並行程式開發的程式
Debbuger 雖然好用,但也不是萬用的,像是嘗試在 http request 的 handler 設置中斷點,但 http request 本身是有時限性的,如果在中斷點停留過久反而會增生其他問題,導致原本的問題無法被原況呈現。在不同的情境下,選用不同的除錯工具才能有效解決問題。

如果你使用的是 VSCode,請安裝 Go extension,該套件包含了 delve 執行所需要的環境。有了 Go extension,我們就能在 IDE 上直接使用 debugger 的功能:

參考上圖,我們能夠直接在程式碼行數左方按下右鍵直接插入一個中斷點。
要在 VSCode 上直接使用 delve 除錯,請在工作目錄下建立以下檔案:
```bash=
code .vscode/launch.json
```
並且將以下內容貼上 `.vscode/launch.json`
```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": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}"
},
{
"name": "Attach to Process",
"type": "go",
"request": "attach",
"mode": "local",
"processId": "${command:pickProcess}"
},
{
"name": "Attach via Headless Delve",
"type": "go",
"request": "attach",
"mode": "remote",
"remotePath": "",
"port": 2345,
"host": "127.0.0.1"
}
]
}
```
- 碰到 `try writing "0" to /proc/sys/kernel/yama/ptrace_scope` 問題時,請執行 `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope`。
- 第三種方式需要額外運作 delve,如:`sudo dlv attach <PID> --headless --listen=:2345 --api-version=2`,`<PID>` 請填入你要進行除錯的程式。
成功後,你會看到以下畫面:

*圖片來源:https://github.com/golang/vscode-go/wiki/debugging*
## eBPF (extended BPF)
eBPF 的 "BPF" 來自一篇 1992 年發表的論文「The BSD Packet Filter: A New Architecture for User-level Packet Capture」。這篇論文引入 instruction set 幫助開發者有效的監測網路流量。
如今的 eBPF 與 BPF(又稱 Classic BPF)並沒有太大的關聯,eBPF 最早是由 Plumgrid 開發,稱之為 "iovisor.ko"。後面為了貢獻至 Linux Kernel 的上游,在經過一番努力後變成現在的 eBPF。

Kernel 允許使用者將經過編譯產生的 eBPF program 以探針的形式注入到系統之中。可以注入的觀測點有上百個,主要涵蓋:
- 網路子系統
- 檔案系統
- 系統呼叫
- 內部函式的動態追蹤
- 排程器(sched_ext)
- ...,不勝枚舉
eBPF 之所以這麼熱門,除了千變萬化的應用場景外,最大的原因就是安全性,它能夠保證 eBPF program 注入到系統後不會造成 kernel panic,這對於 kernel developer 來說是一大福音。

追求絕對的安全性勢必需要犧牲一定程度的彈性,所有的 eBPF program 在使用 C 語言撰寫時就受到強烈的規範:
- 僅能使用特定的 eBPF library
- 編譯後的 instruction size 有限制,記憶體用量也有限制
- 僅能在[有限的情況下](https://docs.ebpf.io/linux/concepts/loops/)使用 loop
諸多限制下,在開發 eBPF program 時經常會因為不符合 eBPF verifier 的要求而被擋下:

### 與 user space app 互動
eBPF 的使用場景多半出現在動態追蹤上,當我們在系統中注入大量的 eBPF program 蒐集目標事件,背後也需要一個應用程式來消化這些資料。
eBPF 提供 Map(以 key-value 的方式進行儲存),eBPF program 與 user space app 都能夠存取 eBPF Map:

事實上,各位在 Project 3 使用的 Gthulhu 就高度的利用 eBPF Map:

除了 Gthulhu,還有許多讓人驚艷的 eBPF 應用:
- 使用 eBPF 開發 TCP congestion controler:https://github.com/orace-github/ebpf-coupled-congestion-controler
- 使用 eBPF 開發 5G UPF:https://github.com/edgecomllc/eupf
- 使用 eBPF 在 PF_PACKET Socket 上實作 Receive Packet Steering:https://github.com/acassen/bpf-pfpacket-rps
- 使用 eBPF 追蹤封包流量:https://github.com/cilium/pwru
- 使用 eBPF 實作可觀測性平台:https://github.com/coroot/coroot