Top Banner
1 Go言語のトラブルシューティング用機能 v1.1 2015/10/10 Satoru Takeuchi <[email protected]>
15

Go言語のトラブルシューティング用機能

Apr 15, 2017

Download

Technology

Satoru Takeuchi
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Go言語のトラブルシューティング用機能

1

Go言語のトラブルシューティング用機能v1.1

2015/10/10Satoru Takeuchi

<[email protected]>

Page 2: Go言語のトラブルシューティング用機能

2

はじめに● 本書の目的

– Go言語のトラブルシューティング用機能を紹介● 本書を執筆した動機

– Go言語のトラブルシューティング用機能(デバッガ等)はドキュメントがあまりない

– ドキュメントがあっても、様々な場所に情報が分散している– トラブルシューティング機能に特化した情報が一箇所に集約されていると便利だと判断

● 調査対象にしたGo言語のバージョンは1.5.1

● 調査環境はfedora22/x86_64

Page 3: Go言語のトラブルシューティング用機能

3

目次● gdb

● Race Detector

● godebug

● Heapdump

● Pprof

● 結論

Page 4: Go言語のトラブルシューティング用機能

4

gdb: core dumpの採取● 採取に必要な設定

– 以下の環境変数を設定● GOTRACEBACK=crash # プログラムの異常終了時にcoreを生成

– (go固有ではないが) limit –c unlimited

– (go固有ではないが) /proc/sys/kernel/core_patternの設定● [tips]最適化によって変数が消えてしまわないようにする

– go buildに`-gcflags “-N -l“`オプションを付加● 採取方法

– SEGVなどでプログラムを異常終了させる– SIGABRTを送る

Page 5: Go言語のトラブルシューティング用機能

5

gdb: 解析● go用の拡張コマンドを使用するために、以下のpythonスクリ

プトをロード● (gdb) source $GOROOT/src/runtime/runtime-gdb.py

● 参考) バージョン1.4.2の当該スクリプトはロード時にエラーが出るため、動かない

● 以下サイトに記載の拡張コマンドを使って解析https://golang.org/doc/gdb

– 注意: 現在のところ、解析できる場合、できない場合がある● 動作中のプロセス(gdb <プログラム名>で実行): 解析可能● Core dump: 解析不能(理由は不明)

– プロセスが死んだはずの箇所で止まっていない– まともにスタックが見られない– goroutineコマンドも正しく動作しない

Page 6: Go言語のトラブルシューティング用機能

6

gdb: 今後の展望● 近いうちに改善する見込みはなし● 公式ドキュメント(https://golang.org/doc/gdb)に以下の記載あり

…As a consequence, although GDB can be useful in some situations, it is not areliable debugger for Go programs, particularly heavily concurrent ones.Moreover, it is not a priority for the Go project to address these issues,which are difficult. In short, the instructions below should be taken only as aguide to how to use GDB when it works, not as a guarantee of success.

In time, a more Go-centric debugging architecture may be required.…

Page 7: Go言語のトラブルシューティング用機能

7

Race Detector:概要● Race conditionに由来するバグ検出用のツール

– https://golang.org/doc/articles/race_detector.html

● プログラム内に競合が存在する場合に警告を出す● 使い方

– -raceオプションを付けてビルド、実行● $ go run –race foo.go

● $ go build –race && ./foo

● 注意: この機能を使用すると、メモリ使用量が5-10倍、実行時間が2-20倍になるらしい(上記リンクより)

Page 8: Go言語のトラブルシューティング用機能

8

Race Detector:使い方

$ go run -race race.go==================WARNING: DATA RACEWrite by goroutine 4: main.func·001() /home/sat/gopath/src/test/race/race.go:7 +0x43

Previous write by main goroutine: main.main() /home/sat/gopath/src/test/race/race.go:10 +0x176

Goroutine 4 (running) created at: main.main() /home/sat/gopath/src/test/race/race.go:9 +0x166==================Found 1 data race(s)exit status 66

1 package main 2 3 func main() { 4 c := make(chan bool) 5 i := 0 6 go func() { 7 i = 1 8 c <- true 9 }()10 i = 211 <- c12}

この時点でiが1か2か不定。言い換えると、7行目と10行目のどちらが先に実行されるか不定

7行目と10行目の競合を検出

Page 9: Go言語のトラブルシューティング用機能

9

godebug: 概要● Go専用のデバッガ

– https://github.com/mailgun/godebug

● 使い方は上記リンク先のREADME.mdに記載あり– “Installation”の欄にソース取得コマンドしか記載が無いが、実際にgodebugプログラムを使用するには、以下コマンドの実行が必要

1. go get github.com/mailgun/godebug

2. cd $GOPATH/src/github.com/mailgun/godebug

3. go install # これで$GOPATH/bin以下にgodebugがインストールされる

● 本書執筆時点では機能が非常に少ない(gdb未満)

● まだプロジェクトが新しいので、将来に期待か

Page 10: Go言語のトラブルシューティング用機能

10

godebug: gdbとの機能比較

godebug gdb

Breakpointの設定 ▲ソースに直接埋め込む必要あり

Core dumpの解析 × ×

Goroutineの考慮 × ○Step実行/next実行 ○ ○Continue ○ ○ソース表示 ○ ○変数の表示 ○ ○

Page 11: Go言語のトラブルシューティング用機能

11

Heapdump

● Runtime/debugのWriteHeapDump()関数– https://golang.org/pkg/runtime/debug

● プログラムのheapの中身を指定したファイルにダンプ● 将来的には役立ちそうだが、現在はまともに使えない

– 出力はバイナリ形式だが、テキスト形式にして読むツールが存在しない

– Goのマイナーバージョンごとに形式が異なるものの、Go1.5のバイナリ形式はドキュメント化されていない(ドキュメントが存在するのは1.3, 1.4のみ)

● https://github.com/golang/go/wiki/heapdump13

● https://github.com/golang/go/wiki/heapdump14

Page 12: Go言語のトラブルシューティング用機能

12

pprof: 概要● go 言語プログラム用のプロファイラ● CPUとメモリのプロファイル情報を採取可能● プロファイル用APIが含まれるパッケージ

– https://golang.org/pkg/runtime/pprof/

● 使用例– http://blog.golang.org/profiling-go-programs

● 基本的な使いかた– プログラム内からプロファイル用APIを呼び出し、プロファイル情報を所

定ファイルに採取● 所定のビルドオプション(gccの-pgのような)を付ければ勝手にプロファイル情報

を採取してくれるわけではないので面倒– 採取したプロファイル情報を下記コマンドによって可視化し、分析

● $ go tool pprof <プログラム名> <手順1で採取したプロファイル用ファイル>

Page 13: Go言語のトラブルシューティング用機能

13

pprof: サンプルコードpackage main

import ( "os" "log" "runtime/pprof")

func bar() { for i := 0; i < 1000000; i++ { }}

func foo() { for i := 0; i < 10000000; i++ { if i % 1000 == 0 { bar() } }}

func main() { f, err := os.Create("cpu.pprof") if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() foo()}

CPUプロファイル情報の採取開始

main終了時にCPUプロファイル情報の採取終了

prof.go:CPUプロファイル

情報採取用ファイルの作成

Page 14: Go言語のトラブルシューティング用機能

14

pprof: 実行&プロファイリング$ lsprof.go$ go build$ ./pprof$ lscpu.pprof prof prof.go # プロファイル用のファイル、cpu.pprofが生成されている$ go tool pprof prof cpu.pprof Entering interactive mode (type "help" for commands)(pprof) top9.79s of 9.80s total (99.90%)Dropped 2 nodes (cum <= 0.05s) flat flat% sum% cum cum% 9.74s 99.39% 99.39% 9.74s 99.39% main.bar 0.05s 0.51% 99.90% 9.79s 99.90% main.foo 0 0% 99.90% 9.79s 99.90% main.main 0 0% 99.90% 9.79s 99.90% runtime.goexit 0 0% 99.90% 9.79s 99.90% runtime.main

ほとんどの実行時間は(99.39%)はmain.bar()で消費

ほんのわずか(0.51%)、main.foo()で消費

残りはゴミみたいなもの

Page 15: Go言語のトラブルシューティング用機能

15

結論● Go言語のトラブルシューティング機能はそれな

りに充実している● ただし、Core dumpを解析できるデバッガが無

いのが痛い– 個人的にはこれがGo言語の一番の弱点だと思う– 実運用でプロセスが異常終了した場合にどうやって

原因を特定するのか。スタックトレースだけでは心もとない