[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