診断

はじめに

Go エコシステムは、Go プログラムのロジックとパフォーマンスの問題を診断するための、大規模な API とツールのスイートを提供します。このページでは、利用可能なツールを要約し、Go ユーザーが特定の問題に最適なツールを選択するのに役立ちます。

診断ソリューションは、次のグループに分類できます。

注記:一部の診断ツールは互いに干渉する可能性があります。たとえば、正確なメモリプロファイリングは CPU プロファイルに歪みを発生させ、ゴルーチンのブロッキングプロファイリングはスケジューラートレースに影響を与えます。より正確な情報を得るには、ツールを個別に使用してください。

プロファイリング

プロファイリングは、高コストのコードセクションまたは頻繁に呼び出されるコードセクションを特定するのに役立ちます。Go ランタイムは、プロファイリングデータpprof 可視化ツールで期待される形式で提供します。プロファイリングデータは、go test を介したテスト中、または net/http/pprof パッケージから提供されるエンドポイント中に収集できます。ユーザーはプロファイリングデータを取得し、pprof ツールを使用して上位のコードパスをフィルタリングおよび可視化する必要があります。

runtime/pprof パッケージによって提供される定義済みのプロファイル

Go プログラムをプロファイリングするために使用できる他のプロファイラは何ですか?

Linux では、perf ツールを使用して Go プログラムをプロファイリングできます。Perf は cgo/SWIG コードとカーネルのプロファイリングとアンワインドを行うことができるため、ネイティブ/カーネルのパフォーマンスボトルネックに関する洞察を得るのに役立ちます。macOS では、Instruments スイートを使用して Go プログラムをプロファイリングできます。

本番サービスをプロファイリングできますか?

はい。本番環境でプログラムをプロファイリングすることは安全ですが、一部のプロファイル(例:CPU プロファイル)を有効にするとコストが増加します。パフォーマンスの低下が見込まれます。パフォーマンスのペナルティは、本番環境で有効にする前にプロファイラのオーバーヘッドを測定することで推定できます。

本番サービスを定期的にプロファイリングすることをお勧めします。特に単一プロセスの多くのレプリカを持つシステムでは、ランダムにレプリカを選択するのが安全なオプションです。本番プロセスを選択し、Y 秒ごとに X 秒間プロファイリングして結果を可視化と分析のために保存します。次に、定期的に繰り返します。結果は手動で、または自動的にレビューして問題を見つけることができます。プロファイルの収集は互いに干渉する可能性があるため、一度に 1 つのプロファイルのみを収集することをお勧めします。

プロファイリングデータを可視化する最良の方法は何ですか?

Go ツールは、go tool pprof を使用して、プロファイルデータのテキスト、グラフ、および callgrind の可視化を提供します。Go プログラムのプロファイリングを読んで、実際に使用している様子を確認してください。


最も高コストの呼び出しのテキストによるリスト。


最も高コストの呼び出しのグラフによる可視化。

Webリストビューは、ソースコードの行単位で高コストの部分を HTML ページに表示します。次の例では、runtime.concatstrings に 530ms が費やされ、各行のコストがリストに表示されます。


最も高コストの呼び出しの Web リストによる可視化。

プロファイルデータを可視化する別の方法は、フレイムグラフです。フレイムグラフを使用すると、特定の祖先パスを移動できるため、コードの特定のセクションを拡大/縮小できます。アップストリーム pprofは、フレイムグラフをサポートしています。


フレイムグラフは、最も高コストのコードパスを見つけるための可視化を提供します。

組み込みのプロファイルに制限されますか?

ランタイムによって提供されるものに加えて、Go ユーザーは pprof.Profile を介してカスタムプロファイルを作成し、既存のツールを使用してそれらを調べることができます。

プロファイラハンドラ(/debug/pprof/…)を別のパスとポートで提供できますか?

はい。net/http/pprof パッケージはデフォルトでデフォルトの mux にハンドラを登録しますが、パッケージからエクスポートされたハンドラを使用して自分で登録することもできます。

たとえば、次の例では、pprof.Profile ハンドラを :7777 の /custom_debug_path/profile で提供します。

package main

import (
	"log"
	"net/http"
	"net/http/pprof"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/custom_debug_path/profile", pprof.Profile)
	log.Fatal(http.ListenAndServe(":7777", mux))
}

トレース

トレースは、呼び出しの連鎖のライフサイクル全体を通してレイテンシを分析するためにコードをインストルメント化する方法です。Go は、Go ノードごとに最小限のトレースバックエンドとして golang.org/x/net/trace パッケージを提供し、簡単なダッシュボードを備えたシンプルなインストルメンテーションライブラリを提供します。Go はまた、間隔内のランタイムイベントをトレースするための実行トレーサーを提供します。

トレースにより、次のことが可能になります。

モノリシックシステムでは、プログラムの構成要素から診断データを収集することは比較的容易です。すべてのモジュールは 1 つのプロセス内に存在し、ログ、エラー、その他の診断情報を報告するための共通のリソースを共有します。システムが単一のプロセスを超えて成長し、分散型になるにつれて、フロントエンドの Web サーバーからすべてのバックエンドまで、ユーザーにレスポンスが返されるまで呼び出しを追跡することが難しくなります。これが、分散トレースが本番システムをインストルメント化して分析する上で大きな役割を果たす場所です。

分散トレースは、ユーザーリクエストのライフサイクル全体を通してレイテンシを分析するためにコードをインストルメント化する方法です。システムが分散されていて、従来のプロファイリングツールとデバッグツールがスケールしない場合、分散トレースツールを使用してユーザーリクエストと RPC のパフォーマンスを分析することを検討する必要があります。

分散トレースにより、次のことが可能になります。

Go エコシステムは、トレースシステムごとにさまざまな分散トレースライブラリと、バックエンドに依存しないライブラリを提供します。

各関数呼び出しを自動的にインターセプトしてトレースを作成する方法はありますか?

Go は、すべての関数呼び出しを自動的にインターセプトしてトレーススパンを作成する方法を提供していません。スパンの作成、終了、および注釈付けを行うには、コードを手動でインストルメント化する必要があります。

Go ライブラリでトレースヘッダーを伝播する方法は?

context.Contextでトレース識別子とタグを伝播できます。業界ではまだ、標準的なトレースキーやトレースヘッダーの共通表現はありません。各トレースプロバイダーは、Go ライブラリで伝播ユーティリティを提供する責任があります。

標準ライブラリまたはランタイムのその他の低レベルイベントをトレースに含めることはできますか?

標準ライブラリとランタイムは、低レベルの内部イベントを通知するためのいくつかの追加 API を公開しようとしています。たとえば、httptrace.ClientTraceは、発信リクエストのライフサイクルで低レベルイベントを追跡するための API を提供します。ランタイム実行トレーサーから低レベルのランタイムイベントを取得し、ユーザーがユーザーイベントを定義して記録できるようにするための取り組みが進行中です。

デバッグ

デバッグとは、プログラムが誤動作する理由を特定するプロセスです。デバッガーを使用すると、プログラムの実行フローと現在の状態を理解できます。デバッグにはいくつかのスタイルがありますが、このセクションでは、デバッガーをプログラムにアタッチすることと、コアダンプデバッグのみに焦点を当てます。

Go ユーザーは主に次のデバッガーを使用します。

デバッガーは Go プログラムでどの程度うまく機能しますか?

gc コンパイラは、関数インライン化や変数レジスタ化などの最適化を実行します。これらの最適化により、デバッガーによるデバッグが困難になる場合があります。最適化されたバイナリでデバッガーが機能するように、生成された DWARF 情報の品質を向上させるための取り組みが進行中です。これらの改善が利用可能になるまでは、デバッグ対象のコードをビルドする際に最適化を無効にすることをお勧めします。次のコマンドは、コンパイラの最適化を行わずにパッケージをビルドします。

$ go build -gcflags=all="-N -l"

改善の取り組みの一環として、Go 1.10 は新しいコンパイラフラグ -dwarflocationlists を導入しました。このフラグにより、コンパイラはデバッガーが最適化されたバイナリで機能するのに役立つ位置リストを追加します。次のコマンドは、最適化を行いますが DWARF 位置リストを使用するパッケージをビルドします。

$ go build -gcflags="-dwarflocationlists=true"

推奨されるデバッガーユーザーインターフェースは何ですか?

Delve と gdb の両方が CLI を提供していますが、ほとんどのエディター統合と IDE はデバッグ固有のユーザーインターフェースを提供しています。

Go プログラムで事後デバッグを行うことは可能ですか?

コアダンプファイルは、実行中のプロセスのメモリダンプとそのプロセス状態を含むファイルです。主にプログラムの事後デバッグに使用され、実行中の状態を理解するために役立ちます。これらの2つのケースにより、コアダンプのデバッグは、事後分析と本番サービスの分析に役立つ優れた診断手段となります。Goプログラムからコアファイルを取得し、delveまたはgdbを使用してデバッグすることができます。コアダンプデバッグページに手順が記載されています。

ランタイム統計とイベント

ランタイムは、内部イベントの統計とレポートを提供し、ユーザーがランタイムレベルでのパフォーマンスと使用率の問題を診断できるようにします。

ユーザーはこれらの統計を監視することで、Goプログラムの全体的な状態とパフォーマンスをよりよく理解できます。頻繁に監視される統計と状態の例を以下に示します。

実行トレーサー

Goには、幅広いランタイムイベントをキャプチャするランタイム実行トレーサーが付属しています。スケジューリング、システムコール、ガベージコレクション、ヒープサイズ、その他のイベントはランタイムによって収集され、go tool traceによって視覚化できます。実行トレーサーは、レイテンシと使用率の問題を検出するためのツールです。CPUの使用率、ネットワークまたはシステムコールがゴルーチンのプリエンプションの原因となっているタイミングを調べることができます。

トレーサーは、以下のような場合に役立ちます。

ただし、過剰なメモリ使用量やCPU使用量の原因を分析するなど、ホットスポットの特定には適していません。代わりに、まずプロファイリングツールを使用して対処してください。

上記のgo tool traceの視覚化は、実行が正常に開始された後、シリアライズされたことを示しています。これは、ボトルネックを作成する共有リソースに対するロック競合がある可能性を示唆しています。

ランタイムトレースの収集と分析については、go tool traceを参照してください。

GODEBUG

GODEBUG環境変数が適切に設定されている場合、ランタイムはイベントと情報を生成します。

GODEBUG環境変数は、標準ライブラリとランタイムでの命令セット拡張の使用を無効にするために使用できます。