Slide 1

Slide 1 text

デバッガと 和解せよ Takaya Saeki (@nullpo_head) Kernel/VM 探検隊 online part 5 @nullpo_head 1 2022/08/27

Slide 2

Slide 2 text

デバッガー使うの、 腰 が 重 く な い ? 2

Slide 3

Slide 3 text

3 デバッガーは少し面倒で気軽に使えない アタッチが 面倒 使い方を 忘れる その他 デバッガーを使うのが面倒な理由(発表者脳内調べ) GDBコマンド 忘れた - list? s? si? Gdb用のmake run ターゲットがない VSCodeの launch.json 書きたくない デ バ ッ ガ の ア タ ッ チ / 起 動 は 既存の開発フローと親和性が低い デバッガはいつもより一手間必要

Slide 4

Slide 4 text

デバッガを気軽に 使えるようにする 便利ツールを作ろう 4 Key technology • Ptrace • Seccomp-BPF • DWARF

Slide 5

Slide 5 text

5 デバッガーはなぜ面倒か? アタッチが 面倒 使い方を 忘れる その他 デバッガーを使うのが面倒な理由(発表者脳内調べ)

Slide 6

Slide 6 text

6 使い方を忘れる 使い方を 忘れる デバッガーを使うのが面倒な理由(発表者脳内調べ) (解決済み)フロントエンドを使おう • VSCode 起動さえすれば使いやすい • Vim/NeoVim Vim 8.1から termdebug が入ったらしい (未体験 • Emacs emacsユーザーではないので割愛

Slide 7

Slide 7 text

7 アタッチ/起動が面倒 アタッチが 面倒 デバッガーを使うのが面倒な理由(発表者脳内調べ) GDB/LLDBなら • デバッグ対象の起動がシェルスクリプトや ビルドシステムでラップされていると デバッガで起動するのが大変 VSCodeなら加えて • Launch.jsonを書くのが面倒 • Stdin/outがあるとやや使いづらい

Slide 8

Slide 8 text

単体バイナリを起動するシンプルな場合 簡単にデバッガを挿入できる 8 起動スクリプトとデバッガ相性悪い問題 ※RustやC/C++、Goのようなバイナリコンパイル言語をひとまず話題にします 👌

Slide 9

Slide 9 text

Makeやもっと複雑なビルドスクリプトで起動がラップされていると? スクリプトを覗かないとデバッガの挿入方法/可否が分からない • debugターゲットがある?それとも自前で修正? 9 起動スクリプトとデバッガ相性悪い問題 🤔

Slide 10

Slide 10 text

_人人人人人人人人人人人人人人人人人_ > 仕様を調べるのがもうすでに面倒 <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄ 10 VSCode、便利だけどlaunch.jsonが面倒

Slide 11

Slide 11 text

_人人人人人人人人人人人人人人人人人_ > 仕様を調べるのがもうすでに面倒 <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄ 11 VSCode、便利だけどlaunch.jsonが面倒 ボーナス点 _人人人人人人人人人人人人人人人人人_ > CLI引数やリダイレクトが必要なら < > さらに面倒なことに! <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^^Y^ ̄

Slide 12

Slide 12 text

理想のデバッガの 起動方法は何だ? 12

Slide 13

Slide 13 text

普段のコマンドに足すだけでデバッガが起動してほしい (無茶ブリ) 13 既存のCLIワークフローに親和してほしい

Slide 14

Slide 14 text

普段のコマンドに足すだけでデバッガが起動してほしい (VScode編) 14

Slide 15

Slide 15 text

普段のコマンドに足すだけで デバッガが起動してほしい!!! 15

Slide 16

Slide 16 text

このコマンドが欲しい!!! 16

Slide 17

Slide 17 text

実は前に”dbgee”を作ったのだが… 17

Slide 18

Slide 18 text

`Run`: ターゲットプログラムのパスと引数を指定してデバッグ `Set`: ターゲットプログラムのパスを指定すると、それをデバッガ起動コマンドでラップする • VSCodeのシェル内で叩けばVSCodeのデバッグセッションが始まる (with vscode plugin) • 普通のターミナルで叩けば、tmux paneでgdb –tuiが起動する 18 Dbgeeができること

Slide 19

Slide 19 text

`Run`: ターゲットプログラムのパスと引数を指定してデバッグ `Set`: ターゲットプログラムのパスを指定すると、それをデバッガ起動コマンドでラップする • VSCodeのシェル内で叩けばVSCodeのデバッグセッションが始まる (with vscode plugin) • 普通のターミナルで叩けば、tmux paneでgdb –tuiが起動する 19 Dbgee、まだ使いづらい・・・ ビルド方法によってはビルド後の パ ス を 指 定 す る の が 結 構 面 倒

Slide 20

Slide 20 text

ターゲットプログラムのパス を指定しないでいい方法が欲しい! 20

Slide 21

Slide 21 text

• プログラム中で特定の出力をすれば、どこにビルドされていようと/どんなスクリプトから 起動されていようとブレークできれば便利では?? 21 第1案 どんなコマンドでビルド/起動さ れていようとここでブレーク Secomp-bpfとptraceで 実 現 で き そ う !

Slide 22

Slide 22 text

デバッグ対象をptraceにより起動直後に停止させて、VSCodeやgdbに明け渡す 1. Dbgee `fork()` 2. 親(dbgee) 3. 子を`waitpid()` 6. `SIGTRAP`を受信 7. `ptrace(PTRACE_CONT, …, SIGSTOP)`で、子からデタッチしつつ子を停止 8. Gdbやそのほかのデバッガを起動し、Attach by PIDで子にアタッチさせる VSCodeなら、dbgeeプラグインとのfifoコネクションにアタッチリクエストを送信 2. 子 4. `ptrace(TRACE_ME, …)` 5. `execve(ターゲットPATH, …)` ここでSIGTRAPが発生 ← ptraceの仕様 22 現Dbgeeの仕組み兼Ptraceのおさらい

Slide 23

Slide 23 text

• Ptraceとseccomp-bpfの組み合わせで可能 • Ptraceにはすべてのforkを追跡するオプションがあ る • Seccomp-bpfには、bpfでフックするシステムコー ルを条件判定してSIGTRAPするオプションがある • Seccomp-bpf触りたい!これはKernel/VM向き だ!! 23 特定出力でブレークの実現

Slide 24

Slide 24 text

いやまてよ・・・ 24

Slide 25

Slide 25 text

もっと簡単に実現できるくない? 25

Slide 26

Slide 26 text

• すべてのExecveをトラップする • 新プロセスのDWARFを読んで、ソース一覧に編集中のソースファイルが含まれているか判断 • Yesならアタッチ でよくない? 26 第2案 Secomp-bpfいらねぇ!

Slide 27

Slide 27 text

27 そしてメインからLTへ・・・

Slide 28

Slide 28 text

• ビルド先は知らずに & 起動スクリプトの内容も知らずに • ソースコードの場所さえ知っていれば デバッガをアタッチ可能 28 第2案でのデバッガ起動

Slide 29

Slide 29 text

29 処理の流れ Ptraceですべての子孫プロセスにアタッチしつつ、DWARFをパースして目的のプロセスを探す 1. `ptrace(PTRACE_SETOPTIONS, …, PTRACE_O_TRACECLONE)` • すべてのcloneされたプロセスもptrace配下になる • ちなみにLinux専用機能になります(macOSでは同様の機能はない 2. 子孫プロセスがexecveするたびにSIGTRAPを受信 3. `/proc/PID/exe`を見て、DWARFがあればパース、ソースコードのリストを得る 4. ソースコードの中に、引数で与えられたソースがある? • If YES • そのプロセスを`ptrace(PTRACE_CONT, …, SIGSTOP)`でデタッチしつつ止める • 真のデバッガにそのプロセスを渡す • 全プロセスからデタッチ

Slide 30

Slide 30 text

• Cargo.tomlに二行足すだけで、DWARFのパーサが入る • Crateのドキュメントを書く文化とLSPのおかげで快適にDWARFライブラリが使える システムプログラミングにも低レイヤプラグラミングにも本当におすすめ 30 Rust最高!

Slide 31

Slide 31 text

普段のコマンドに足すだけで デバッガが起動してほしい!!! できる!!! 31

Slide 32

Slide 32 text

このコマンド相当ができた!!! 32

Slide 33

Slide 33 text

33 デモ

Slide 34

Slide 34 text

デバッガーがかなり 気軽に使えるように! デバッガと和解しよう 34

Slide 35

Slide 35 text

• Dockerへの対応 • Dockerで実行されると、子ではなくデーモンの方で実行されてしまうのでptraceできない • Minikubeやkindでの実行にも対応したい • ので、runcをptraceでラップしたOCIランタイムとして実装して対応したい • Pythonへの対応 • 今回の新機能はいまのところC/C++/Rust/Goにだけ対応 • こっちは本当にseccomp-bpfいるかも? やる気がないやつ • macOS対応 • Ptraceが実質「無」で、macOSのAPIを学ばないと厳しいので 35 TODO(かつやる気があるやつ)