Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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のプロファイリングは、主に以下の種類があります。

  1. CPUプロファイル (CPU Profile): CPUがどの関数で時間を費やしているかを測定します。
  2. ヒーププロファイル (Heap Profile): メモリ割り当ての状況を測定し、メモリリークや過剰なメモリ使用を特定します。
  3. ゴルーチンプロファイル (Goroutine Profile): 実行中のすべてのゴルーチンのスタックトレースを記録します。
  4. ブロックプロファイル (Block Profile): ゴルーチンがブロッキング操作(例: チャンネル送受信、ミューテックスロック、システムコールなど)によって待機している時間を測定します。これは、並行処理における競合やデッドロックの可能性を特定するのに役立ちます。
  5. ミューテックスプロファイル (Mutex Profile): ミューテックスの競合状況を測定します。
  6. スレッド作成プロファイル (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.Mutexsync.RWMutexなどのロックの取得待ち。
  • システムコール: ファイルI/O、ネットワークI/Oなど、OSレベルの操作の完了待ち。
  • タイマー/スリープ: time.Sleeptime.Afterなどによる意図的な待機。

ブロックプロファイルは、これらのブロッキング操作が発生した場所と、それぞれの操作でゴルーチンがどれくらいの時間待機したかを詳細に記録します。これにより、アプリケーションの応答性が低下している原因が、CPUの計算能力不足ではなく、リソースの競合やI/Oの遅延にある場合に、その根本原因を特定できます。

技術的詳細

このコミットは、misc/pprofスクリプト(Perlスクリプト)にブロックプロファイルのサポートを追加するために、主に以下の3つの変更を行っています。

  1. $BLOCK_PAGE変数の追加: my $BLOCK_PAGE = "/pprof/block"; この行は、ブロックプロファイルデータを提供するHTTPエンドポイントのパスを定義しています。Goのnet/http/pprofパッケージは、デフォルトで/debug/pprof/blockというパスでブロックプロファイルを公開します。pprofツールはこのパスを使用してプロファイルデータを取得します。

  2. 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のようなコマンドでブロックプロファイルを直接取得・解析できるようになります。

  3. コメントの更新: 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.

コアとなるコードの解説

  1. my $BLOCK_PAGE = "/pprof/block";: この行は、Perlスクリプト内で$BLOCK_PAGEという変数を定義し、その値として文字列"/pprof/block"を割り当てています。これは、Goアプリケーションがnet/http/pprofを通じて公開するブロックプロファイルのエンドポイントパスに対応します。pprofツールがこのパスを認識することで、ブロックプロファイルデータを取得するためのURLを構築できるようになります。

  2. コメントの更新: The /<service> can be ...で始まるコメント行は、pprofツールがサポートするプロファイルサービスの一覧を示しています。この一覧に$BLOCK_PAGEが追加されたことで、ユーザーや開発者がpprofがブロックプロファイルを扱えることを視覚的に確認できるようになりました。これは機能追加のドキュメンテーション的な側面を持ちます。

  3. 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公式ドキュメント
  • GoのIssueトラッカー
  • Goのコードレビューシステム (Gerrit)
  • pprofツールの使用方法に関する一般的な情報源 (ブログ記事、チュートリアルなど)