github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/runtime/pprof/pprof.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package pprofは、pprof視覚化ツールで期待される形式でランタイムプロファイリングデータを書き込みます。 6 // # Goプログラムのプロファイリング 7 // 8 // Goプログラムをプロファイリングする最初のステップは、プロファイリングを有効にすることです。 9 // 標準のテストパッケージでビルドされたベンチマークのプロファイリングをサポートするためには、go testに組み込まれています。 10 // たとえば、次のコマンドは現在のディレクトリでベンチマークを実行し、CPUプロファイルとメモリプロファイルをcpu.profとmem.profに書き込みます: 11 // 12 // go test -cpuprofile cpu.prof -memprofile mem.prof -bench . 13 // 14 // スタンドアロンのプログラムに同等のプロファイリングサポートを追加するには、以下のようなコードをmain関数に追加します: 15 // 16 // var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") 17 // var memprofile = flag.String("memprofile", "", "write memory profile to `file`") 18 // 19 // func main() { 20 // flag.Parse() 21 // if *cpuprofile != "" { 22 // f, err := os.Create(*cpuprofile) 23 // if err != nil { 24 // log.Fatal("could not create CPU profile: ", err) 25 // } 26 // defer f.Close() // エラーハンドリングは例外です 27 // if err := pprof.StartCPUProfile(f); err != nil { 28 // log.Fatal("could not start CPU profile: ", err) 29 // } 30 // defer pprof.StopCPUProfile() 31 // } 32 // 33 // // ... プログラムの残り ... 34 // 35 // if *memprofile != "" { 36 // f, err := os.Create(*memprofile) 37 // if err != nil { 38 // log.Fatal("could not create memory profile: ", err) 39 // } 40 // defer f.Close() // エラーハンドリングは例外です 41 // runtime.GC() // 最新の統計情報を取得 42 // if err := pprof.WriteHeapProfile(f); err != nil { 43 // log.Fatal("could not write memory profile: ", err) 44 // } 45 // } 46 // } 47 // 48 // プロファイリングデータへの標準のHTTPインターフェースもあります。以下の行を追加すると、/debug/pprof/の下にハンドラがインストールされ、ライブプロファイルをダウンロードすることができます: 49 // 50 // import _ "net/http/pprof" 51 // 52 // 詳細については、net/http/pprofパッケージを参照してください。 53 // プロファイルはpprofツールで可視化することができます: 54 // 55 // go tool pprof cpu.prof 56 // 57 // pprofコマンドラインからは多くのコマンドが利用できます。 58 // よく使用されるコマンドには、「top」(プログラムのホットスポットの要約を表示する)や、「web」(ホットスポットとその呼び出しグラフの対話型グラフを開く)があります。 59 // すべてのpprofコマンドに関する情報については、「help」を使用してください。 60 // pprofに関する詳細情報は、次を参照してください 61 // 62 // https://github.com/google/pprof/blob/main/doc/README.md. 63 package pprof 64 65 import ( 66 "github.com/shogo82148/std/io" 67 "github.com/shogo82148/std/sync" 68 ) 69 70 // Profileは、特定のイベント(例えば、割り当て)へのインスタンスにつながる呼び出しシーケンスを示すスタックトレースの集合です。 71 // パッケージは自身のプロファイルを作成し、維持することができます。最も一般的な使用例は、 72 // ファイルやネットワーク接続のような、明示的に閉じる必要があるリソースの追跡です。 73 // 74 // プロファイルのメソッドは、複数のゴルーチンから同時に呼び出すことができます。 75 // 76 // 各プロファイルには一意の名前があります。いくつかのプロファイルは事前に定義されています: 77 // 78 // goroutine - 現在のすべてのゴルーチンのスタックトレース 79 // heap - 生存しているオブジェクトのメモリ割り当てのサンプリング 80 // allocs - 過去のすべてのメモリ割り当てのサンプリング 81 // threadcreate - 新しいOSスレッドの作成につながったスタックトレース 82 // block - 同期プリミティブでのブロックにつながったスタックトレース 83 // mutex - 競合するミューテックスの保持者のスタックトレース 84 // 85 // これらの事前定義されたプロファイルは自己維持し、明示的な 86 // [Profile.Add] または [Profile.Remove] メソッド呼び出しでパニックを起こします。 87 // 88 // CPUプロファイルはProfileとして利用できません。これは特別なAPIを持っており、 89 // [StartCPUProfile] と [StopCPUProfile] 関数があります。これはプロファイリング中に 90 // 出力をライターにストリームします。 91 // 92 // # Heap profile 93 // 94 // ヒーププロファイルは、最も最近に完了したガベージコレクション時点の統計を報告します。 95 // これは、プロファイルを生データからガベージに偏らせるのを避けるため、より最近の割り当てを省略します。 96 // ガベージコレクションが一度も行われていない場合、ヒーププロファイルはすべての既知の割り当てを報告します。 97 // この例外は主に、通常はデバッグ目的で、ガベージコレクションが有効になっていないプログラムで役立ちます。 98 // 99 // ヒーププロファイルは、アプリケーションメモリ内のすべてのライブオブジェクトの割り当て場所と、 100 // プログラム開始以降に割り当てられたすべてのオブジェクトを追跡します。 101 // Pprofの -inuse_space、-inuse_objects、-alloc_space、および -alloc_objects 102 // フラグは、表示するものを選択し、デフォルトは -inuse_space(ライブオブジェクト、サイズによってスケーリング)です。 103 // 104 // # Allocs profile 105 // 106 // allocsプロファイルはheapプロファイルと同じですが、デフォルトの 107 // pprof表示を -alloc_space(プログラムが開始してから割り当てられた 108 // バイト数の合計(ガベージコレクションされたバイトを含む))に変更します。 109 // 110 // # Block profile 111 // 112 // ブロックプロファイルは、[sync.Mutex]、[sync.RWMutex]、[sync.WaitGroup]、 113 // [sync.Cond]、およびチャネルの送信/受信/選択などの同期プリミティブで 114 // ブロックされた時間を追跡します。 115 // 116 // スタックトレースは、ブロックした場所(例えば、[sync.Mutex.Lock])に対応します。 117 // 118 // サンプル値は、そのスタックトレースでブロックされた累積時間に対応します。 119 // これは [runtime.SetBlockProfileRate] で指定された時間ベースのサンプリングに従います。 120 // 121 // # Mutex profile 122 // 123 // ミューテックスプロファイルは、[sync.Mutex]、[sync.RWMutex]、およびランタイム内部のロックなど、 124 // ミューテックスの競合を追跡します。 125 // 126 // スタックトレースは、競合を引き起こすクリティカルセクションの終わりに対応します。 127 // 例えば、他のゴルーチンがロックを取得しようと待っている間に長時間保持されたロックは、 128 // ロックが最終的に解除されたとき(つまり、[sync.Mutex.Unlock] で)に競合を報告します。 129 // 130 // サンプル値は、他のゴルーチンがロックを待ってブロックされた約積算時間に対応します。 131 // これは [runtime.SetMutexProfileFraction] で指定されたイベントベースのサンプリングに従います。 132 // 例えば、呼び出し元がロックを1秒間保持している間に他の5つのゴルーチンがロックを取得するために 133 // 完全に秒を待っている場合、そのアンロック呼び出しスタックは5秒間の競合を報告します。 134 // 135 // ランタイム内部のロックは常に"runtime._LostContendedRuntimeLock"の位置で報告されます。 136 // ランタイム内部のロックのより詳細なスタックトレースは、 137 // `GODEBUG=runtimecontentionstacks=1`を設定することで取得できます(注意事項については、 138 // パッケージ [runtime] のドキュメントを参照してください)。 139 type Profile struct { 140 name string 141 mu sync.Mutex 142 m map[any][]uintptr 143 count func() int 144 write func(io.Writer, int) error 145 } 146 147 // NewProfileは指定された名前で新しいプロファイルを作成します。 148 // すでにその名前のプロファイルが存在する場合、NewProfileはパニックを引き起こします。 149 // 各パッケージごとに別の名前空間を作成するために、'import/path.'接頭辞を使用するのが一般的です。 150 // pprofデータを読み取るさまざまなツールとの互換性のために、プロファイル名にはスペースを含めないでください。 151 func NewProfile(name string) *Profile 152 153 // Lookupは指定された名前のプロフィールを返します。存在しない場合はnilを返します。 154 func Lookup(name string) *Profile 155 156 // Profilesは、名前でソートされたすべてのプロフィールのスライスを返します。 157 func Profiles() []*Profile 158 159 // Nameはこのプロフィールの名前を返します。プロフィールを再取得するために [Lookup] に渡すことができます。 160 func (p *Profile) Name() string 161 162 // Countは現在のプロファイル内の実行スタックの数を返します。 163 func (p *Profile) Count() int 164 165 // Addは現在の実行スタックを、値と関連付けてプロファイルに追加します。 166 // Addは値を内部のマップに保存するため、値はマップのキーとして使用するのに適しており、対応する [Profile.Remove] 呼び出しまでガベージコレクトされません。 167 // Addはもしプロファイルにすでに値のスタックが含まれている場合、パニックを発生させます。 168 // 169 // skipパラメータは [runtime.Caller] のskipと同じ意味を持ち、スタックトレースが始まる場所を制御します。 170 // skip=0を渡すと、Addを呼び出した関数からトレースが始まります。例えば、以下のような実行スタックがあるとします: 171 // 172 // Add 173 // rpc.NewClientから呼び出される 174 // mypkg.Runから呼び出される 175 // main.mainから呼び出される 176 // 177 // skip=0を渡すと、スタックトレースはrpc.NewClient内でのAddの呼び出しで始まります。 178 // skip=1を渡すと、スタックトレースはmypkg.Run内でのNewClientの呼び出しで始まります。 179 func (p *Profile) Add(value any, skip int) 180 181 // Removeはプロファイルから関連付けられた実行スタックを削除します。 182 // valueがプロファイルに存在しない場合、何もしません。 183 func (p *Profile) Remove(value any) 184 185 // WriteToはプロファイルのスナップショットをwにpprof形式で書き込みます。 186 // wへの書き込みがエラーを返す場合、WriteToはそのエラーを返します。 187 // それ以外の場合、WriteToはnilを返します。 188 // 189 // debugパラメータは追加の出力を有効にします。 190 // debug=0を渡すと、https://github.com/google/pprof/tree/master/proto#overviewで 191 // 説明されているgzip圧縮されたプロトコルバッファ形式で書き込まれます。 192 // debug=1を渡すと、関数名と行番号をアドレスに変換したレガシーテキスト形式で書き込まれます。 193 // これにより、プログラマがツールなしでプロファイルを読むことができます。 194 // 195 // プリセットのプロファイルは、他のdebugの値に意味を割り当てることができます。 196 // たとえば、"goroutine"プロファイルの場合、debug=2は、 197 // ゴルーチンのスタックをGoプログラムが回復不可能なパニックによって終了する際に使用する同じ形式で表示することを意味します。 198 func (p *Profile) WriteTo(w io.Writer, debug int) error 199 200 // WriteHeapProfileは、[Lookup]("heap").WriteTo(w, 0)の略記です。 201 // 後方互換性のために保持されています。 202 func WriteHeapProfile(w io.Writer) error 203 204 // StartCPUProfileは現在のプロセスに対してCPUプロファイリングを有効にします。 205 // プロファイリング中は、プロファイルがバッファリングされ、wに書き込まれます。 206 // StartCPUProfileは、すでにプロファイリングが有効な場合にエラーを返します。 207 // 208 // Unix系システムでは、-buildmode=c-archiveまたは-buildmode=c-sharedで 209 // ビルドされたGoコードでは、デフォルトではStartCPUProfileは動作しません。 210 // StartCPUProfileはSIGPROFシグナルを利用していますが、 211 // そのシグナルはGoが使用するものではなく、 212 // メインプログラムのSIGPROFシグナルハンドラ(あれば)に送信されます。 213 // 関数 [os/signal.Notify] を [syscall.SIGPROF] に対して呼び出すことで、 214 // それが動作するようにすることができますが、その場合、 215 // メインプログラムで実行されているプロファイリングが壊れる可能性があります。 216 func StartCPUProfile(w io.Writer) error 217 218 // StopCPUProfileは現在のCPUプロファイルを停止します(もし存在する場合)。 219 // StopCPUProfileはプロファイルの書き込みが完了するまでのみ戻ります。 220 func StopCPUProfile()