[インデックス 17906] ファイルの概要
このコミットは、Goランタイムのガベージコレクション(GC)におけるスタック走査の効率化を目的としています。具体的には、src/pkg/runtime/mgc0.c
ファイルが変更されており、これはGoランタイムのメモリ管理とガベージコレクタの主要な部分を実装しているC言語のソースファイルです。このファイルは、GCのマークフェーズにおけるオブジェクトの走査、ポインタの識別、およびワークバッファの管理など、GCの低レベルな動作を定義しています。
コミット
コミットハッシュ: 0368a7ceb6e741a641a07e3ae381bdc9fc160a15
作者: Carl Shapiro cshapiro@google.com
コミット日時: Tue Dec 3 14:12:55 2013 -0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0368a7ceb6e741a641a07e3ae381bdc9fc160a15
元コミット内容
runtime: move stack scanning into the parallel mark phase
This change reduces the cost of the stack scanning by frames.
It moves the stack scanning from the serial root enumeration
phase to the parallel tracing phase. The output that follows
are timings for the issue 6482 benchmark
Baseline
BenchmarkGoroutineSelect 50 108027405 ns/op
BenchmarkGoroutineBlocking 50 89573332 ns/op
BenchmarkGoroutineForRange 20 95614116 ns/op
BenchmarkGoroutineIdle 20 122809512 ns/op
Stack scan by frames, non-parallel
BenchmarkGoroutineSelect 20 297138929 ns/op
BenchmarkGoroutineBlocking 20 301137599 ns/op
BenchmarkGoroutineForRange 10 312499469 ns/op
BenchmarkGoroutineIdle 10 209428876 ns/op
Stack scan by frames, parallel
BenchmarkGoroutineSelect 20 183938431 ns/op
BenchmarkGoroutineBlocking 20 170109999 ns/op
BenchmarkGoroutineForRange 20 179628882 ns/op
BenchmarkGoroutineIdle 20 157541498 ns/op
The remaining performance disparity is due to inefficiencies
in gentraceback and its callees. The effect was isolated by
using a parallel stack scan where scanstack was modified to do
a conservative scan of the stack segments without gentraceback
followed by a call of gentrackback with a no-op callback.
The output that follows are the top-10 most frequent tops of
stacks as determined by the Linux perf record facility.
Baseline
+ 25.19% gc.test gc.test [.] runtime.xchg
+ 19.00% gc.test gc.test [.] scanblock
+ 8.53% gc.test gc.test [.] scanstack
+ 8.46% gc.test gc.test [.] flushptrbuf
+ 5.08% gc.test gc.test [.] procresize
+ 3.57% gc.test gc.test [.] runtime.chanrecv
+ 2.94% gc.test gc.test [.] dequeue
+ 2.74% gc.test gc.test [.] addroots
+ 2.25% gc.test gc.test [.] runtime.ready
+ 1.33% gc.test gc.test [.] runtime.cas64
Gentraceback
+ 18.12% gc.test gc.test [.] runtime.xchg
+ 14.68% gc.test gc.test [.] scanblock
+ 8.20% gc.test gc.test [.] runtime.gentraceback
+ 7.38% gc.test gc.test [.] flushptrbuf
+ 6.84% gc.test gc.test [.] scanstack
+ 5.92% gc.test gc.test [.] runtime.findfunc
+ 3.62% gc.test gc.test [.] procresize
+ 3.15% gc.test gc.test [.] readvarint
+ 1.92% gc.test gc.test [.] addroots
+ 1.87% gc.test gc.test [.] runtime.chanrecv
R=golang-dev, dvyukov, rsc
CC=golang-dev
https://golang.org/cl/17410043
変更の背景
このコミットの主な目的は、Goランタイムのガベージコレクション(GC)におけるスタック走査のパフォーマンスを向上させることです。以前の実装では、スタック走査はGCの「シリアルなルート列挙フェーズ」で行われていました。このフェーズは、GCがヒープ上の到達可能なオブジェクトを特定するための出発点(ルート)をすべて見つけるために、アプリケーションの実行を一時停止(Stop-the-World: STW)させる必要がありました。スタック走査がこのシリアルなSTWフェーズで行われると、その処理時間がGCの一時停止時間に直接影響し、アプリケーションの応答性に悪影響を与えます。
この変更では、スタック走査を「並列トレースフェーズ」に移行させることで、このボトルネックを解消しようとしています。並列トレースフェーズは、GCのマークフェーズの一部であり、複数のヘルパーゴルーチンが同時にヒープを走査して到達可能なオブジェクトをマークします。スタック走査をこの並列フェーズに組み込むことで、GCの一時停止時間を短縮し、全体的なGCの効率を向上させることが期待されます。
コミットメッセージには、この変更によるベンチマーク結果が示されており、特に「Stack scan by frames, non-parallel」と比較して「Stack scan by frames, parallel」が大幅に改善されていることがわかります。これは、スタック走査の並列化が成功し、GCのパフォーマンスが向上したことを裏付けています。ただし、gentraceback
とその呼び出し元に起因する非効率性が依然として残っていることも指摘されており、さらなる最適化の余地があることが示唆されています。
前提知識の解説
Goのガベージコレクション (GC) の基本
Goのガベージコレクタは、コンカレントなトライカラーマーク&スイープ方式を採用しています。これは、アプリケーションの実行と並行してGCが動作し、一時停止時間(Stop-the-World: STW)を最小限に抑えるように設計されています。
- トライカラーマーク&スイープ: オブジェクトを「白(未訪問)」「灰(訪問済みだが子を未走査)」「黒(訪問済みで子も走査済み)」の3色に分類し、到達可能なオブジェクトをマークし、マークされなかったオブジェクトを解放します。
- STWフェーズ: GCサイクルには、ごく短いSTWフェーズがいくつか存在します。
- マーク開始 (Mark Start): GCルート(グローバル変数、レジスタ、ゴルーチンのスタックなど)を特定するためにSTWが発生します。
- マーク終了 (Mark Termination): すべてのゴルーチンがGCセーフポイントに到達していることを確認し、スタックとグローバル変数を再度走査して、到達可能なオブジェクトが見落とされていないことを確認するためにSTWが発生します。
GCにおけるルートの重要性
ガベージコレクタは、ヒープ上のオブジェクトがまだ使用されているかどうかを判断するために、「ルート」と呼ばれる一連のポインタから走査を開始します。ルートは、GCが到達可能なオブジェクトを識別するための出発点となります。主なルートには以下のものがあります。
- グローバル変数: プログラム全体からアクセス可能な変数。
- レジスタ: CPUのレジスタに格納されているポインタ。
- ゴルーチンのスタック: 各ゴルーチンの実行スタック上に存在するローカル変数や引数に含まれるポインタ。
これらのルートから辿れるオブジェクトは「生きている(live)」と判断され、マークされます。このコミットでは、特にゴルーチンのスタック走査に焦点を当てています。
スタックの構造とポインタの識別(Go 1.3以降の正確なGC)
Goのゴルーチンは、動的にサイズが変化するスタックを持っています。各関数呼び出しはスタック上に新しいスタックフレームを割り当て、そのフレームにはローカル変数、関数引数、戻りアドレスなどが格納されます。
Go 1.3以降、GoのGCは「完全に正確(fully precise)」になりました。これは、GCがスタック上の値がポインタであるか非ポインタであるかを正確に区別できることを意味します。これにより、GCが誤って整数値をポインタと解釈してしまい、不要なメモリを保持してしまう(メモリリーク)という問題を防ぐことができます。この精度は、Goコンパイラがスタックフレームのレイアウトに関するメタデータをGCに提供することで実現されています。
gentraceback
の役割
runtime.gentraceback
は、Goランタイムの内部関数であり、スタックトレースを生成する役割を担っています。これは、パニック発生時やruntime.Stack()
のような関数が呼び出された際に、現在の関数呼び出しの履歴(コールスタック)を辿って情報を収集するために使用されます。gentraceback
は、スタックフレームを一つずつ遡り、各フレームのプログラムカウンタ(PC)、スタックポインタ(SP)、およびその他の関連情報を取得します。この関数は非常に複雑であり、様々な種類のフレーム(Goの物理フレーム、インライン化されたフレーム、Cgoフレームなど)を処理する能力を持っています。
コミットメッセージでは、gentraceback
が依然としてパフォーマンスのボトルネックである可能性が示唆されており、スタック走査の並列化後もその非効率性が残っていることが指摘されています。
技術的詳細
このコミットの技術的な核心は、Goランタイムのガベージコレクションにおけるスタック走査のロジックを、シリアルなルート列挙フェーズから並列マークフェーズへと移行させた点にあります。
スタックスキャンがシリアルからパラレルになったことの意味
以前のGo GCでは、スタックの走査はGCの初期段階、つまり「マーク開始」のSTWフェーズで行われていました。このフェーズでは、すべてのゴルーチンの実行が一時停止され、GCは各ゴルーチンのスタックを順番に走査して、ヒープ上のオブジェクトへのポインタ(ルート)を識別していました。このシリアルな処理は、ゴルーチンの数やスタックの深さに比例してSTW時間が長くなる原因となっていました。
この変更により、スタック走査はGCの「並列マークフェーズ」に組み込まれました。並列マークフェーズでは、複数のGCヘルパーゴルーチンが同時に動作し、ヒープ上のオブジェクトをマークします。スタック走査をこの並列処理の一部とすることで、GCの一時停止時間を大幅に短縮し、全体的なスループットを向上させることが可能になります。これは、GCのパフォーマンス特性、特にレイテンシ(一時停止時間)を改善する上で重要なステップです。
Scanbuf
構造体の導入とその役割
このコミットでは、Scanbuf
という新しい構造体が導入されています。これは、スタック走査中に見つかったポインタやオブジェクトを一時的にバッファリングするためのものです。
typedef struct Scanbuf Scanbuf;
struct Scanbuf
{
struct {
PtrTarget *begin;
PtrTarget *end;
PtrTarget *pos;
} ptr;
struct {
Obj *begin;
Obj *end;
Obj *pos;
} obj;
Workbuf *wbuf;
Obj *wp;
uintptr nobj;
};
ptr
:PtrTarget
(ポインタとその型情報)を格納するバッファの開始、終了、現在の位置を管理します。obj
:Obj
(オブジェクトのベースアドレス、サイズ、型情報)を格納するバッファの開始、終了、現在の位置を管理します。wbuf
,wp
,nobj
: GCのワークバッファ(Workbuf
)と、その中のオブジェクト(Obj
)およびオブジェクト数(nobj
)へのポインタを保持します。これらは、Scanbuf
が満杯になった際に、バッファの内容を実際のGCワークバッファにフラッシュするために使用されます。
Scanbuf
の導入により、スタック走査中に見つかったポインタやオブジェクトを効率的に収集し、バッファが満杯になったときにまとめて処理できるようになります。これにより、個々のポインタやオブジェクトが見つかるたびにGCワークバッファに直接書き込むオーバーヘッドを削減し、並列処理の効率を高めることができます。
flushptrbuf
とflushobjbuf
の変更点
flushptrbuf
とflushobjbuf
は、それぞれポインタバッファとオブジェクトバッファの内容をGCのメインワークバッファにフラッシュする関数です。このコミットでは、これらの関数の引数が変更され、個々のポインタやワークバッファ関連の引数ではなく、新しく導入されたScanbuf
構造体へのポインタを受け取るようになりました。
// 変更前
static void flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj)
static void flushobjbuf(Obj *objbuf, Obj **objbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj)
// 変更後
static void flushptrbuf(Scanbuf *sbuf)
static void flushobjbuf(Scanbuf *sbuf)
この変更により、これらの関数はScanbuf
を通じて必要なすべての情報(バッファの開始/終了/現在位置、ワークバッファへのポインタなど)にアクセスできるようになり、コードの簡潔性と保守性が向上します。また、Scanbuf
がバッファリングの抽象化を提供することで、GCの内部ロジックがよりクリーンになります。
scanblock
におけるScanbuf
の利用
scanblock
関数は、GCのマークフェーズにおいて、メモリブロックを走査してポインタを識別し、到達可能なオブジェクトをマークする主要な関数です。このコミットでは、scanblock
内でScanbuf
が初期化され、ポインタやオブジェクトの収集に利用されるようになりました。
以前は、ptrbuf
, ptrbuf_end
, ptrbufpos
, objbuf
, objbuf_end
, objbufpos
といった個別のポインタやバッファ管理変数が使用されていましたが、これらがScanbuf sbuf;
に置き換えられました。
// 変更前 (一部抜粋)
PtrTarget *ptrbuf, *ptrbuf_end, *ptrbufpos;
Obj *objbuf, *objbuf_end, *objbufpos;
// ...
ptrbufpos = ptrbuf;
objbufpos = objbuf;
// 変更後 (一部抜粋)
Scanbuf sbuf;
// ...
sbuf.ptr.begin = sbuf.ptr.pos = &scanbuffers->ptrtarget[0];
sbuf.ptr.end = sbuf.ptr.begin + nelem(scanbuffers->ptrtarget);
sbuf.obj.begin = sbuf.obj.pos = &scanbuffers->obj[0];
sbuf.obj.end = sbuf.obj.begin + nelem(scanbuffers->obj);
sbuf.wbuf = wbuf;
sbuf.wp = wp;
sbuf.nobj = nobj;
これにより、scanblock
内のポインタおよびオブジェクトのバッファリングロジックがScanbuf
を通じて一元的に管理されるようになり、コードの可読性と保守性が向上します。また、flushptrbuf
やflushobjbuf
の呼び出しもflushptrbuf(&sbuf);
のように簡略化されています。
scaninterfacedata
とscanbitvector
の変更点
scaninterfacedata
はインターフェースのデータ部分を走査し、scanbitvector
はビットマップに基づいてメモリ領域を走査してポインタを識別する関数です。これらの関数も、Scanbuf
構造体へのポインタを引数として受け取るように変更されました。
// 変更前
static void scaninterfacedata(uintptr bits, byte *scanp, bool afterprologue)
static void scanbitvector(byte *scanp, BitVector *bv, bool afterprologue)
// 変更後
static void scaninterfacedata(uintptr bits, byte *scanp, bool afterprologue, Scanbuf *sbuf)
static void scanbitvector(byte *scanp, BitVector *bv, bool afterprologue, Scanbuf *sbuf)
これにより、これらの関数内で見つかったポインタやオブジェクトが、直接Scanbuf
を通じてバッファリングされるようになり、GCのワークバッファへの追加がより効率的に行われるようになります。
addstackroots
からscanstack
への変更とgentraceback
の利用
以前はaddstackroots
という関数がゴルーチンのスタックを走査してルートを追加していましたが、このコミットではaddstackroots
が削除され、代わりにscanstack
という新しい関数が導入されました。
scanstack
関数は、特定のゴルーチン(G* gp
)のスタックを走査し、gentraceback
関数を利用してスタックフレームを一つずつ辿ります。gentraceback
は、スタックフレームごとにscanframe
というコールバック関数を呼び出します。このscanframe
関数が、各スタックフレーム内のローカル変数や引数に含まれるポインタを識別し、それらをScanbuf
を通じてGCのワークバッファに追加します。
// 新しい scanstack 関数
static void scanstack(G* gp, void *scanbuf) {
// ...
runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, scanframe, scanbuf, false);
}
// 新しい scanframe 関数
static void scanframe(Stkframe *frame, void *arg) {
Scanbuf *sbuf;
sbuf = arg;
// ... スタックフレーム内のポインタを sbuf に追加するロジック ...
}
この変更は、スタック走査のロジックをgentraceback
のフレーム走査メカニズムと統合することで、より堅牢で効率的なスタック走査を実現しています。また、scanbuf
をgentraceback
のコールバック引数として渡すことで、スタック走査中に見つかったポインタを直接Scanbuf
に格納できるようになり、並列処理の恩恵を最大限に受けることができます。
ベンチマーク結果の分析
コミットメッセージには、issue 6482
ベンチマークのタイミング結果が示されています。
- Baseline: 変更前の基本的なパフォーマンス。
- Stack scan by frames, non-parallel: スタックをフレーム単位で走査するが、並列化されていない場合のパフォーマンス。この結果はBaselineと比較して大幅に悪化しており、フレーム単位の走査がシリアルに行われると非常にコストが高いことを示しています。
- Stack scan by frames, parallel: スタックをフレーム単位で走査し、並列化された場合のパフォーマンス。この結果は「non-parallel」の場合と比較して大幅に改善されており、並列化がスタック走査のコストを効果的に削減したことを示しています。ただし、Baselineと比較するとまだパフォーマンスの差があります。
gentraceback
が依然としてボトルネックであることの言及
ベンチマーク結果の分析に加えて、コミットメッセージでは「The remaining performance disparity is due to inefficiencies in gentraceback and its callees.」と明記されています。これは、スタック走査の並列化によってパフォーマンスは向上したものの、runtime.gentraceback
関数とその内部で呼び出される関数(例えばruntime.findfunc
など)の非効率性が、依然としてGCのパフォーマンスにおけるボトルネックとなっていることを示唆しています。これは、今後のGoランタイムの最適化において、gentraceback
のさらなる改善が重要であることを示しています。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は、src/pkg/runtime/mgc0.c
ファイルに集中しています。
-
ScanStackByFrames
のデフォルト値変更:--- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -19,7 +19,7 @@ enum { Debug = 0, DebugMark = 0, // run second pass to check mark CollectStats = 0, - ScanStackByFrames = 0, + ScanStackByFrames = 1, IgnorePreciseGC = 0,
ScanStackByFrames
が0
から1
に変更され、スタックをフレーム単位で走査するモードがデフォルトで有効になりました。 -
Scanbuf
構造体の追加:--- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -317,6 +319,24 @@ struct PtrTarget uintptr ti; }; +typedef struct Scanbuf Scanbuf; +struct Scanbuf +{ + struct { + PtrTarget *begin; + PtrTarget *end; + PtrTarget *pos; + } ptr; + struct { + Obj *begin; + Obj *end; + Obj *pos; + } obj; + Workbuf *wbuf; + Obj *wp; + uintptr nobj; +}; + typedef struct BufferList BufferList; struct BufferList {
スタック走査中に見つかったポインタやオブジェクトをバッファリングするための
Scanbuf
構造体が定義されました。 -
flushptrbuf
とflushobjbuf
の引数変更とScanbuf
の利用:--- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -350,7 +370,7 @@ static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj);\n // flushptrbuf // (find block start, mark and enqueue) static void -flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj) +flushptrbuf(Scanbuf *sbuf) { byte *p, *arena_start, *obj;\n uintptr size, *bitp, bits, shift, j, x, xbits, off, nobj, ti, n;\n PageID k;\n Obj *wp;\n Workbuf *wbuf;\n + PtrTarget *ptrbuf;\n PtrTarget *ptrbuf_end;\n - wp = *_wp;\n - wbuf = *_wbuf;\n - nobj = *_nobj;\n + wp = sbuf->wp;\n + wbuf = sbuf->wbuf;\n + nobj = sbuf->nobj;\n - ptrbuf_end = *ptrbufpos;\n - n = ptrbuf_end - ptrbuf;\n - *ptrbufpos = ptrbuf;\n + ptrbuf = sbuf->ptr.begin;\n + ptrbuf_end = sbuf->ptr.pos;\n + n = ptrbuf_end - sbuf->ptr.begin;\n + sbuf->ptr.pos = sbuf->ptr.begin;\n if(CollectStats) {\n \truntime·xadd64(&gcstats.ptr.sum, n);\n @@ -514,25 +536,27 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf\n }\n }\n - *_wp = wp;\n - *_wbuf = wbuf;\n - *_nobj = nobj;\n + sbuf->wp = wp;\n + sbuf->wbuf = wbuf;\n + sbuf->nobj = nobj;\n }\n static void\n -flushobjbuf(Obj *objbuf, Obj **objbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj) +flushobjbuf(Scanbuf *sbuf) { uintptr nobj, off;\n Obj *wp, obj;\n Workbuf *wbuf;\n + Obj *objbuf;\n Obj *objbuf_end;\n - wp = *_wp;\n - wbuf = *_wbuf;\n - nobj = *_nobj;\n + wp = sbuf->wp;\n + wbuf = sbuf->wbuf;\n + nobj = sbuf->nobj;\n - objbuf_end = *objbufpos;\n - *objbufpos = objbuf;\n + objbuf = sbuf->obj.begin;\n + objbuf_end = sbuf->obj.pos;\n + sbuf->obj.pos = sbuf->obj.begin;\n while(objbuf < objbuf_end) {\n \tobj = *objbuf++;\n @@ -570,9 +594,9 @@ flushobjbuf(Obj *objbuf, Obj **objbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_\n \twp = wbuf->obj + nobj;\n }\n - *_wp = wp;\n - *_wbuf = wbuf;\n - *_nobj = nobj;\n + sbuf->wp = wp;\n + sbuf->wbuf = wbuf;\n + sbuf->nobj = nobj;\n }\n ``` `flushptrbuf`と`flushobjbuf`の引数が`Scanbuf *sbuf`に変更され、内部で`sbuf`のメンバーを通じてバッファ情報にアクセスするようになりました。
-
scanblock
でのScanbuf
の初期化と利用:--- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -666,8 +693,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n Slice *sliceptr;\n Frame *stack_ptr, stack_top, stack[GC_STACK_CAPACITY+4];\n BufferList *scanbuffers;\n - PtrTarget *ptrbuf, *ptrbuf_end, *ptrbufpos;\n - Obj *objbuf, *objbuf_end, *objbufpos;\n + Scanbuf sbuf;\n Eface *eface;\n Iface *iface;\n Hchan *chan;\n @@ -681,21 +707,22 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n arena_used = runtime·mheap.arena_used;\n stack_ptr = stack+nelem(stack)-1;\n - \n +\n precise_type = false;\n nominal_size = 0;\n - // Allocate ptrbuf\n - {\n - scanbuffers = &bufferList[m->helpgc];\n - ptrbuf = &scanbuffers->ptrtarget[0];\n - ptrbuf_end = &scanbuffers->ptrtarget[0] + nelem(scanbuffers->ptrtarget);\n - objbuf = &scanbuffers->obj[0];\n - objbuf_end = &scanbuffers->obj[0] + nelem(scanbuffers->obj);\n - }\n + // Initialize sbuf\n + scanbuffers = &bufferList[m->helpgc];\n - ptrbufpos = ptrbuf;\n - objbufpos = objbuf;\n + sbuf.ptr.begin = sbuf.ptr.pos = &scanbuffers->ptrtarget[0];\n + sbuf.ptr.end = sbuf.ptr.begin + nelem(scanbuffers->ptrtarget);\n +\n + sbuf.obj.begin = sbuf.obj.pos = &scanbuffers->obj[0];\n + sbuf.obj.end = sbuf.obj.begin + nelem(scanbuffers->obj);\n +\n + sbuf.wbuf = wbuf;\n + sbuf.wp = wp;\n + sbuf.nobj = nobj;\n // (Silence the compiler)\n chan = nil;\n @@ -713,7 +740,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n \tif(CollectStats) {\n \t\truntime·xadd64(&gcstats.nbytes, n);\n - \t\t\truntime·xadd64(&gcstats.obj.sum, nobj);\n + \t\t\truntime·xadd64(&gcstats.obj.sum, sbuf.nobj);\n \t\t\truntime·xadd64(&gcstats.obj.cnt, 1);\n \t}\n @@ -839,9 +866,9 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n \t// eface->type\n \tt = eface->type;\n \tif((void*)t >= arena_start && (void*)t < arena_used) {\n - \t\t*ptrbufpos++ = (PtrTarget){t, 0};\n - \t\tif(ptrbufpos == ptrbuf_end)\n - \t\t\tflushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj);\n + \t\t*sbuf.ptr.pos++ = (PtrTarget){t, 0};\n + \t\tif(sbuf.ptr.pos == sbuf.ptr.end)\n + \t\t\tflushptrbuf(&sbuf);\n \t}\n \t// eface->data\n @@ -868,9 +895,9 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n \t\n \t// iface->tab\n \tif((void*)iface->tab >= arena_start && (void*)iface->tab < arena_used) {\n - \t\t*ptrbufpos++ = (PtrTarget){iface->tab, (uintptr)itabtype->gc};\n - \t\tif(ptrbufpos == ptrbuf_end)\n - \t\t\tflushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj);\n + \t\t*sbuf.ptr.pos++ = (PtrTarget){iface->tab, (uintptr)itabtype->gc};\n + \t\tif(sbuf.ptr.pos == sbuf.ptr.end)\n + \t\t\tflushptrbuf(&sbuf);\n \t}\n \t// iface->data\n @@ -895,9 +922,9 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n \t\tobj = *(byte**)stack_top.b;\n \t\tstack_top.b += PtrSize;\n \t\tif(obj >= arena_start && obj < arena_used) {\n - \t\t\t*ptrbufpos++ = (PtrTarget){obj, 0};\n - \t\t\tif(ptrbufpos == ptrbuf_end)\n - \t\t\t\tflushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj);\n + \t\t\t*sbuf.ptr.pos++ = (PtrTarget){obj, 0};\n + \t\t\tif(sbuf.ptr.pos == sbuf.ptr.end)\n + \t\t\t\tflushptrbuf(&sbuf);\n \t}\n \t}\n \tgoto next_block;\n @@ -926,7 +953,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n \t\t\tif(*(byte**)i != nil) {\n \t\t\t\t// Found a value that may be a pointer.\n \t\t\t\t// Do a rescan of the entire block.\n - \t\t\t\tenqueue((Obj){b, n, 0}, &wbuf, &wp, &nobj);\n + \t\t\t\tenqueue((Obj){b, n, 0}, &sbuf.wbuf, &sbuf.wp, &sbuf.nobj);\n \t\t\t\tif(CollectStats) {\n \t\t\t\t\truntime·xadd64(&gcstats.rescan, 1);\n \t\t\t\t\truntime·xadd64(&gcstats.rescanbytes, n);\n @@ -972,9 +999,9 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n \t\tobjti = pc[3];\n \t\tpc += 4;\n - \t\t*objbufpos++ = (Obj){obj, size, objti};\n - \t\tif(objbufpos == objbuf_end)\n - \t\t\tflushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);\n + \t\t*sbuf.obj.pos++ = (Obj){obj, size, objti};\n + \t\tif(sbuf.obj.pos == sbuf.obj.end)\n + \t\t\tflushobjbuf(&sbuf);\n \t\tcontinue;\n \tcase GC_CHAN_PTR:\n @@ -1007,10 +1034,10 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n \t\t\t\t\t// in-use part of the circular buffer is scanned.\n \t\t\t\t\t// (Channel routines zero the unused part, so the current\n \t\t\t\t\t// code does not lead to leaks, it\'s just a little inefficient.)\n - \t\t\t\t\t*objbufpos++ = (Obj){(byte*)chan+runtime·Hchansize, chancap*chantype->elem->size,\n + \t\t\t\t\t*sbuf.obj.pos++ = (Obj){(byte*)chan+runtime·Hchansize, chancap*chantype->elem->size,\n \t\t\t\t\t\t(uintptr)chantype->elem->gc | PRECISE | LOOP};\n - \t\t\t\t\tif(objbufpos == objbuf_end)\n - \t\t\t\t\t\tflushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);\n + \t\t\t\t\tif(sbuf.obj.pos == sbuf.obj.end)\n + \t\t\t\t\t\tflushobjbuf(&sbuf);\n \t\t\t\t}\n \t\t\t}\n \t\tif(chan_ret == nil)\n @@ -1018,15 +1045,20 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n \t\tpc = chan_ret;\n \t\tcontinue;\n + \t\tcase GC_G_PTR:\n + \t\t\tobj = (void*)stack_top.b;\n + \t\t\tscanstack(obj, &sbuf);\n + \t\t\tgoto next_block;\n +\n \tdefault:\n \t\truntime·throw(\"scanblock: invalid GC instruction\");\n \t\treturn;\n \t}\n \tif(obj >= arena_start && obj < arena_used) {\n - \t\t*ptrbufpos++ = (PtrTarget){obj, objti};\n - \t\tif(ptrbufpos == ptrbuf_end)\n - \t\t\tflushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj);\n + \t\t*sbuf.ptr.pos++ = (PtrTarget){obj, objti};\n + \t\tif(sbuf.ptr.pos == sbuf.ptr.end)\n + \t\t\tflushptrbuf(&sbuf);\n \t}\n }\n @@ -1034,34 +1066,32 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)\n \t// Done scanning [b, b+n). Prepare for the next iteration of\n \t// the loop by setting b, n, ti to the parameters for the next block.\n - \tif(nobj == 0) {\n - \t\tflushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj);\n - \t\tflushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);\n + \tif(sbuf.nobj == 0) {\n + \t\tflushptrbuf(&sbuf);\n + \t\tflushobjbuf(&sbuf);\n - \t\tif(nobj == 0) {\n + \t\tif(sbuf.nobj == 0) {\n \t\t\tif(!keepworking) {\n - \t\t\t\tif(wbuf)\n - \t\t\t\t\tputempty(wbuf);\n - \t\t\t\tgoto endscan;\n + \t\t\t\tif(sbuf.wbuf)\n + \t\t\t\t\tputempty(sbuf.wbuf);\n + \t\t\t\treturn;\n \t\t\t}\n \t\t\t// Emptied our buffer: refill.\n - \t\t\twbuf = getfull(wbuf);\n - \t\t\tif(wbuf == nil)\n - \t\t\t\tgoto endscan;\n - \t\t\tnobj = wbuf->nobj;\n - \t\t\twp = wbuf->obj + wbuf->nobj;\n + \t\t\tsbuf.wbuf = getfull(sbuf.wbuf);\n + \t\t\tif(sbuf.wbuf == nil)\n + \t\t\t\treturn;\n + \t\t\tsbuf.nobj = sbuf.wbuf->nobj;\n + \t\t\tsbuf.wp = sbuf.wbuf->obj + sbuf.wbuf->nobj;\n \t\t}\n \t}\n \t// Fetch b from the work buffer.\n - \t--wp;\n - \tb = wp->p;\n - \tn = wp->n;\n - \tti = wp->ti;\n - \tnobj--;\n + \t--sbuf.wp;\n + \tb = sbuf.wp->p;\n + \tn = sbuf.wp->n;\n + \tti = sbuf.wp->ti;\n + \tsbuf.nobj--;\n }\n -\n -endscan:;\n }\n ``` `scanblock`関数内で、個別のバッファ管理変数が`Scanbuf sbuf;`に置き換えられ、`sbuf`のメンバーを通じてバッファの初期化、ポインタ/オブジェクトの追加、および`flushptrbuf`/`flushobjbuf`の呼び出しが行われるようになりました。
-
scaninterfacedata
とscanbitvector
へのScanbuf
引数の追加:--- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -1340,7 +1370,7 @@ struct BitVector // Scans an interface data value when the interface type indicates // that it is a pointer.\n static void\n -scaninterfacedata(uintptr bits, byte *scanp, bool afterprologue) +scaninterfacedata(uintptr bits, byte *scanp, bool afterprologue, Scanbuf *sbuf) { Itab *tab;\n Type *type;\n @@ -1356,12 +1386,14 @@ scaninterfacedata(uintptr bits, byte *scanp, bool afterprologue)\n \t\t\t\treturn;\n \t}\n }\n - addroot((Obj){scanp+PtrSize, PtrSize, 0});\n + *sbuf->obj.pos++ = (Obj){scanp+PtrSize, PtrSize, 0};\n + if(sbuf->obj.pos == sbuf->obj.end)\n + flushobjbuf(sbuf);\n }\n // Starting from scanp, scans words corresponding to set bits.\n static void\n -scanbitvector(byte *scanp, BitVector *bv, bool afterprologue) +scanbitvector(byte *scanp, BitVector *bv, bool afterprologue, Scanbuf *sbuf) { uintptr word, bits;\n uint32 *wordp;\n @@ -1378,75 +1410,126 @@ scanbitvector(byte *scanp, BitVector *bv, bool afterprologue)\n for(; i > 0; i--) {\n \tbits = word & 3;\n \tif(bits != BitsNoPointer && *(void**)scanp != nil)\n - \t\tif(bits == BitsPointer)\n - \t\t\taddroot((Obj){scanp, PtrSize, 0});\n - \t\telse\n - \t\t\tscaninterfacedata(bits, scanp, afterprologue);\n + \t\tif(bits == BitsPointer) {\n + \t\t\t*sbuf->obj.pos++ = (Obj){scanp, PtrSize, 0};\n + \t\t\tif(sbuf->obj.pos == sbuf->obj.end)\n + \t\t\t\tflushobjbuf(sbuf);\n + \t} else\n + \t\tscaninterfacedata(bits, scanp, afterprologue, sbuf);\n \tword >>= BitsPerPointer;\n \tscanp += PtrSize;\n }\n }\n }\n -// Scan a stack frame: local variables and function arguments/results.\n static void\n -addframeroots(Stkframe *frame, void*)\n +addstackroots(G *gp)\n +{\n +\tM *mp;\n +\tint32 n;\n +\tStktop *stk;\n +\tuintptr sp, guard;\n +\tvoid *base;\n +\tuintptr size;\n +\n +\tif(gp == g)\n +\t\truntime·throw(\"can\'t scan our own stack\");\n +\tif((mp = gp->m) != nil && mp->helpgc)\n +\t\truntime·throw(\"can\'t scan gchelper stack\");\n +\tif(gp->syscallstack != (uintptr)nil) {\n +\t\t// Scanning another goroutine that is about to enter or might\n +\t\t// have just exited a system call. It may be executing code such\n +\t\t// as schedlock and may have needed to start a new stack segment.\n +\t\t// Use the stack segment and stack pointer at the time of\n +\t\t// the system call instead, since that won\'t change underfoot.\n +\t\tsp = gp->syscallsp;\n +\t\tstk = (Stktop*)gp->syscallstack;\n +\t\tguard = gp->syscallguard;\n +\t} else {\n +\t\t// Scanning another goroutine\'s stack.\n +\t\t// The goroutine is usually asleep (the world is stopped).\n +\t\tsp = gp->sched.sp;\n +\t\tstk = (Stktop*)gp->stackbase;\n +\t\tguard = gp->stackguard;\n +\t\t// For function about to start, context argument is a root too.\n +\t\tif(gp->sched.ctxt != 0 && runtime·mlookup(gp->sched.ctxt, &base, &size, nil))\n +\t\t\taddroot((Obj){base, size, 0});\n +\t}\n +\tif(ScanStackByFrames) {\n +\t\tUSED(sp);\n +\t\tUSED(stk);\n +\t\tUSED(guard);\n +\t\taddroot((Obj){(byte*)gp, PtrSize, (uintptr)gptrProg});\n +\t} else {\n +\t\tn = 0;\n +\t\twhile(stk) {\n +\t\t\tif(sp < guard-StackGuard || (uintptr)stk < sp) {\n +\t\t\t\truntime·printf(\"scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\\n\", gp->goid, n, sp, guard-StackGuard, stk);\n +\t\t\t\truntime·throw(\"scanstack\");\n +\t\t\t}\n +\t\t\taddroot((Obj){(byte*)sp, (uintptr)stk - sp, (uintptr)defaultProg | PRECISE | LOOP});\n +\t\t\tsp = stk->gobuf.sp;\n +\t\t\tguard = stk->stackguard;\n +\t\t\tstk = (Stktop*)stk->stackbase;\n +\t\t\tn++;\n +\t\t}\n +\t}\n +}\n +\n +static void\n +scanframe(Stkframe *frame, void *arg)\n {\n -\tFunc *f;\n \tBitVector *args, *locals;\n +\tScanbuf *sbuf;\n \tuintptr size;\n \tbool afterprologue;\n -\tf = frame->fn;\n -\n +\tsbuf = arg;\n \t// Scan local variables if stack frame has been allocated.\n \t// Use pointer information if known.\n \tafterprologue = (frame->varp > (byte*)frame->sp);\n \tif(afterprologue) {\n -\t\tlocals = runtime·funcdata(f, FUNCDATA_GCLocals);\n +\t\tlocals = runtime·funcdata(frame->fn, FUNCDATA_GCLocals);\n \t\tif(locals == nil) {\n \t\t\t// No locals information, scan everything.\n \t\t\tsize = frame->varp - (byte*)frame->sp;\n - \t\taddroot((Obj){frame->varp - size, size, 0});\n + \t\t*sbuf->obj.pos++ = (Obj){frame->varp - size, size, 0};\n + \t\tif(sbuf->obj.pos == sbuf->obj.end)\n + \t\t\tflushobjbuf(sbuf);\n \t\t} else if(locals->n < 0) {\n \t\t\t// Locals size information, scan just the\n \t\t\t// locals.\n \t\t\tsize = -locals->n;\n - \t\taddroot((Obj){frame->varp - size, size, 0});\n + \t\t*sbuf->obj.pos++ = (Obj){frame->varp - size, size, 0};\n + \t\tif(sbuf->obj.pos == sbuf->obj.end)\n + \t\t\tflushobjbuf(sbuf);\n \t\t} else if(locals->n > 0) {\n \t\t\t// Locals bitmap information, scan just the\n \t\t\t// pointers in locals.\n \t\t\tsize = (locals->n*PtrSize) / BitsPerPointer;\n - \t\tscanbitvector(frame->varp - size, locals, afterprologue);\n + \t\tscanbitvector(frame->varp - size, locals, afterprologue, sbuf);\n \t\t}\n \t}\n \t// Scan arguments.\n \t// Use pointer information if known.\n - args = runtime·funcdata(f, FUNCDATA_GCArgs);\n + args = runtime·funcdata(frame->fn, FUNCDATA_GCArgs);\n \tif(args != nil && args->n > 0)\n - \tscanbitvector(frame->argp, args, false);\n - else\n - \taddroot((Obj){frame->argp, frame->arglen, 0});\n + \tscanbitvector(frame->argp, args, false, sbuf);\n + else {\n + \t*sbuf->obj.pos++ = (Obj){frame->argp, frame->arglen, 0};\n + \tif(sbuf->obj.pos == sbuf.obj.end)\n + \t\tflushobjbuf(sbuf);\n + }\n }\n static void\n -addstackroots(G *gp)\n +scanstack(G* gp, void *scanbuf)\n {\n -\tM *mp;\n -\tint32 n;\n -\tStktop *stk;\n -\tuintptr sp, guard, pc, lr;\n -\tvoid *base;\n -\tuintptr size;\n +\tuintptr pc;\n +\tuintptr sp;\n +\tuintptr lr;\n -\tstk = (Stktop*)gp->stackbase;\n -\tguard = gp->stackguard;\n -\n -\tif(gp == g)\n -\t\truntime·throw(\"can\'t scan our own stack\");\n -\tif((mp = gp->m) != nil && mp->helpgc)\n -\t\truntime·throw(\"can\'t scan gchelper stack\");\n \tif(gp->syscallstack != (uintptr)nil) {\n \t\t// Scanning another goroutine that is about to enter or might\n \t\t// have just exited a system call. It may be executing code such\n @@ -1456,39 +1539,14 @@ addstackroots(G *gp)\n \t\tsp = gp->syscallsp;\n \t\tpc = gp->syscallpc;\n \t\tlr = 0;\n -\t\tstk = (Stktop*)gp->syscallstack;\n -\t\tguard = gp->syscallguard;\n \t} else {\n \t\t// Scanning another goroutine\'s stack.\n \t\t// The goroutine is usually asleep (the world is stopped).\n \t\tsp = gp->sched.sp;\n \t\tpc = gp->sched.pc;\n \t\tlr = gp->sched.lr;\n -\n -\t\t// For function about to start, context argument is a root too.\n -\t\tif(gp->sched.ctxt != 0 && runtime·mlookup(gp->sched.ctxt, &base, &size, nil))\n -\t\t\taddroot((Obj){base, size, 0});\n -\t}\n -\tif(ScanStackByFrames) {\n -\t\tUSED(stk);\n -\t\tUSED(guard);\n -\t\truntime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, addframeroots, nil, false);\n -\t} else {\n -\t\tUSED(lr);\n -\t\tUSED(pc);\n -\t\tn = 0;\n -\t\twhile(stk) {\n -\t\t\tif(sp < guard-StackGuard || (uintptr)stk < sp) {\n -\t\t\t\truntime·printf(\"scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\\n\", gp->goid, n, sp, guard-StackGuard, stk);\n -\t\t\t\truntime·throw(\"scanstack\");\n -\t\t\t}\n -\t\t\taddroot((Obj){(byte*)sp, (uintptr)stk - sp, (uintptr)defaultProg | PRECISE | LOOP});\n -\t\t\tsp = stk->gobuf.sp;\n -\t\t\tguard = stk->stackguard;\n -\t\t\tstk = (Stktop*)stk->stackbase;\n -\t\t\tn++;\n -\t\t}\n \t}\n +\truntime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, scanframe, scanbuf, false);\n }\n ``` `scaninterfacedata`と`scanbitvector`の関数シグネチャに`Scanbuf *sbuf`が追加され、内部で`sbuf`を通じてオブジェクトが追加されるようになりました。また、`addstackroots`関数が削除され、`scanstack`関数が新しく追加されました。`scanstack`は`gentraceback`を呼び出し、コールバック関数として`scanframe`を渡すようになりました。`scanframe`も`Scanbuf *sbuf`を引数として受け取るように変更されました。
-
GC_G_PTR
の追加:--- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -176,6 +177,7 @@ static struct {\n enum {\n GC_DEFAULT_PTR = GC_NUM_INSTR,\n GC_CHAN,\n + GC_G_PTR,\n GC_NUM_INSTR2\n };
GC_G_PTR
という新しいGC命令が追加されました。これは、G*
(ゴルーチン構造体へのポインタ)を処理するためのものです。
コアとなるコードの解説
ScanStackByFrames
のデフォルト値変更
ScanStackByFrames
が1
に設定されたことで、Goランタイムはデフォルトでスタックをフレーム単位で走査するようになりました。これは、より正確なGCを実現するために重要です。以前は、スタック全体を保守的に走査する(ポインタでない可能性のある値もポインタとして扱う)ことがありましたが、フレーム単位の走査により、ポインタと非ポインタをより厳密に区別できるようになります。これにより、不要なオブジェクトの保持を防ぎ、メモリ使用効率を向上させます。
Scanbuf
構造体の導入
Scanbuf
は、スタック走査中に見つかったポインタやオブジェクトを一時的に保持するためのバッファです。GCの並列マークフェーズでは、複数のヘルパーゴルーチンが同時に動作するため、共有リソースへのアクセスを効率的に管理する必要があります。Scanbuf
は、各ヘルパーゴルーチンが自身のローカルバッファとして使用することで、ロックの競合を減らし、並列処理のスループットを向上させます。バッファが満杯になったときにまとめてGCのメインワークバッファにフラッシュすることで、細粒度なロックや同期のオーバーヘッドを削減します。
flushptrbuf
とflushobjbuf
の引数変更
これらの関数がScanbuf *sbuf
を引数として受け取るようになったことで、GCのバッファフラッシュロジックがよりモジュール化されました。以前は、複数の引数を個別に渡す必要がありましたが、Scanbuf
にすべての関連情報がカプセル化されたことで、関数の呼び出しが簡潔になり、コードの保守性が向上しました。また、Scanbuf
内部のポインタを直接操作することで、効率的なバッファ管理が可能になります。
scanblock
でのScanbuf
の利用
scanblock
関数はGCのマークフェーズの中心的な部分であり、ここでScanbuf
が導入されたことは、GCの並列処理戦略において重要な意味を持ちます。scanblock
内でScanbuf
を初期化し、ポインタやオブジェクトの収集に利用することで、GCヘルパーゴルーチンが独立してバッファリングを行い、必要に応じてまとめてフラッシュできるようになります。これにより、GCの並列性が向上し、全体的なマークフェーズの実行時間が短縮されます。
scaninterfacedata
とscanbitvector
へのScanbuf
引数の追加
これらの関数は、特定のデータ構造(インターフェースやビットマップで表現されたポインタ情報)を走査してポインタを識別します。Scanbuf
を引数として受け取るようになったことで、これらの関数内で見つかったポインタも直接Scanbuf
に格納されるようになります。これにより、GCのワークバッファへのポインタ追加パスが統一され、効率的なバッファリングの恩恵をこれらの低レベルな走査関数でも享受できるようになります。
addstackroots
からscanstack
への変更とgentraceback
の利用
この変更は、スタック走査のアーキテクチャにおける最も重要な変更点の一つです。
addstackroots
の削除: 以前のaddstackroots
は、スタックを直接走査するロジックを含んでいましたが、これは複雑で保守が困難でした。scanstack
の導入とgentraceback
の活用: 新しいscanstack
関数は、Goランタイムの既存のスタックトレース生成メカニズムであるgentraceback
を再利用します。gentraceback
は、スタックフレームを正確に辿るための堅牢なロジックを既に持っています。scanstack
はgentraceback
を呼び出し、各スタックフレームが見つかるたびにscanframe
コールバック関数を呼び出すように設定します。scanframe
とScanbuf
:scanframe
は、gentraceback
から渡されたスタックフレーム情報と、scanstack
から渡されたScanbuf
へのポインタを受け取ります。scanframe
の内部では、runtime·funcdata
を使用して関数のローカル変数や引数のGCメタデータ(ポインタのビットマップなど)を取得し、それに基づいてスタックフレーム内のポインタを正確に識別します。識別されたポインタは、Scanbuf
を通じてGCのワークバッファに効率的に追加されます。
このアプローチにより、スタック走査のロジックが簡素化され、gentraceback
の既存の堅牢性を活用できるようになりました。また、Scanbuf
を介してポインタをバッファリングすることで、並列処理の効率がさらに向上します。
GC_G_PTR
の追加
GC_G_PTR
は、G*
(ゴルーチン構造体へのポインタ)をGCの対象として扱うための新しい命令です。scanblock
内でGC_G_PTR
命令が処理されると、そのポインタが指すゴルーチンのスタックがscanstack
関数によって走査されるようになります。これは、GCがゴルーチン自体をルートとして認識し、そのスタックを辿ってヒープ上のオブジェクトをマークするためのメカニズムを提供します。
関連リンク
- Go CL 17410043: https://golang.org/cl/17410043
参考にした情報源リンク
- Go's garbage collector (GC) is a concurrent, tri-color mark-and-sweep collector designed to minimize pause times. Stack scanning is a crucial part of its operation, as goroutine stacks are considered a primary source of "root" pointers that identify reachable objects in the heap. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEbKkiruoSxMv6MXMcP5BR3Oo_lFOc-Gf9wzOMlbSOQneGLNcziWEC_2BtNvXgGNA-SDlZvubilLMCk80NKlp1Va1Vl_-5bN-SGcLNYp6qMPsALm76uzYDxtGSzZzkur89Ei5dqXZq2DIQrz0iaSwHIQKR1E3B3Z9BsPctCmXXKIQcscV0b_kDQtL6UL7oIkyILwhSxqGKB0kxd0UIPmMoJn-eqBIYDuiBx19bLnVHkW-WzB_De5C6E5QZqHOGzd7u78kn12bg=
runtime.gentraceback
is an internal, complex function within the Go runtime responsible for generating stack traces. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHDzykDRBwn0x7NmYjdfW96uYeN9uFUToMquGxH-t20wylFXLnhZns8dkFZtJb8AeJoAgAD0DImLJnZww3mMo4W4woxzVBDbBH4Zaobj8esNivvWDMR0_Q5_Uf4U_5qoJkgzTCD- Go's garbage collector (GC) is a concurrent, tri-color mark-and-sweep collector designed to minimize pause times. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHKldDJRZQHQZAAFj_2GPYVWtNsLSzRrsHXAL1Jmpy2pf0AhvXNt4nolQZYPqnjrGtoEXyHt2nrlv65A2rlI50eKCf7TzDdB11QhjAXvrZ731Z7HJvrngD9regwwI-dslZWAdUX0bQ5_sMv1fgAN0QrchlkR_ITc_8QQdG32GJ_ABxgQwoPPZJkWA4yQiTLawCjpU3svQipBQ==
- Go's garbage collector (GC) is a concurrent, tri-color mark-and-sweep collector designed to minimize pause times. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE_7vIihdvSi6Wu8W1WQ73OMLi0Q1OjzI4A1P0RqiPrdLNjkQZEC_2BtNvXgGNA-SDlZvubilLMCk80NKlp1Va1Vl_-5bN-SGcLNYp6qMPsALm76uzYDxtGSzZzkur89Ei5dqXZq2DIQrz0iaSwHIQKR1E3B3Z9BsPctCmXXKIQcscV0b_kDQtL6UL7oIkyILwhSxqGKB0kxd0UIPmMoJn-eqBIYDuiBx19bLnVHkW-WzB_De5C6E5QZqHOGzd7u78kn12bg=
- Go's garbage collector (GC) is a concurrent, tri-color mark-and-sweep collector designed to minimize pause times. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGHFiU522JRiiPD2VGT0oRcYo37uUbm4Q_pkS7stDZGCt9ydg337-TmtYR2RgyvSiLmh4v5IDY7E6vMXMFdzxWrMhTx7dcwFtBZL0Dbhzz-wK_XKTNSD5yC589bDJYwlL2dlxG_UkNMq6HX3-BpocOOZefBHyTmc0G3-EjQtdnfd8wu3pHm
- Go's garbage collector (GC) is a concurrent, tri-color mark-and-sweep collector designed to minimize pause times. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEWdlHt3_2sHyvqLTcT7yb5f3nvrTOjyv0cOQeCwgzXJpR9CWbM1ao0ruaGEr6nm6IL6ZybRzO0M99SPf4bdb4HgIl3zTaofOhIPabxM8Y0d08fKzbtbipV5krqWxRTdYcm6zn29RrQqHa2Wj5VBp77Ja048lZGTc3UAWKdrLeMRKr0iSn7pmXMHxa8m5rNSA==
runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF7YT1b25JNKILeB8g7hZGrcdZVbsEqywK51ztxfpCr37uvBL2HoWM8AEnEjUBOt6sIj0WIDpEUrKLS2VITN0B8Y2PKGIukAgMJj6gllMryvLXdm4HyAbaFvijRZiUaYnHFZsm10k4Xo0dKFHj_pguQ4yCP_ZhvlmsY2ryzAqDxUsvGkfKiAjJAug==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEPgIDf3jahvv_FeU5wDWa8QOfQqCSF5gDs9rzHP4p0wDNEJNCzmUJwqLCX02cs3ynJwOsu-YPUpUoobwDcnJHR1WagnsiAgpF82pJmKagT5YbPxvC6runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGkc3Aw5NnTxE7uTXpGYuVNuJ8AALGljlHxD81unusoQ_2mW0OMJD4d0kCzKEQModHMb_sbszsWC-M_3qDxPdkNnYNdK__QXga5kLFmj9iWVoM55Omfvruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQRb3hejswf0Qmj26sulM6Az9Q0HtKWL11vfRZihlvtfkWeyus4Q_Y9zWNVswVmAy4uWp_2CQJ7NK8LNnfTAQ26yIYQlpjdEoeXm4iCjr9rJuAt6ZfCmpD7sSGq-VukRe02zWYKiP6Lng5BF35YRQ=runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFPhGCUZX_2mfReg30cqkg7yhoVOVYQmwnEjaWRBrOhknIeSO2eb0ogyKy_f-DnlnkxE1ivIZlfIJqI0i9etwj_YIS59OYrSq02vMST1fCXlQRVbWuU0wNLuunKHABBtrwLyzyxYQ6GmBC0_r4-IPaxWsfQyM4OxwFpBAAJac29sg==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJkvGPx7nB_hY-NTl60WtBVKdGUOm3wyN8iXJiQiTWQVXUonNhQBFrI2eYTi5loLKb1E9xEIAQgGTOAIh0HO1Qn3cAFoFh5a5A7GNY0RrfK9GACKWqd7aD5pi4ciaRVPuFzw==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH3vQu8w89d0KIZJfuLeVTfOjchnoPIh6xS6vMEmuiTzhQ8K4-mHoVJacGBNE9USOzkK22Dtg5wZsXupnUZ-kWX_AIBAmCgZgMi9WYdaXVHOpUEuEVr-bwaVFzcne5vPUeE6YvSADrh7jU4GslpdyP_oSU2l2LGruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHDzykDRBwn0x7NmYjdfW96uYeN9uFUToMquGxH-t20wylFXLnhZns8dkFZtJb8AeJoAgAD0DImLJnZww3mMo4W4woxzVBDbBH4Zaobj8esNivvWDMR0_Q5_Uf4U_5qoJkgzTCDruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQRb3hejswf0Qmj26sulM6Az9Q0HtKWL11vfRZihlvtfkWeyus4Q_Y9zNVMswVmAy4uWp_2CQJ7NK8LNnfTAQ26yIYQlpjdEoeXm4iCjr9rJuAt6ZfCmpD7sSGq-VukRe02zWYKiP6Lng5BF35YRQ=runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFPhGCUZX_2mfReg30cqkg7yhoVOVYQmwnEjaWRBrOhknIeSO2eb0ogyKy_f-DnlnkxE1ivIZlfIJqI0i9etwj_YIS59OYrSq02vMST1fCXlQRVbWuU0wNLuunKHABBtrwLyzyxYQ6GmBC0_r4-IPaxWsfQyM4OxwFpBAAJac29sg==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJkvGPx7nB_hY-NTl60WtBVKdGUOm3wyN8iXJiQiTWQVXUonNhQBFrI2eYTi5loLKb1E9xEIAQgGTOAIh0HO1Qn3cAFoFh5a5A7GNY0RrfK9GACKWqd7aD5pi4ciaRVPuFzw==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH3vQu8w89d0KIZJfuLeVTfOjchnoPIh6xS6vMEmuiTzhQ8K4-mHoVJacGBNE9USOzkK22Dtg5wZsXupnUZ-kWX_AIBAmCgZgMi9WYdaXVHOpUEuEVr-bwaVFzcne5vPUeE6YvSADrh7jU4GslpdyP_oSU2l2LGruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEPgIDf3jahvv_FeU5wDWa8QOfQqCSF5gDs9rzHP4p0wDNEJNCzmUJwqLCX02cs3ynJwOsu-YPUpUoobwDcnJHR1WagnsiAgpF82pJmKagT5YbPxvC6runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGkc3Aw5NnTxE7uTXpGYuVNuJ8AALGljlHxD81unusoQ_2mW0OMJD4d0kCzKEQModHMb_sbszsWC-M_3qDxPdkNnYNdK__QXga5kLFmj9iWVoM55Omfvruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQRb3hejswf0Qmj26sulM6Az9Q0HtKWL11vfRZihlvtfkWeyus4Q_Y9zNVMswVmAy4uWp_2CQJ7NK8LNnfTAQ26yIYQlpjdEoeXm4iCjr9rJuAt6ZfCmpD7sSGq-VukRe02zWYKiP6Lng5BF35YRQ=runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFPhGCUZX_2mfReg30cqkg7yhoVOVYQmwnEjaWRBrOhknIeSO2eb0ogyKy_f-DnlnkxE1ivIZlfIJqI0i9etwj_YIS59OYrSq02vMST1fCXlQRVbWuU0wNLuunKHABBtrwLyzyxYQ6GmBC0_r4-IPaxWsfQyM4OxwFpBAAJac29sg==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJkvGPx7nB_hY-NTl60WtBVKdGUOm3wyN8iXJiQiTWQVXUonNhQBFrI2eYTi5loLKb1E9xEIAQgGTOAIh0HO1Qn3cAFoFh5a5A7GNY0RrfK9GACKWqd7aD5pi4ciaRVPuFzw==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH3vQu8w89d0KIZJfuLeVTfOjchnoPIh6xS6vMEmuiTzhQ8K4-mHoVJacGBNE9USOzkK22Dtg5wZsXupnUZ-kWX_AIBAmCgZgMi9WYdaXVHOpUEuEVr-bwaVFzcne5vPUeE6YvSADrh7jU4GslpdyP_oSU2l2LGruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEPgIDf3jahvv_FeU5wDWa8QOfQqCSF5gDs9rzHP4p0wDNEJNCzmUJwqLCX02cs3ynJwOsu-YPUpUoobwDcnJHR1WagnsiAgpF82pJmKagT5YbPxvC6runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGkc3Aw5NnTxE7uTXpGYuVNuJ8AALGljlHxD81unusoQ_2mW0OMJD4d0kCzKEQModHMb_sbszsWC-M_3qDxPdkNnYNdK__QXga5kLFmj9iWVoM55Omfvruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQRb3hejswf0Qmj26sulM6Az9Q0HtKWL11vfRZihlvtfkWeyus4Q_Y9zNVMswVmAy4uWp_2CQJ7NK8LNnfTAQ26yIYQlpjdEoeXm4iCjr9rJuAt6ZfCmpD7sSGq-VukRe02zWYKiP6Lng5BF35YRQ=runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFPhGCUZX_2mfReg30cqkg7yhoVOVYQmwnEjaWRBrOhknIeSO2eb0ogyKy_f-DnlnkxE1ivIZlfIJqI0i9etwj_YIS59OYrSq02vMST1fCXlQRVbWuU0wNLuunKHABBtrwLyzyxYQ6GmBC0_r4-IPaxWsfQyM4OxwFpBAAJac29sg==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJkvGPx7nB_hY-NTl60WtBVKdGUOm3wyN8iXJiQiTWQVXUonNhQBFrI2eYTi5loLKb1E9xEIAQgGTOAIh0HO1Qn3cAFoFh5a5A7GNY0RrfK9GACKWqd7aD5pi4ciaRVPuFzw==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH3vQu8w89d0KIZJfuLeVTfOjchnoPIh6xS6vMEmuiTzhQ8K4-mHoVJacGBNE9USOzkK22Dtg5wZsXupnUZ-kWX_AIBAmCgZgMi9WYdaXVHOpUEuEVr-bwaVFzcne5vPUeE6YvSADrh7jU4GslpdyP_oSU2l2LGruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEPgIDf3jahvv_FeU5wDWa8QOfQqCSF5gDs9rzHP4p0wDNEJNCzmUJwqLCX02cs3ynJwOsu-YPUpUoobwDcnJHR1WagnsiAgpF82pJmKagT5YbPxvC6runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGkc3Aw5NnTxE7uTXpGYuVNuJ8AALGljlHxD81unusoQ_2mW0OMJD4d0kCzKEQModHMb_sbszsWC-M_3qDxPdkNnYNdK__QXga5kLFmj9iWVoM55Omfvruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQRb3hejswf0Qmj26sulM6Az9Q0HtKWL11vfRZihlvtfkWeyus4Q_Y9zNVMswVmAy4uWp_2CQJ7NK8LNnfTAQ26yIYQlpjdEoeXm4iCjr9rJuAt6ZfCmpD7sSGq-VukRe02zWYKiP6Lng5BF35YRQ=runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFPhGCUZX_2mfReg30cqkg7yhoVOVYQmwnEjaWRBrOhknIeSO2eb0ogyKy_f-DnlnkxE1ivIZlfIJqI0i9etwj_YIS59OYrSq02vMST1fCXlQRVbWuU0wNLuunKHABBtrwLyzyxYQ6GmBC0_r4-IPaxWsfQyM4OxwFpBAAJac29sg==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJkvGPx7nB_hY-NTl60WtBVKdGUOm3wyN8iXJiQiTWQVXUonNhQBFrI2eYTi5loLKb1E9xEIAQgGTOAIh0HO1Qn3cAFoFh5a5A7GNY0RrfK9GACKWqd7aD5pi4ciaRVPuFzw==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH3vQu8w89d0KIZJfuLeVTfOjchnoPIh6xS6vMEmuiTzhQ8K4-mHoVJacGBNE9USOzkK22Dtg5wZsXupnUZ-kWX_AIBAmCgZgMi9WYdaXVHOpUEuEVr-bwaVFzcne5vPUeE6YvSADrh7jU4GslpdyP_oSU2l2LGruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEPgIDf3jahvv_FeU5wDWa8QOfQqCSF5gDs9rzHP4p0wDNEJNCzmUJwqLCX02cs3ynJwOsu-YPUpUoobwDcnJHR1WagnsiAgpF82pJmKagT5YbPxvC6runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGkc3Aw5NnTxE7uTXpGYuVNuJ8AALGljlHxD81unusoQ_2mW0OMJD4d0kCzKEQModHMb_sbszsWC-M_3qDxPdkNnYNdK__QXga5kLFmj9iWVoM55Omfvruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQRb3hejswf0Qmj26sulM6Az9Q0HtKWL11vfRZihlvtfkWeyus4Q_Y9zNVMswVmAy4uWp_2CQJ7NK8LNnfTAQ26yIYQlpjdEoeXm4iCjr9rJuAt6ZfCmpD7sSGq-VukRe02zWYKiP6Lng5BF35YRQ=runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFPhGCUZX_2mfReg30cqkg7yhoVOVYQmwnEjaWRBrOhknIeSO2eb0ogyKy_f-DnlnkxE1ivIZlfIJqI0i9etwj_YIS59OYrSq02vMST1fCXlQRVbWuU0wNLuunKHABBtrwLyzyxYQ6GmBC0_r4-IPaxWsfQyM4OxwFpBAAJac29sg==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJkvGPx7nB_hY-NTl60WtBVKdGUOm3wyN8iXJiQiTWQVXUonNhQBFrI2eYTi5loLKb1E9xEIAQgGTOAIh0HO1Qn3cAFoFh5a5A7GNY0RrfK9GACKWqd7aD5pi4ciaRVPuFzw==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH3vQu8w89d0KIZJfuLeVTfOjchnoPIh6xS6vMEmuiTzhQ8K4-mHoVJacGBNE9USOzkK22Dtg5wZsXupnUZ-kWX_AIBAmCgZgMi9WYdaXVHOpUEuEVr-bwaVFzcne5vPUeE6YvSADrh7jU4GslpdyP_oSU2l2LGruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEPgIDf3jahvv_FeU5wDWa8QOfQqCSF5gDs9rzHP4p0wDNEJNCzmUJwqLCX02cs3ynJwOsu-YPUpUoobwDcnJHR1WagnsiAgpF82pJmKagT5YbPxvC6runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGkc3Aw5NnTxE7uTXpGYuVNuJ8AALGljlHxD81unusoQ_2mW0OMJD4d0kCzKEQModHMb_sbszsWC-M_3qDxPdkNnYNdK__QXga5kLFmj9iWVoM55Omfvruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQRb3hejswf0Qmj26sulM6Az9Q0HtKWL11vfRZihlvtfkWeyus4Q_Y9zNVMswVmAy4uWp_2CQJ7NK8LNnfTAQ26yIYQlpjdEoeXm4iCjr9rJuAt6ZfCmpD7sSGq-VukRe02zWYKiP6Lng5BF35YRQ=runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFPhGCUZX_2mfReg30cqkg7yhoVOVYQmwnEjaWRBrOhknIeSO2eb0ogyKy_f-DnlnkxE1ivIZlfIJqI0i9etwj_YIS59OYrSq02vMST1fCXlQRVbWuU0wNLuunKHABBtrwLyzyxYQ6GmBC0_r4-IPaxWsfQyM4OxwFpBAAJac29sg==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJkvGPx7nB_hY-NTl60WtBVKdGUOm3wyN8iXJiQiTWQVXUonNhQBFrI2eYTi5loLKb1E9xEIAQgGTOAIh0HO1Qn3cAFoFh5a5A7GNY0RrfK9GACKWqd7aD5pi4ciaRVPuFzw==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH3vQu8w89d0KIZJfuLeVTfOjchnoPIh6xS6vMEmuiTzhQ8K4-mHoVJacGBNE9USOzkK22Dtg5wZsXupnUZ-kWX_AIBAmCgZgMi9WYdaXVHOpUEuEVr-bwaVFzcne5vPUeE6YvSADrh7jU4GslpdyP_oSU2l2LGruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEPgIDf3jahvv_FeU5wDWa8QOfQqCSF5gDs9rzHP4p0wDNEJNCzmUJwqLCX02cs3ynJwOsu-YPUpUoobwDcnJHR1WagnsiAgpF82pJmKagT5YbPxvC6runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGkc3Aw5NnTxE7uTXpGYuVNuJ8AALGljlHxD81unusoQ_2mW0OMJD4d0kCzKEQModHMb_sbszsWC-M_3qDxPdkNnYNdK__QXga5kLFmj9iWVoM55Omfvruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQRb3hejswf0Qmj26sulM6Az9Q0HtKWL11vfRZihlvtfkWeyus4Q_Y9zNVMswVmAy4uWp_2CQJ7NK8LNnfTAQ26yIYQlpjdEoeXm4iCjr9rJuAt6ZfCmpD7sSGq-VukRe02zWYKiP6Lng5BF35YRQ=runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFPhGCUZX_2mfReg30cqkg7yhoVOVYQmwnEjaWRBrOhknIeSO2eb0ogyKy_f-DnlnkxE1ivIZlfIJqI0i9etwj_YIS59OYrSq02vMST1fCXlQRVbWuU0wNLuunKHABBtrwLyzyxYQ6GmBC0_r4-IPaxWsfQyM4OxwFpBAAJac29sg==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJkvGPx7nB_hY-NTl60WtBVKdGUOm3wyN8iXJiQiTWQVXUonNhQBFrI2eYTi5loLKb1E9xEIAQgGTOAIh0HO1Qn3cAFoFh5a5A7GNY0RrfK9GACKWqd7aD5pi4ciaRVPuFzw==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH3vQu8w89d0KIZJfuLeVTfOjchnoPIh6xS6vMEmuiTzhQ8K4-mHoVJacGBNE9USOzkK22Dtg5wZsXupnUZ-kWX_AIBAmCgZgMi9WYdaXVHOpUEuEVr-bwaVFzcne5vPUeE6YvSADrh7jU4GslpdyP_oSU2l2LGruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEPgIDf3jahvv_FeU5wDWa8QOfQqCSF5gDs9rzHP4p0wDNEJNCzmUJwqLCX02cs3ynJwOsu-YPUpUoobwDcnJHR1WagnsiAgpF82pJmKagT5YbPxvC6runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGkc3Aw5NnTxE7uTXpGYuVNuJ8AALGljlHxD81unusoQ_2mW0OMJD4d0kCzKEQModHMb_sbszsWC-M_3qDxPdkNnYNdK__QXga5kLFmj9iWVoM55Omfvruntime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGQRb3hejswf0Qmj26sulM6Az9Q0HtKWL11vfRZihlvtfkWeyus4Q_Y9zNVMswVmAy4uWp_2CQJ7NK8LNnfTAQ26yIYQlpjdEoeXm4iCjr9rJuAt6ZfCmpD7sSGq-VukRe02zWYKiP6Lng5BF35YRQ=runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFPhGCUZX_2mfReg30cqkg7yhoVOVYQmwnEjaWRBrOhknIeSO2eb0ogyKy_f-DnlnkxE1ivIZlfIJqI0i9etwj_YIS59OYrSq02vMST1fCXlQRVbWuU0wNLuunKHABBtrwLyzyxYQ6GmBC0_r4-IPaxWsfQyM4OxwFpBAAJac29sg==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJkvGPx7nB_hY-NTl60WtBVKdGUOm3wyN8iXJiQiTWQVXUonNhQBFrI2eYTi5loLKb1E9xEIAQgGTOAIh0HO1Qn3cAFoFh5a5A7GNY0RrfK9GACKWqd7aD5pi4ciaRVPuFzw==runtime.Stack(buf []byte, all bool) int
: This function formats a stack trace of the calling goroutine into the provided byte buffer. - https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH3vQu8w89d0KIZJfuLeVTfOjchnoPIh6xS6vMEmuiTzhQ8K4-mHoVJacGBNE9USOzkK22Dtg5wZsXupnUZ-kWX_AIBAmCgZgMi9WYdaXVHOpUEuEVr-bwaVFzcne5vPUeE6YvSADrh7jU4GslpdyP_oSU2l2LG