Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Goのデバッグ用ロガーの開発を通して得た デバッグとgoパッケージに関する知見/Knowled...

Goのデバッグ用ロガーの開発を通して得た デバッグとgoパッケージに関する知見/Knowledge by given implementation of logger for debug

[Elevator Pitch]

Goでデバッグをする場合, 基本的にはDelve, GDB, ロギングの3択になります. DelveやGDBによるデバッグは便利ですが複数の変数情報を高速に得ることは困難であり, この用途にはロギングが適しています. しかし, デバッグ用に追加したロギング用のコードを消し忘れ, 実行速度の低下や業務用のコードにおける機密情報のログ出力等の問題を引き起こす可能性があります. これらの問題に対処するべく, 私はGoの静的解析および動的解析によって変数情報等を表示可能で, コミット時に自動的に削除されるデバッグ用のロガーdlを開発しました. 本セッションでは, 前述したGoにおける3種類のデバッグ方法の利点および欠点ならびにdlの開発を通して得たgo/ast等のgoパッケージに関する知見をご紹介します.

[Main Description]

開発において, 想定外の挙動をした際にデバッグをした経験がある方は多いのではないでしょうか? Goでデバッグをする場合, 基本的にはDelve[1], GDB[2], ロギングの3択になります. Delve[1]はサードパーティー製のGo用デバッガであり, 実行時の変数やスタック情報の表示や上書き, goroutineの情報取得等ができます. また, リモードデバッグを有効にすることで, VS CodeやGoLandなどのエディタやIDEに組み込んでGUI上でデバッグすることもできるため, 開発時に重宝します. GDB[2]はDelve[1]と似たデバッガで, gef[5]やpeda[4], pwndbg[6]等のGDBスクリプトにより, 低レイヤを対象としたデバッグを円滑に行うことができます. しかし, Go Blogの記事[3]に記載されているように, Goプログラムの構造を理解していないため誤った結果を表示する場合もあります. また, Delve[1]やGDB[2]はある1点の状態を詳しく得ることは得意ですが, 複数の変数情報等をまとめて高速に得ることは不得手です. したがって, 手軽に複数の変数情報等をまとめて高速に得る用途にはロギングが適しています.

Goのロギングには, builtinパッケージのpanic, print, println, fmtパッケージやlogパッケージの各種関数およびメソッド, サードパーティー製のglog[7], Logrus[8], zap[9]等のロガーが利用できます. また, logr[10]が提供するinterfaceを満たすロガーを利用することで, それぞれのロガーを手軽に切り替えることもできます. これにより, Delve[1]やGDB[2]と違い, 複数の変数情報等をまとめて高速に得ることができます. しかし, デバッグ用に追加したロギング用のコードを消し忘れ, 実行速度の低下や業務用のコードにおける機密情報のログ出力等の問題を引き起こす可能性があります. 実際に, 私もインターンシップ先のリポジトリに対して余分なロギング用のコードを追加し, レビュー漏れで本番コードに紛れ込んでしまった経験があります.

そこで, 私はGoの静的解析と動的解析によって変数情報等を表示でき,コミット時に自動的に削除されるデバッグ用のロガーdl( https://github.com/task4233/dl )を開発しました. Design Docsはこちら( http://bit.ly/3XGdtyF )です. dlは変数とその型情報, 変数が利用されているコードの行数を表示する機能とコミット時に自動的に削除される機能を提供します. これらの機能は, Goのgo/token, go/parser, go/astパッケージ等を用いた静的解析やruntimeパッケージを用いた動的解析を活用することで実現しています. また, dlは前述したlogrのinterfaceを満たしているロガーであればラップすることができ, 既存のロガーを変更することなく容易に導入することができます. 私が調査した中では, これらの機能を全て備えたロガーは他に存在しませんでした(2023/01/30現在).

本セッションでは, 前述したGoにおける3種類のデバッグ方法の利点および欠点ならびにdlの開発を通して得たgo/ast等のgoパッケージに関する知見をご紹介します. 過去のGo ConferenceでGDB[2]に関する講演[11]がありましたが, これとは異なる切り口の内容なので差分はあると考えています.

[1] go-delve/delve: https://github.com/go-delve/delve

[2] GDB: The GNU Project Debugger: https://www.sourceware.org/gdb/

[3] Debugging Go Code with GDB: https://go.dev/doc/gdb

[4] hugsy/gef: https://github.com/hugsy/gef

[5] longld/peda: https://github.com/longld/peda

[6] pwndbg/pwndbg: https://github.com/pwndbg/pwndbg

[7] golang/glog: https://github.com/golang/glog

[8] Sirupsen/logrus: https://github.com/Sirupsen/logrus

[9] uber-go/zap: https://github.com/uber-go/zap/

[10] go-logr/logr: https://github.com/go-logr/logr

[11] Debugging Go Code with GDB:https://speakerdeck.com/kaneshin/debugging-go-code-with-gdb

task4233

June 02, 2023
Tweet

More Decks by task4233

Other Decks in Technology

Transcript

  1. Takashi Mima(@task4233) 所属 ・株式会社メルカリ(Identity Platform) 興味分野 ・バックエンド  ・Go(Contribution/Go Conference) ・セキュリティ

     ・CTF(SECCON Beginners)  ・セキュリティ・ミニキャンプ in 三重 2023 講師 ほか ・Twitter: @task4233 ・Portfolio: task4233.dev 2
  2. 3種類のデバッグ方法 Delve GDB ロギング 👍 ・大半のGoプログラム  のデバッグに利用可能 ・Goに特化しており、  GDBよりも正確な結果  が得られる

    ・Cgoやランタイムの  デバッグに利用可能 ・機能拡張が豊富 ・Pythonスクリプトで  処理を自動化可能 ・手軽(学習コスト低) ・複数の変数の状態を  まとめて確認可能 ・手動のデバッガ操作  よりも高速 🤔 ・単一の状態でしか情報  を取得できない ・単一の状態でしか情報  を取得できない ・ビルド時のフラグを  設定する必要がある ・ログの埋め込み/  消し忘れが起き得る ・実行速度の低下 6
  3. 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
  4. 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
  5. 3種類のデバッグ方法 Delve GDB ロギング 👍 ・大半のGoプログラム  のデバッグに利用可能 ・Goに特化しており、  GDBよりも正確な結果  が得られる

    ・Cgoやランタイムの  デバッグに利用可能 ・機能拡張が豊富 ・Pythonスクリプトで  処理を自動化可能 ・手軽(学習コスト低) ・複数の変数の状態を  まとめて確認可能 ・手動のデバッガ操作  よりも高速 🤔 ・単一の状態でしか情報  を取得できない ・単一の状態でしか情報  を取得できない ・ビルド時のフラグを  設定する必要がある ・ログの埋め込み/  消し忘れが起き得る ・実行速度の低下 13
  6. 3種類のデバッグ方法 Delve GDB ロギング 👍 ・大半のGoプログラム  のデバッグに利用可能 ・Goに特化しており、  GDBよりも正確な結果  が得られる

    ・Cgoやランタイムの  デバッグに利用可能 ・機能拡張が豊富 ・Pythonスクリプトで  処理を自動化可能 ・手軽(学習コスト低) ・複数の変数の状態を  まとめて確認可能 ・手動のデバッガ操作  よりも高速 🤔 ・単一の状態でしか情報  を取得できない ・単一の状態でしか情報  を取得できない ・ビルド時のフラグを  設定する必要がある ・ログの埋め込み/  消し忘れが起き得る ・実行速度の低下 14
  7. dlで利用されているgoパッケージと用途 20 パッケージ名 用途 該当部分 go/token 字句解析(Go Code→tokens) コード go/parser

    構文解析(tokens→AST) コード (x/tools/) go/ast ASTの関連操作 コード go/format AST操作後の整形 コード
  8. 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