Google App Engine for Go のローカルサーバでデバッグをする #golang #GAE


## はじめに

Google App Engine for Go (GAE/Go) を使っていて、ローカルでデバッグがしたくなることがあるかと思います。
Goにはいくつかデバッガがあり、IDEが対応しているものもあります。
ここでは [Delve](https://github.com/derekparker/delve) というデバッガを使ってデバッグする方法を紹介します。

![delve2.gif](https://qiita-image-store.s3.amazonaws.com/0/14952/2fb8e6d1-479a-7e44-6869-37f3300c8a9e.gif "delve2.gif")


Delveは動いているGoのプロセスにアタッチしてデバッグすることができます。
そのため、GAE/Goのローカルサーバのプロセスにもアタッチしてデバッグすることができます。
しかし、GAE/Goのローカルサーバのプロセスは、複数動いていたり、切り替わったりするため、一筋縄ではいきません。
ここでは、以下の記事を基に、ターミナル上でローカルサーバのデバッグをやってみます。

* [Debugging Golang Appengine module with Visual Studio Code](https://medium.com/@dbenque/debugging-golang-appengine-module-with-visual-studio-code-85b3aa59e0f#.fju2mcvhm)

## Delveのインストール

Delveのインストールについては ネットにたくさん記事がありますので、そちらを参考にしてみてください。

* [vscode-go/READMEとdelve/Buildingの日本語訳](http://qiita.com/qt-luigi/items/1e73c2e129c408c16cbf)
* [Visual Studio CodeによるGo言語のデバッグ](http://dev.classmethod.jp/go/visual-studio-code-golang-debug/)

また、英語ですが本家のドキュメントを見ると間違いないかと思います。

* [Delve Documentation - Installation](https://github.com/derekparker/delve/tree/master/Documentation/installation)

インストールに成功すると、`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の使い方

使い方は、本家の[ドキュメント](https://github.com/derekparker/delve/tree/master/Documentation)を見ると確実です。
GDBを使ったことがある方は馴染みやすいコマンド体系だと思います。以下の記事も参考になります。

* [Windows対応中のGo言語のデバッガーdelveを試してみる](http://www.indetail.co.jp/blog/8017/)

## GAEで使う方法

「はじめに」でも書きましたが、以下の記事を参考にしています。

* [Debugging Golang Appengine module with Visual Studio Code](https://medium.com/@dbenque/debugging-golang-appengine-module-with-visual-studio-code-85b3aa59e0f#.fju2mcvhm)

### delveAppengineのインストール

Delveは起動済みのGoのプロセスにアタッチすることができます。
しかし、GAEのローカルサーバのプロセスは変わっていくので、それに追従するためにdelveAppengineを使います。

`go get`してきます。

```
$ go get github.com/dbenque/delveAppengine
```



なお、[こちらのPR](https://github.com/dbenque/delveAppengine/pull/5)がマージされるまでは、最新のDelveとバージョンがあわなくてうまくいかないので、注意してください。
PRがマージされる前は以下のようにしておくとよいです。


*[追記 8/24] マージされてました。特に何もせず`go get`すれば使えます。* 

```
$ 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`を使って説明します。

1. ローカルサーバの起動
2. delveAppengineの起動
3. 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`コマンドをクライアントとして使いましたが、[元の記事](https://medium.com/@dbenque/debugging-golang-appengine-module-with-visual-studio-code-85b3aa59e0f#.267bnxndk)を参考にvscodeを使っても良さそうです。
他のエディターでもDelveのリモートデバッグに対応しているものであれば同じようにデバッグできると思います。
残念ながらvim-goは対応していませんでした。。

私も[Luaのデバッガ](https://github.com/tenntenn/lupe)を作ったことがありますが、一番大変なのはGUIでした。
結局、私の場合はGUIは公開するまでにいたらなかったですが。。
まぁ、ターミナルでもここまでできたら嬉しいですよね！