[インデックス 17685] ファイルの概要
このコミットは、Go言語のプロファイリングツールであるpprof
に、ブロックプロファイル(Block Profile)のサポートを追加するものです。具体的には、pprof
スクリプトが/pprof/block
エンドポイントからブロックプロファイルデータを取得し、解析できるようにするための変更が含まれています。
コミット
commit 39361170d40a8d846d5065a614bd5a722a6aceb4
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Mon Sep 23 14:15:20 2013 -0700
misc/pprof: support block profile
Fixes #6347.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/13845044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/39361170d40a8d846d5065a614bd5a722a6aceb4
元コミット内容
misc/pprof: support block profile
Fixes #6347.
変更の背景
このコミットは、Go言語のIssue #6347「pprof
がブロックプロファイルをサポートすべき」に対応するものです。Goアプリケーションのパフォーマンスチューニングにおいて、CPU使用率だけでなく、ゴルーチン(goroutine)がブロッキング操作(例: チャンネル操作、ミューテックスロック、システムコールなど)によって待機している時間を特定することは非常に重要です。
Goのランタイムは、このようなブロッキングイベントに関するプロファイルデータ(ブロックプロファイル)を収集する機能を提供しています。しかし、このコミット以前のpprof
ツールは、CPUプロファイル、ヒーププロファイル、スレッドプロファイルなどの他のプロファイルタイプはサポートしていましたが、ブロックプロファイルデータを直接解析・可視化する機能がありませんでした。
この不足を解消し、開発者がGoアプリケーションのブロッキングパフォーマンスをより詳細に分析できるようにするために、pprof
ツールにブロックプロファイルのサポートを追加する必要がありました。これにより、アプリケーションのボトルネックがCPUバウンドではなく、I/O待機や同期プリミティブの競合などによるブロッキングにある場合に、その原因を特定しやすくなります。
前提知識の解説
Go言語のプロファイリング
Go言語には、アプリケーションのパフォーマンスを分析するための強力なプロファイリングツール群が標準で提供されています。これらは主にruntime/pprof
パッケージとnet/http/pprof
パッケージを通じて利用できます。
runtime/pprof
: プログラム内で直接プロファイルデータを生成・書き出すためのAPIを提供します。net/http/pprof
: HTTPサーバーとしてプロファイルデータを公開し、リモートから簡単に取得できるようにします。開発中にアプリケーションのパフォーマンスを監視する際によく利用されます。
Goのプロファイリングは、主に以下の種類があります。
- CPUプロファイル (CPU Profile): CPUがどの関数で時間を費やしているかを測定します。
- ヒーププロファイル (Heap Profile): メモリ割り当ての状況を測定し、メモリリークや過剰なメモリ使用を特定します。
- ゴルーチンプロファイル (Goroutine Profile): 実行中のすべてのゴルーチンのスタックトレースを記録します。
- ブロックプロファイル (Block Profile): ゴルーチンがブロッキング操作(例: チャンネル送受信、ミューテックスロック、システムコールなど)によって待機している時間を測定します。これは、並行処理における競合やデッドロックの可能性を特定するのに役立ちます。
- ミューテックスプロファイル (Mutex Profile): ミューテックスの競合状況を測定します。
- スレッド作成プロファイル (Threadcreate Profile): OSスレッドの作成状況を測定します。
pprof
ツール
pprof
は、Go言語のプロファイルデータを解析し、グラフやテキスト形式で可視化するためのコマンドラインツールです。Go SDKに含まれており、go tool pprof
として実行できます。pprof
は、プロファイルデータを読み込み、それをコールグラフ、フレームグラフ、テキストリストなどの形式で表示することで、開発者がパフォーマンスのボトルネックを特定するのを支援します。
pprof
は、プロファイルデータをファイルから読み込むだけでなく、net/http/pprof
によって公開されたHTTPエンドポイントから直接データを取得することもできます。例えば、http://localhost:8080/debug/pprof/heap
のようなURLからヒーププロファイルを取得し、解析することが可能です。
ブロッキング操作
Goの並行処理モデルはゴルーチンとチャンネルに基づいています。ゴルーチンは軽量なスレッドのようなもので、チャンネルはゴルーチン間の通信手段です。しかし、これらの並行処理プリミティブを使用する際には、ゴルーチンが何らかのリソースを待つために実行を一時停止する「ブロッキング」が発生することがあります。
一般的なブロッキング操作には以下のようなものがあります。
- チャンネル操作: バッファリングされていないチャンネルでの送受信、またはバッファリングされたチャンネルが満杯/空の場合の送受信。
- ミューテックスロック:
sync.Mutex
やsync.RWMutex
などのロックの取得待ち。 - システムコール: ファイルI/O、ネットワークI/Oなど、OSレベルの操作の完了待ち。
- タイマー/スリープ:
time.Sleep
やtime.After
などによる意図的な待機。
ブロックプロファイルは、これらのブロッキング操作が発生した場所と、それぞれの操作でゴルーチンがどれくらいの時間待機したかを詳細に記録します。これにより、アプリケーションの応答性が低下している原因が、CPUの計算能力不足ではなく、リソースの競合やI/Oの遅延にある場合に、その根本原因を特定できます。
技術的詳細
このコミットは、misc/pprof
スクリプト(Perlスクリプト)にブロックプロファイルのサポートを追加するために、主に以下の3つの変更を行っています。
-
$BLOCK_PAGE
変数の追加:my $BLOCK_PAGE = "/pprof/block";
この行は、ブロックプロファイルデータを提供するHTTPエンドポイントのパスを定義しています。Goのnet/http/pprof
パッケージは、デフォルトで/debug/pprof/block
というパスでブロックプロファイルを公開します。pprof
ツールはこのパスを使用してプロファイルデータを取得します。 -
IsProfileURL
サブルーチン内の正規表現の更新: 変更前:$profile_name =~ m,^(?:(https?)://|)([^/:]+):(\\d+)(|\\@\\d+)(|/|(.*?)($PROFILE_PAGE|$PMUPROFILE_PAGE|$HEAP_PAGE|$GROWTH_PAGE|$THREAD_PAGE|$CONTENTION_PAGE|$WALL_PAGE|$FILTEREDPROFILE_PAGE))$,o)
変更後:$profile_name =~ m,^(?:(https?)://|)([^/:]+):(\\d+)(|\\@\\d+)(|/|(.*?)($PROFILE_PAGE|$PMUPROFILE_PAGE|$HEAP_PAGE|$GROWTH_PAGE|$THREAD_PAGE|$BLOCK_PAGE|$CONTENTION_PAGE|$WALL_PAGE|$FILTEREDPROFILE_PAGE))$,o)
この正規表現は、pprof
ツールが解析できるプロファイルURLのパターンを定義しています。変更により、既存のプロファイルタイプ(CPU、ヒープ、スレッドなど)に加えて、新しく定義された$BLOCK_PAGE
(/pprof/block
)も有効なプロファイルURLとして認識されるようになりました。これにより、pprof http://localhost:8080/debug/pprof/block
のようなコマンドでブロックプロファイルを直接取得・解析できるようになります。 -
コメントの更新:
pprof
ツールのヘルプメッセージまたは内部コメントで、サポートされるプロファイルタイプの一覧に$BLOCK_PAGE
が追加されました。これは、ツールの機能が拡張されたことを示すドキュメンテーションの一部です。
これらの変更により、pprof
ツールは/pprof/block
エンドポイントからブロックプロファイルデータを取得し、他のプロファイルタイプと同様に解析・可視化できるようになります。ユーザーは、アプリケーションのブロッキングパフォーマンスを詳細に分析し、ボトルネックを特定するための新しい強力な手段を手に入れることになります。
コアとなるコードの変更箇所
--- a/misc/pprof
+++ b/misc/pprof
@@ -110,6 +110,7 @@ my $PS2PDF = "ps2pdf";
my $HEAP_PAGE = "/pprof/heap";
my $THREAD_PAGE = "/pprof/thread";
my $PROFILE_PAGE = "/pprof/profile"; # must support cgi-param "?seconds=#"
+my $BLOCK_PAGE = "/pprof/block";
my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\\\?.*)?"; # must support cgi-param
# ?seconds=#&event=x&period=n
my $GROWTH_PAGE = "/pprof/growth";
@@ -162,7 +163,7 @@ pprof [options] <profile>
The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
$GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
- $THREAD_PAGE, or /pprof/filteredprofile.
+ $THREAD_PAGE, $BLOCK_PAGE or /pprof/filteredprofile.
For instance:
pprof http://myserver.com:80$HEAP_PAGE
If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
@@ -3002,7 +3003,7 @@ sub IsProfileURL {
sub ParseProfileURL {
my $profile_name = shift;
if (defined($profile_name) &&
- $profile_name =~ m,^(?:(https?)://|)([^/:]+):(\\d+)(|\\@\\d+)(|/|(.*?)($PROFILE_PAGE|$PMUPROFILE_PAGE|$HEAP_PAGE|$GROWTH_PAGE|$THREAD_PAGE|$CONTENTION_PAGE|$WALL_PAGE|$FILTEREDPROFILE_PAGE))$,o) {
+ $profile_name =~ m,^(?:(https?)://|)([^/:]+):(\\d+)(|\\@\\d+)(|/|(.*?)($PROFILE_PAGE|$PMUPROFILE_PAGE|$HEAP_PAGE|$GROWTH_PAGE|$THREAD_PAGE|$BLOCK_PAGE|$CONTENTION_PAGE|$WALL_PAGE|$FILTEREDPROFILE_PAGE))$,o) {
# $7 is $PROFILE_PAGE/$HEAP_PAGE/etc. $5 is *everything* after
# the hostname, as long as that everything is the empty string,
# a slash, or something ending in $PROFILE_PAGE/$HEAP_PAGE/etc.
コアとなるコードの解説
-
my $BLOCK_PAGE = "/pprof/block";
: この行は、Perlスクリプト内で$BLOCK_PAGE
という変数を定義し、その値として文字列"/pprof/block"
を割り当てています。これは、Goアプリケーションがnet/http/pprof
を通じて公開するブロックプロファイルのエンドポイントパスに対応します。pprof
ツールがこのパスを認識することで、ブロックプロファイルデータを取得するためのURLを構築できるようになります。 -
コメントの更新:
The /<service> can be ...
で始まるコメント行は、pprof
ツールがサポートするプロファイルサービスの一覧を示しています。この一覧に$BLOCK_PAGE
が追加されたことで、ユーザーや開発者がpprof
がブロックプロファイルを扱えることを視覚的に確認できるようになりました。これは機能追加のドキュメンテーション的な側面を持ちます。 -
ParseProfileURL
サブルーチン内の正規表現の変更:ParseProfileURL
サブルーチンは、pprof
コマンドに渡されたURLが有効なプロファイルURLであるかどうかを検証し、そのURLからホスト、ポート、プロファイルタイプなどの情報を抽出するために使用されます。 変更された正規表現は、URLのパス部分にマッチするパターンを拡張しています。具体的には、($PROFILE_PAGE|$PMUPROFILE_PAGE|$HEAP_PAGE|$GROWTH_PAGE|$THREAD_PAGE|$CONTENTION_PAGE|$WALL_PAGE|$FILTEREDPROFILE_PAGE)
という既存のOR条件のグループに|$BLOCK_PAGE
が追加されました。 これにより、pprof
ツールは/pprof/block
を含むURLも有効なプロファイルURLとして認識し、適切に処理できるようになります。この正規表現の変更は、pprof
ツールがブロックプロファイルデータを取得するためのURLを正しく解析するために不可欠な部分です。
これらの変更は、pprof
ツールがブロックプロファイルデータを取得し、解析するための基盤を確立するものであり、Goアプリケーションのパフォーマンス分析能力を向上させる上で重要なステップでした。
関連リンク
- Go Issue #6347:
pprof
should support block profile: https://github.com/golang/go/issues/6347 - Go CL 13845044:
misc/pprof
: support block profile: https://golang.org/cl/13845044 runtime/pprof
パッケージのドキュメント: https://pkg.go.dev/runtime/pprofnet/http/pprof
パッケージのドキュメント: https://pkg.go.dev/net/http/pprof
参考にした情報源リンク
- Go公式ドキュメント
- GoのIssueトラッカー
- Goのコードレビューシステム (Gerrit)
pprof
ツールの使用方法に関する一般的な情報源 (ブログ記事、チュートリアルなど)