はじめに
Google App Engine for Go (GAE/Go) を使っていて、ローカルでデバッグがしたくなることがあるかと思います。
Goにはいくつかデバッガがあり、IDEが対応しているものもあります。
ここでは Delve というデバッガを使ってデバッグする方法を紹介します。
Delveは動いているGoのプロセスにアタッチしてデバッグすることができます。
そのため、GAE/Goのローカルサーバのプロセスにもアタッチしてデバッグすることができます。
しかし、GAE/Goのローカルサーバのプロセスは、複数動いていたり、切り替わったりするため、一筋縄ではいきません。
ここでは、以下の記事を基に、ターミナル上でローカルサーバのデバッグをやってみます。
Delveのインストール
Delveのインストールについては ネットにたくさん記事がありますので、そちらを参考にしてみてください。
また、英語ですが本家のドキュメントを見ると間違いないかと思います。
インストールに成功すると、dlv
コマンドが使えるようになります。
$ dlv help
Delve is a source level debugger for Go programs.
Delve enables you to interact with your program by controlling the execution of the process,
evaluating variables, and providing information of thread / goroutine state, CPU register state and more.
The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.
Usage:
dlv [command]
Available Commands:
attach Attach to running process and begin debugging.
connect Connect to a headless debug server.
debug Compile and begin debugging main package in current directory, or the package specified.
exec Execute a precompiled binary, and begin a debug session.
run Deprecated command. Use 'debug' instead.
test Compile test binary and begin debugging program.
trace Compile and begin tracing program.
version Prints version.
Flags:
--accept-multiclient[=false]: Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate.
--api-version=1: Selects API version when headless.
--build-flags="": Build flags, to be passed to the compiler.
--headless[=false]: Run debug server only, in headless mode.
--init="": Init file, executed by the terminal client.
-l, --listen="localhost:0": Debugging server listen address.
--log[=false]: Enable debugging server logging.
Use "dlv [command] --help" for more information about a command.
Delveの使い方
使い方は、本家のドキュメントを見ると確実です。
GDBを使ったことがある方は馴染みやすいコマンド体系だと思います。以下の記事も参考になります。
GAEで使う方法
「はじめに」でも書きましたが、以下の記事を参考にしています。
delveAppengineのインストール
Delveは起動済みのGoのプロセスにアタッチすることができます。
しかし、GAEのローカルサーバのプロセスは変わっていくので、それに追従するためにdelveAppengineを使います。
go get
してきます。
$ go get github.com/dbenque/delveAppengine
$ go get github.com/tenntenn/delveAppengine
$ cd $GOPATH/src/github.com/tenntenn/delveAppengine
$ git checkout update-delve
$ go install github.com/tenntenn/delveAppengine
デバッグの仕方
デバッグは以下の手順で行います。
ここでは、GAE/GoのSDKに付いているデモのHelloWorld
を使って説明します。
- ローカルサーバの起動
- delveAppengineの起動
- Delveクライアントの起動
まずは以下のようにローカルサーバを起動します。
オプションに--max_module_instances=1
を指定しているのは、複数のプロセスを立ち上げないためです。
なお、path/to/go_appengine
はGAE/GoのSDKをインストールしたディレクトリに置き換えてください。
$ cd path/to/go_appengine
$ dev_appserver.py --max_module_instances=1 demos/helloworld/app.yaml
続いて、delveAppengineを起動します。
Delveはデバッグサーバとしても起動できます、delveAppengineは内部でDelveのデバッグサーバを立ち上げ、ローカルサーバのプロセスが変わるとアタッチし直します。
なお、私のMacOSXでは、sudo
しないとうまくいきませんでした。
$ sudo delveAppengine
最後に、Delveをクライントモードで接続します。
delveAppengineは、デフォルトでlocalhost:2345
でデバッグサーバを立てています。
dlv connect
を使えばデバッグサーバに接続することができます。
起動に成功すると、以下のようにプロンプトが表示されます。
[追記 8/26] app.yamlがあるディレクトリに移動してdlv
を起動しないとソースコードがうまく表示されないかもしれません。
$ dlv connect localhost:2345
(dlv)
それでは、ブレークポイントを仕掛けてみます。
なお、b
はブレークポイントを仕掛けるコマンドbreak
のエイリアスで、c
は処理を継続させるcontinue
のエイリアスです。
(dlv) b handle
Breakpoint 1 set at 0x63fc8 for main07214.handle() helloworld.go:16
(dlv) c
http://localhost:8080
にアクセスすると、ブレークポイントで止まります。
> main07214.handle() helloworld.go:16 (hits goroutine(6):1 total:1) (PC: 0x63fc8)
11:
12: func init() {
13: http.HandleFunc("/", handle)
14: }
15:
=> 16: func handle(w http.ResponseWriter, r *http.Request) {
17: fmt.Fprint(w, "<html><body>Hello, World! 세상아 안녕!</body></html>")
18: }
リクエストのメソッドを調べて見ましょう。
なお、n
は次の行に移るコマンドnext
のエイリアスで、p
は変数を表示するコマンドprint
のエイリアスです。
(dlv) n
> main07214.handle() helloworld.go:17 (PC: 0x63fcf)
12: func init() {
13: http.HandleFunc("/", handle)
14: }
15:
16: func handle(w http.ResponseWriter, r *http.Request) {
=> 17: fmt.Fprint(w, "<html><body>Hello, World! 세상아 안녕!</body></html>")
18: }
(dlv) p r.Method
"GET"
うまく"GET"
と表示されました。
ここでは、dlv
コマンドをクライアントとして使いましたが、元の記事を参考にvscodeを使っても良さそうです。
他のエディターでもDelveのリモートデバッグに対応しているものであれば同じようにデバッグできると思います。
残念ながらvim-goは対応していませんでした。。
私もLuaのデバッガを作ったことがありますが、一番大変なのはGUIでした。
結局、私の場合はGUIは公開するまでにいたらなかったですが。。
まぁ、ターミナルでもここまでできたら嬉しいですよね!