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

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

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

An introduction to some debug features of golang

Avatar for Satoru Takeuchi

Satoru Takeuchi

October 12, 2017
Tweet

More Decks by Satoru Takeuchi

Other Decks in Programming

Transcript

  1. 2 はじめに • 本書の目的 – Go言語のトラブルシューティング用機能を紹介 • 本書を執筆した動機 – Go言語のトラブルシューティング用機能(デバッガ等)はドキュメン

    トがあまりない – ドキュメントがあっても、様々な場所に情報が分散している – トラブルシューティング機能に特化した情報が一箇所に集約されて いると便利だと判断 • 調査対象にしたGo言語のバージョンは1.5.1 • 調査環境はfedora22/x86_64
  2. 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を送る
  3. 5 gdb: 解析 • go用の拡張コマンドを使用するために、以下のpythonスクリ プトをロード • (gdb) source $GOROOT/src/runtime/runtime-gdb.py

    • 参考) バージョン1.4.2の当該スクリプトはロード時にエラーが出るため 、動かない • 以下サイトに記載の拡張コマンドを使って解析 https://golang.org/doc/gdb – 注意: 現在のところ、解析できる場合、できない場合がある • 動作中のプロセス(gdb <プログラム名>で実行): 解析可能 • Core dump: 解析不能(理由は不明) – プロセスが死んだはずの箇所で止まっていない – まともにスタックが見られない – goroutineコマンドも正しく動作しない
  4. 6 gdb: 今後の展望 • 近いうちに改善する見込みはなし • 公式ドキュメント(https://golang.org/doc/gdb)に以下の 記載あり … As

    a consequence, although GDB can be useful in some situations, it is not a reliable 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 a guide 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. …
  5. 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倍になるらしい(上記リンクより)
  6. 8 Race Detector: 使い方 $ go run -race race.go ==================

    WARNING: DATA RACE Write 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 = 2 11 <- c 12} この時点でiが1か2か不定。 言い換えると、7行目と10行目の どちらが先に実行されるか不定 7行目と10行目の競合を検出
  7. 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未満) • まだプロジェクトが新しいので、将来に期待か
  8. 10 godebug: gdbとの機能比較 godebug gdb Breakpointの設定 ▲ ソースに直接埋め込む必要あり ◦ Core

    dumpの解析 × × Goroutineの考慮 × ◦ Step実行/next実行 ◦ ◦ Continue ◦ ◦ ソース表示 ◦ ◦ 変数の表示 ◦ ◦
  9. 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
  10. 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で採取したプロファイル用ファイル>
  11. 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プロファイル 情報採取用ファイルの作成
  12. 14 pprof: 実行&プロファイリング $ ls prof.go $ go build $

    ./pprof $ ls cpu.pprof prof prof.go # プロファイル用のファイル、cpu.pprofが生成されている $ go tool pprof prof cpu.pprof Entering interactive mode (type "help" for commands) (pprof) top 9.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()で消費 残りはゴミ みたいなもの
  13. 15 結論 • Go言語のトラブルシューティング機能はそれな りに充実している • ただし、Core dumpを解析できるデバッガが無 いのが痛い –

    個人的にはこれがGo言語の一番の弱点だと思う – 実運用でプロセスが異常終了した場合にどうやって 原因を特定するのか。スタックトレースだけでは心 もとない