Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Goのデバッグ用ロガーの開発を通して得た デバッグとgoパッケージに関する知見 Go Conference 2023 2023/06/02 Takashi Mima(@task4233)
Slide 2
Slide 2 text
Takashi Mima(@task4233) 所属 ・株式会社メルカリ(Identity Platform) 興味分野 ・バックエンド ・Go(Contribution/Go Conference) ・セキュリティ ・CTF(SECCON Beginners) ・セキュリティ・ミニキャンプ in 三重 2023 講師 ほか ・Twitter: @task4233 ・Portfolio: task4233.dev 2
Slide 3
Slide 3 text
Goでデバッグする際に 何を使いますか?👀 3
Slide 4
Slide 4 text
Delve?GDB?ロギング? 他の何か?🧐 4
Slide 5
Slide 5 text
本発表で共有する2つのこと 1. Goにおける3種類のデバッグ方法 2. 自作したロガーの開発に用いられているgoパッケージ ・デバッガの詳しい使い方 ・GODEBUG等の環境変数を用いたデバッグ 5 本発表で共有しないこと
Slide 6
Slide 6 text
3種類のデバッグ方法 Delve GDB ロギング 👍 ・大半のGoプログラム のデバッグに利用可能 ・Goに特化しており、 GDBよりも正確な結果 が得られる ・Cgoやランタイムの デバッグに利用可能 ・機能拡張が豊富 ・Pythonスクリプトで 処理を自動化可能 ・手軽(学習コスト低) ・複数の変数の状態を まとめて確認可能 ・手動のデバッガ操作 よりも高速 🤔 ・単一の状態でしか情報 を取得できない ・単一の状態でしか情報 を取得できない ・ビルド時のフラグを 設定する必要がある ・ログの埋め込み/ 消し忘れが起き得る ・実行速度の低下 6
Slide 7
Slide 7 text
Delve: Goに特化したサードパーティー製のデバッガ 目標 ・Go用のシンプルでフル機能を備えたデバッグツールの提供 Delveが適する場面 ・ビルドされたGoプログラムをデバッグする場合の大半 ・Goのランタイムやデータ構造、式を理解しているため 実装 ・github.com/go-delve/delve 7
Slide 8
Slide 8 text
Goクイズ: 次のプログラム実行時に表示されるのは?🤔 package main import "fmt" type T struct{ N int } func main() { ts := []T{{N: 1}, {N: 3}, {N: 5}} minT := &T{N: 10000} for _, t := range ts { if t.N < minT.N { minT = &t } } fmt.Println(minT.N) } 8 1⃣ 1 2⃣ 5 3⃣ 10000 4⃣ 未定義 Playground: go.dev/play/p/rkeWSeUx4pm
Slide 9
Slide 9 text
Goクイズ: 次のプログラム実行時に表示されるのは?🤔 package main import "fmt" type T struct{ N int } func main() { ts := []T{{N: 1}, {N: 3}, {N: 5}} minT := &T{N: 10000} for _, t := range ts { if t.N < minT.N { minT = &t } } fmt.Println(minT.N) } 9 1⃣ 1 2⃣ 5 3⃣ 10000 4⃣ 未定義 Playground: go.dev/play/p/rkeWSeUx4pm
Slide 10
Slide 10 text
GDB: Goに限らず普遍的に使えるデバッガ 用途 ・プログラム実行中の内部情報の把握 ・クラッシュした際のメモリやスタックの状態の把握 GDBが適する場面 ・Cgoコードやランタイム自体のデバッグをする場合 参考リンク ・https://sourceware.org/git/binutils-gdb.git ・https://go.dev/doc/gdb 10
Slide 11
Slide 11 text
GDB: 低レイヤのデバッグ 拡張スクリプト ・Go用拡張: go/src/runtime/runtime-gdb.py ・peda: https://github.com/longld/peda ・gef: https://github.com/hugsy/gef 11
Slide 12
Slide 12 text
ロギング: プログラムの変数等の情報出力 ロギングが適する場面 ・複数の変数情報を手軽にまとめて出力したい場合 利用できるもの ・builtin関数のpanic, print, println ・fmt, log, x/exp/slog ・glog, logrus, zap ・logr.Logger interface 12
Slide 13
Slide 13 text
3種類のデバッグ方法 Delve GDB ロギング 👍 ・大半のGoプログラム のデバッグに利用可能 ・Goに特化しており、 GDBよりも正確な結果 が得られる ・Cgoやランタイムの デバッグに利用可能 ・機能拡張が豊富 ・Pythonスクリプトで 処理を自動化可能 ・手軽(学習コスト低) ・複数の変数の状態を まとめて確認可能 ・手動のデバッガ操作 よりも高速 🤔 ・単一の状態でしか情報 を取得できない ・単一の状態でしか情報 を取得できない ・ビルド時のフラグを 設定する必要がある ・ログの埋め込み/ 消し忘れが起き得る ・実行速度の低下 13
Slide 14
Slide 14 text
3種類のデバッグ方法 Delve GDB ロギング 👍 ・大半のGoプログラム のデバッグに利用可能 ・Goに特化しており、 GDBよりも正確な結果 が得られる ・Cgoやランタイムの デバッグに利用可能 ・機能拡張が豊富 ・Pythonスクリプトで 処理を自動化可能 ・手軽(学習コスト低) ・複数の変数の状態を まとめて確認可能 ・手動のデバッガ操作 よりも高速 🤔 ・単一の状態でしか情報 を取得できない ・単一の状態でしか情報 を取得できない ・ビルド時のフラグを 設定する必要がある ・ログの埋め込み/ 消し忘れが起き得る ・実行速度の低下 14
Slide 15
Slide 15 text
こんな経験はありませんか? ・テストがうまく動かない... ・プログラムが謎の挙動をする → ログを仕込む! ・対象変数の値は何? log.Printf(“%#v”, targetVariable) ・対象変数の型は何? log.Printf(“%T”, targetVariable) ・そもそも、その箇所は実行されている? log.Printf(“passed!”) → 仕込んだログを消し忘れて commit & push (& release)... 15
Slide 16
Slide 16 text
人はデバッグ時に余計なことを考えたくない ・デバッグ後、ログを仕込んだ場所を忘れがち →コミット時にロギング部分を全て消すロガーを開発すれば 良いのでは?🤔 16
Slide 17
Slide 17 text
デバッグ用ロガーのdlを開発しました 仕組み ・コミット前後のGit Hooksでロギング部分を削除・復元 ・ロガーの削除・復元はGoの静的解析により実現 実装 ・https://github.com/task4233/dl 紹介記事 ・Git にコミットされない魔法のデバッグ用ロガーを開発 してきた - Qiita 17
Slide 18
Slide 18 text
バグらせずにロガー部分を削除する技術 静的解析をする ・プログラムを実行せずにGoのコードを解析すること ・詳しくは tenntennさんの14. 静的解析とコード生成 に 字句解析→構文解析→型チェックの順に行われる ・今回、型情報は必要ないので構文解析のフェーズまで(コード) ・構文解析までするとAST(抽象構文木)が得られる ・型チェックまですると型情報が得られる 18
Slide 19
Slide 19 text
AST(抽象構文木)とは 言語の意味に関係のある情報のみを取り出した木 ・return 1, nil は Return Statements 19 ReturnStmt Expr (1) Expr (nil) Results =
Slide 20
Slide 20 text
dlで利用されているgoパッケージと用途 20 パッケージ名 用途 該当部分 go/token 字句解析(Go Code→tokens) コード go/parser 構文解析(tokens→AST) コード (x/tools/) go/ast ASTの関連操作 コード go/format AST操作後の整形 コード
Slide 21
Slide 21 text
ASTからロガーの呼び出しを削除するために 言語仕様に沿って呼び出され得る箇所を考える ・ロガーの定義は func Info[T any](v T) (int, error) →Expression Expressionが入る場所は? ・Expression Statements(例: dl.Info(1) ) ・Assignment Statements(例: n, err := dl.Info(1) ) ・Return Statements(例: return dl.Info(1) ) など 21
Slide 22
Slide 22 text
ASTからロガーの呼び出しを削除するために 該当部分をASTから発見する ・Expression Statementsを見つけたい時は以下のように ・見つけた後に、該当部分を削除する 22
Slide 23
Slide 23 text
まとめ 3種類のデバッグ方法(Delve, GDB, ロギング) ・得手不得手がある ・目的にあった方法を選択できると良い goパッケージ(go/ast, go/tokenなど) ・静的解析のためのパッケージ ・ASTや型情報等を得ることができる ・興味のある方はツール自作も! 23 ありがとうございました!