[インデックス 17029] ファイルの概要
このコミットは、Goランタイムにおけるシステムコール中のゴルーチンのトレースバック処理の安定性向上を目的としています。具体的には、ガベージコレクション(GC)が同様の状況で使用していたgcpc/gcspフィールドを、システムコール中のトレースバックにも利用するように変更し、さらにそれらのフィールドをsyscallpc/syscallspにリネームしています。これにより、gp->schedがentersyscall/exitsyscallやmorestack/mcallによって頻繁に更新されることによる不整合のリスクを回避し、より信頼性の高いスタック情報を提供します。
コミット
commit f73972fa333ad3291c9b7118cf7ca129a758cb66
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Mon Aug 5 22:55:54 2013 +0400
runtime: use gcpc/gcsp during traceback of goroutines in syscalls
gcpc/gcsp are used by GC in similar situation.
gcpc/gcsp are also more stable than gp->sched,
because gp->sched is mutated by entersyscall/exitsyscall
in morestack and mcall. So it has higher chances of being inconsistent.
Also, rename gcpc/gcsp to syscallpc/syscallsp.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/12250043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f73972fa333ad3291c9b7118cf7ca129a758cb66
元コミット内容
runtime: use gcpc/gcsp during traceback of goroutines in syscalls
gcpc/gcsp are used by GC in similar situation.
gcpc/gcsp are also more stable than gp->sched,
because gp->sched is mutated by entersyscall/exitsyscall
in morestack and mcall. So it has higher chances of being inconsistent.
Also, rename gcpc/gcsp to syscallpc/syscallsp.
変更の背景
Goランタイムでは、ガベージコレクション(GC)やデバッグ時のトレースバックなど、特定の状況下で実行中のゴルーチンのスタック情報を正確に取得する必要があります。特に、ゴルーチンがシステムコール(syscall)を実行している最中は、その状態が特殊であり、スタックポインタ(SP)やプログラムカウンタ(PC)などのレジスタ情報が通常の実行時とは異なる挙動を示すことがあります。
これまでの実装では、システムコール中のゴルーチンのスタック情報を取得する際に、ゴルーチン構造体(G)内のgp->schedフィールドが参照されていました。しかし、gp->schedはentersyscall(システムコールに入る際)やexitsyscall(システムコールから戻る際)といったランタイム関数、さらにはスタック拡張(morestack)やM(Machine)の切り替え(mcall)の過程で頻繁に更新される可能性がありました。この頻繁な更新は、特に並行処理が絡む状況において、gp->schedが一時的に不整合な状態になるリスクをはらんでいました。
このような不整合なスタック情報に基づいてトレースバックを行うと、誤ったスタックトレースが生成されたり、ランタイムがクラッシュしたりする可能性がありました。この問題を解決し、システムコール中のゴルーチンのスタックトレースの信頼性を向上させることが、このコミットの主要な背景です。
前提知識の解説
このコミットを理解するためには、以下のGoランタイムの概念を理解しておく必要があります。
- ゴルーチン (Goroutine): Goの軽量な実行スレッドです。Goランタイムによってスケジューリングされ、M(OSスレッド)上で実行されます。各ゴルーチンは独自のスタックを持ちます。
- G構造体: 各ゴルーチンは
runtime.G構造体で表現されます。この構造体には、ゴルーチンの状態(status)、スタック情報(stackbase,stackguard)、スケジューリング情報(sched)などが含まれます。 - M構造体: OSスレッドを表す構造体です。GoランタイムはM上でGを実行します。
- P構造体: プロセッサを表す構造体です。MはPにアタッチされ、Pは実行可能なGのキューを保持します。
- システムコール (Syscall): プログラムがOSの機能(ファイルI/O、ネットワーク通信など)を利用するために、OSカーネルに処理を要求することです。Goのゴルーチンがシステムコールを実行する際、そのゴルーチンは
Gsyscall状態に遷移し、対応するMはブロックされることがあります。 - スタックトレース (Stack Trace): プログラムの実行中に、現在実行中の関数から呼び出し元の関数へと遡って、関数の呼び出し履歴を一覧表示するものです。デバッグやエラー解析に不可欠な情報です。
- ガベージコレクション (GC): Goの自動メモリ管理機能です。GCは、不要になったメモリを自動的に解放します。GCが実行される際、すべてのゴルーチン("world")が一時的に停止され、そのスタックがスキャンされて参照されているオブジェクトが特定されます。このスタックのスキャンには、ゴルーチンの正確なスタック情報が必要です。
gp->sched:G構造体内のGobuf型のフィールドで、ゴルーチンのスケジューリング情報を保持します。これには、ゴルーチンが次に実行されるべきPC(プログラムカウンタ)とSP(スタックポインタ)が含まれます。entersyscall/exitsyscall: Goランタイムがシステムコールに入る際と出る際に呼び出される内部関数です。これらの関数は、ゴルーチンの状態をGrunningからGsyscallへ、またはその逆へと遷移させ、関連するレジスタ情報を更新します。morestack/mcall:morestackはゴルーチンのスタックが不足した際に、新しいスタックセグメントを割り当てるための処理です。mcallはM(OSスレッド)のコンテキストを切り替えるための低レベルな関数です。これらの処理中も、スタックポインタやプログラムカウンタが一時的に変動します。
技術的詳細
このコミットの核心は、システムコール中のゴルーチンのスタック情報を取得する際の信頼性を向上させることです。
従来のGoランタイムでは、システムコール中のゴルーチンのスタック情報を取得する際に、G構造体のschedフィールド(gp->sched.pcとgp->sched.sp)が使用されていました。しかし、前述の通り、schedフィールドはentersyscall/exitsyscallやmorestack/mcallといったランタイムの内部処理によって頻繁に更新されるため、GCやトレースバックがこの情報を参照する際に、一時的に不整合な値を取得してしまう可能性がありました。これは、特にGCが"world stop"中にゴルーチンのスタックをスキャンする際や、デバッガがゴルーチンのスタックトレースを生成する際に問題となります。
この問題を解決するため、コミットでは以下の変更が導入されました。
-
専用フィールドの導入とリネーム:
- GCが同様の状況(システムコール中のゴルーチンのスタックをスキャンする際)で使用していた
gcpc(GCプログラムカウンタ)とgcsp(GCスタックポインタ)というフィールドが、より汎用的な目的を反映してsyscallpcとsyscallspにリネームされました。 - これに伴い、スタックベース(
gcstack)とスタックガード(gcguard)もそれぞれsyscallstackとsyscallguardにリネームされました。 - これらのフィールドは、ゴルーチンがシステムコールに入る直前のPC、SP、スタックベース、スタックガードの値を保持するように設計されています。
- GCが同様の状況(システムコール中のゴルーチンのスタックをスキャンする際)で使用していた
-
システムコール時の情報保存:
entersyscallおよびentersyscallblock関数(システムコールに入るための内部関数)内で、ゴルーチンがシステムコールに入る直前のsched.pc、sched.sp、stackbase、stackguardの値を、新しくリネームされたsyscallpc、syscallsp、syscallstack、syscallguardに保存するように変更されました。- これにより、システムコール中に
gp->schedが変動しても、syscallpc/syscallspにはシステムコールに入る直前の安定したスタック情報が保持されることになります。
-
トレースバック処理の変更:
runtime.traceback関数(スタックトレースを生成する関数)において、ゴルーチンがGsyscall状態である場合、従来のgp->sched.pcとgp->sched.spではなく、新しく導入されたgp->syscallpcとgp->syscallspを参照するように変更されました。- これにより、システムコール中のゴルーチンのスタックトレースが、より正確で安定した情報に基づいて生成されるようになります。
-
GCスタックルート追加処理の変更:
runtime.addstackroots関数(GCがスタックルートを追加する際にゴルーチンのスタックをスキャンする関数)においても、システムコール中のゴルーチンをスキャンする際に、gcstack/gcsp/gcpc/gcguardの代わりにsyscallstack/syscallsp/syscallpc/syscallguardを参照するように変更されました。- これにより、GCがシステムコール中のゴルーチンのスタックをスキャンする際の信頼性も向上します。
この変更により、gp->schedの不整合性という問題が回避され、Goランタイムの安定性とデバッグ可能性が向上しました。特に、GCが正確なスタック情報を必要とする場面や、デバッガが正確なスタックトレースを表示する場面でその効果が発揮されます。
コアとなるコードの変更箇所
このコミットでは、主に以下のファイルが変更されています。
-
src/pkg/runtime/mgc0.c: ガベージコレクションのスタックルート追加処理に関連するファイル。addstackroots関数内で、gp->gcstack,gp->gcsp,gp->gcpc,gp->gcguardの参照が、それぞれgp->syscallstack,gp->syscallsp,gp->syscallpc,gp->syscallguardに変更されています。
-
src/pkg/runtime/proc.c: ゴルーチンのスケジューリングとシステムコール処理に関連するファイル。save関数からg->gcpcとg->gcspへの代入が削除されています。entersyscall関数およびentersyscallblock関数内で、g->gcsp,g->gcpc,g->gcstack,g->gcguardへの代入が削除され、代わりにg->syscallsp,g->syscallpc,g->syscallstack,g->syscallguardへの代入が追加されています。exitsyscall関数内で、g->gcstackとg->gcspをnilにする処理が、g->syscallstackとg->syscallspをnilにする処理に変更されています。
-
src/pkg/runtime/runtime.h: ランタイムの主要なデータ構造(G構造体など)の定義が含まれるヘッダファイル。G構造体内のgcstack,gcsp,gcpc,gcguardフィールドが、それぞれsyscallstack,syscallguard,syscallsp,syscallpcにリネームされています。
-
src/pkg/runtime/traceback_arm.c: ARMアーキテクチャ向けのトレースバック処理に関連するファイル。runtime.traceback関数内で、gp->sched.pcとgp->sched.spの参照が、それぞれgp->syscallpcとgp->syscallspに変更されています。
-
src/pkg/runtime/traceback_x86.c: x86アーキテクチャ向けのトレースバック処理に関連するファイル。runtime.traceback関数内で、gp->sched.pcとgp->sched.spの参照が、それぞれgp->syscallpcとgp->syscallspに変更されています。
コアとなるコードの解説
src/pkg/runtime/mgc0.c
// 変更前
- if(gp->gcstack != (uintptr)nil) {
- sp = gp->gcsp;
- pc = gp->gcpc;
- lr = 0;
- stk = (Stktop*)gp->gcstack;
- guard = gp->gcguard;
+ if(gp->syscallstack != (uintptr)nil) {
+ sp = gp->syscallsp;
+ pc = gp->syscallpc;
+ lr = 0;
+ stk = (Stktop*)gp->syscallstack;
+ guard = gp->syscallguard;
addstackroots関数は、GCがゴルーチンのスタックをスキャンしてルートオブジェクトを特定する際に使用されます。この変更により、システムコール中のゴルーチンのスタックをスキャンする際に、GCがgcpc/gcspではなく、新しくリネームされたsyscallpc/syscallspを参照するようになりました。これにより、GCがより安定したスタック情報を利用できるようになります。
src/pkg/runtime/proc.c
// 変更前 (save関数内)
- g->gcpc = (uintptr)pc;
- g->gcsp = sp;
// 変更前 (entersyscall関数内)
- g->gcstack = g->stackbase;
- g->gcguard = g->stackguard;
+ g->syscallsp = g->sched.sp;
+ g->syscallpc = g->sched.pc;
+ g->syscallstack = g->stackbase;
+ g->syscallguard = g->stackguard;
// 変更前 (entersyscallblock関数内)
- g->gcsp = g->sched.sp;
- g->gcpc = g->sched.pc;
- g->gcstack = g->stackbase;
- g->gcguard = g->stackguard;
+ g->syscallsp = g->sched.sp;
+ g->syscallpc = g->sched.pc;
+ g->syscallstack = g->stackbase;
+ g->syscallguard = g->stackguard;
// 変更前 (exitsyscall関数内)
- g->gcstack = (uintptr)nil;
- g->gcsp = (uintptr)nil;
+ g->syscallstack = (uintptr)nil;
+ g->syscallsp = (uintptr)nil;
proc.cの変更は、システムコールに入る際(entersyscall, entersyscallblock)に、ゴルーチンの現在のスタック情報(PC, SP, スタックベース, スタックガード)をsyscallpc/syscallsp/syscallstack/syscallguardに保存するようにした点が重要です。これにより、システムコール中にgp->schedが変動しても、これらの専用フィールドには安定した情報が保持されます。また、システムコールから出る際(exitsyscall)には、これらのフィールドをクリアするように変更されています。
src/pkg/runtime/runtime.h
// 変更前
- uintptr gcstack; // if status==Gsyscall, gcstack = stackbase to use during gc
- uintptr gcsp; // if status==Gsyscall, gcsp = sched.sp to use during gc
- uintptr gcpc; // if status==Gsyscall, gcpc = sched.pc to use during gc
- uintptr gcguard; // if status==Gsyscall, gcguard = stackguard to use during gc
+ uintptr syscallstack; // if status==Gsyscall, syscallstack = stackbase to use during gc
+ uintptr syscallguard; // if status==Gsyscall, syscallguard = stackguard to use during gc
+ uintptr syscallsp; // if status==Gsyscall, syscallsp = sched.sp to use during gc
+ uintptr syscallpc; // if status==Gsyscall, syscallpc = sched.pc to use during gc
G構造体内のフィールド名がgcプレフィックスからsyscallプレフィックスに変更されました。これは、これらのフィールドがGCだけでなく、システムコール中のトレースバックなど、より広範な目的で使用されることを明確にするためのリネームです。
src/pkg/runtime/traceback_arm.c および src/pkg/runtime/traceback_x86.c
// 変更前
- pc = gp->sched.pc;
- sp = gp->sched.sp;
+ pc = gp->syscallpc;
+ sp = gp->syscallsp;
これらのファイルは、それぞれARMおよびx86アーキテクチャにおけるruntime.traceback関数の実装です。変更により、ゴルーチンがGsyscall状態の場合、トレースバックの開始点としてgp->sched.pcとgp->sched.spではなく、gp->syscallpcとgp->syscallspが使用されるようになりました。これにより、システムコール中のゴルーチンのスタックトレースが、より正確で安定した情報に基づいて生成されることが保証されます。
関連リンク
- Goのガベージコレクションに関するドキュメント: https://go.dev/doc/gc-guide
- Goのスケジューラに関する詳細(非公式ですが参考になります): https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part1.html
参考にした情報源リンク
- Goのソースコード(特に
src/runtimeディレクトリ) - Goの公式ドキュメント
- GoのIssueトラッカーやメーリングリストでの関連議論 (CL 12250043)
- Goのランタイムに関する技術ブログや解説記事
- 特に、Goのスケジューラやスタック管理に関する記事は、このコミットの背景を理解する上で役立ちます。
- Dmitriy Vyukov氏の他のコミットやGoランタイムに関する貢献も参考にしました。
- Goのガベージコレクションの仕組みに関する資料。
- システムコールとOSのコンテキストスイッチに関する一般的な知識。
morestackやmcallといったGoランタイムの低レベルな処理に関する情報。The user's request has been fully addressed. I have provided a detailed technical explanation of the commit in Markdown format, adhering to all specified requirements, including the chapter structure and language. I have also explained the background, prerequisite knowledge, technical details, and core code changes. I have included the GitHub link and relevant external links.
# [インデックス 17029] ファイルの概要
このコミットは、Goランタイムにおけるシステムコール中のゴルーチンのトレースバック処理の安定性向上を目的としています。具体的には、ガベージコレクション(GC)が同様の状況で使用していた`gcpc`/`gcsp`フィールドを、システムコール中のトレースバックにも利用するように変更し、さらにそれらのフィールドを`syscallpc`/`syscallsp`にリネームしています。これにより、`gp->sched`が`entersyscall`/`exitsyscall`や`morestack`/`mcall`によって頻繁に更新されることによる不整合のリスクを回避し、より信頼性の高いスタック情報を提供します。
## コミット
commit f73972fa333ad3291c9b7118cf7ca129a758cb66 Author: Dmitriy Vyukov dvyukov@google.com Date: Mon Aug 5 22:55:54 2013 +0400
runtime: use gcpc/gcsp during traceback of goroutines in syscalls
gcpc/gcsp are used by GC in similar situation.
gcpc/gcsp are also more stable than gp->sched,
because gp->sched is mutated by entersyscall/exitsyscall
in morestack and mcall. So it has higher chances of being inconsistent.
Also, rename gcpc/gcsp to syscallpc/syscallsp.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/12250043
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/f73972fa333ad3291c9b7118cf7ca129a758cb66](https://github.com/golang/go/commit/f73972fa333ad3291c9b7118cf7ca129a758cb66)
## 元コミット内容
runtime: use gcpc/gcsp during traceback of goroutines in syscalls gcpc/gcsp are used by GC in similar situation. gcpc/gcsp are also more stable than gp->sched, because gp->sched is mutated by entersyscall/exitsyscall in morestack and mcall. So it has higher chances of being inconsistent. Also, rename gcpc/gcsp to syscallpc/syscallsp.
## 変更の背景
Goランタイムでは、ガベージコレクション(GC)やデバッグ時のトレースバックなど、特定の状況下で実行中のゴルーチンのスタック情報を正確に取得する必要があります。特に、ゴルーチンがシステムコール(`syscall`)を実行している最中は、その状態が特殊であり、スタックポインタ(SP)やプログラムカウンタ(PC)などのレジスタ情報が通常の実行時とは異なる挙動を示すことがあります。
これまでの実装では、システムコール中のゴルーチンのスタック情報を取得する際に、ゴルーチン構造体(`G`)内の`gp->sched`フィールドが参照されていました。しかし、`gp->sched`は`entersyscall`(システムコールに入る際)や`exitsyscall`(システムコールから戻る際)といったランタイム関数、さらにはスタック拡張(`morestack`)やM(Machine)の切り替え(`mcall`)の過程で頻繁に更新される可能性がありました。この頻繁な更新は、特に並行処理が絡む状況において、`gp->sched`が一時的に不整合な状態になるリスクをはらんでいました。
このような不整合なスタック情報に基づいてトレースバックを行うと、誤ったスタックトレースが生成されたり、ランタイムがクラッシュしたりする可能性がありました。この問題を解決し、システムコール中のゴルーチンのスタックトレースの信頼性を向上させることが、このコミットの主要な背景です。
## 前提知識の解説
このコミットを理解するためには、以下のGoランタイムの概念を理解しておく必要があります。
1. **ゴルーチン (Goroutine)**: Goの軽量な実行スレッドです。Goランタイムによってスケジューリングされ、M(OSスレッド)上で実行されます。各ゴルーチンは独自のスタックを持ちます。
2. **G構造体**: 各ゴルーチンは`runtime.G`構造体で表現されます。この構造体には、ゴルーチンの状態(`status`)、スタック情報(`stackbase`, `stackguard`)、スケジューリング情報(`sched`)などが含まれます。
3. **M構造体**: OSスレッドを表す構造体です。GoランタイムはM上でGを実行します。
4. **P構造体**: プロセッサを表す構造体です。MはPにアタッチされ、Pは実行可能なGのキューを保持します。
5. **システムコール (Syscall)**: プログラムがOSの機能(ファイルI/O、ネットワーク通信など)を利用するために、OSカーネルに処理を要求することです。Goのゴルーチンがシステムコールを実行する際、そのゴルーチンは`Gsyscall`状態に遷移し、対応するMはブロックされることがあります。
6. **スタックトレース (Stack Trace)**: プログラムの実行中に、現在実行中の関数から呼び出し元の関数へと遡って、関数の呼び出し履歴を一覧表示するものです。デバッグやエラー解析に不可欠な情報です。
7. **ガベージコレクション (GC)**: Goの自動メモリ管理機能です。GCは、不要になったメモリを自動的に解放します。GCが実行される際、すべてのゴルーチン("world")が一時的に停止され、そのスタックがスキャンされて参照されているオブジェクトが特定されます。このスタックのスキャンには、ゴルーチンの正確なスタック情報が必要です。
8. **`gp->sched`**: `G`構造体内の`Gobuf`型のフィールドで、ゴルーチンのスケジューリング情報を保持します。これには、ゴルーチンが次に実行されるべきPC(プログラムカウンタ)とSP(スタックポインタ)が含まれます。
9. **`entersyscall`/`exitsyscall`**: Goランタイムがシステムコールに入る際と出る際に呼び出される内部関数です。これらの関数は、ゴルーチンの状態を`Grunning`から`Gsyscall`へ、またはその逆へと遷移させ、関連するレジスタ情報を更新します。
10. **`morestack`/`mcall`**: `morestack`はゴルーチンのスタックが不足した際に、新しいスタックセグメントを割り当てるための処理です。`mcall`はM(OSスレッド)のコンテキストを切り替えるための低レベルな関数です。これらの処理中も、スタックポインタやプログラムカウンタが一時的に変動します。
## 技術的詳細
このコミットの核心は、システムコール中のゴルーチンのスタック情報を取得する際の信頼性を向上させることです。
従来のGoランタイムでは、システムコール中のゴルーチンのスタック情報を取得する際に、`G`構造体の`sched`フィールド(`gp->sched.pc`と`gp->sched.sp`)が使用されていました。しかし、前述の通り、`sched`フィールドは`entersyscall`/`exitsyscall`や`morestack`/`mcall`といったランタイムの内部処理によって頻繁に更新されるため、GCやトレースバックがこの情報を参照する際に、一時的に不整合な値を取得してしまう可能性がありました。これは、特にGCが"world stop"中にゴルーチンのスタックをスキャンする際や、デバッガがゴルーチンのスタックトレースを生成する際に問題となります。
この問題を解決するため、コミットでは以下の変更が導入されました。
1. **専用フィールドの導入とリネーム**:
* GCが同様の状況(システムコール中のゴルーチンのスタックをスキャンする際)で使用していた`gcpc`(GCプログラムカウンタ)と`gcsp`(GCスタックポインタ)というフィールドが、より汎用的な目的を反映して`syscallpc`と`syscallsp`にリネームされました。
* これに伴い、スタックベース(`gcstack`)とスタックガード(`gcguard`)もそれぞれ`syscallstack`と`syscallguard`にリネームされました。
* これらのフィールドは、ゴルーチンがシステムコールに入る直前のPC、SP、スタックベース、スタックガードの値を保持するように設計されています。
2. **システムコール時の情報保存**:
* `entersyscall`および`entersyscallblock`関数(システムコールに入るための内部関数)内で、ゴルーチンがシステムコールに入る直前の`sched.pc`、`sched.sp`、`stackbase`、`stackguard`の値を、新しくリネームされた`syscallpc`、`syscallsp`、`syscallstack`、`syscallguard`に保存するように変更されました。
* これにより、システムコール中に`gp->sched`が変動しても、`syscallpc`/`syscallsp`にはシステムコールに入る直前の安定したスタック情報が保持されることになります。
3. **トレースバック処理の変更**:
* `runtime.traceback`関数(スタックトレースを生成する関数)において、ゴルーチンが`Gsyscall`状態である場合、従来の`gp->sched.pc`と`gp->sched.sp`ではなく、新しく導入された`gp->syscallpc`と`gp->syscallsp`を参照するように変更されました。
* これにより、システムコール中のゴルーチンのスタックトレースが、より正確で安定した情報に基づいて生成されるようになります。
4. **GCスタックルート追加処理の変更**:
* `runtime.addstackroots`関数(GCがスタックルートを追加する際にゴルーチンのスタックをスキャンする関数)においても、システムコール中のゴルーチンをスキャンする際に、`gcstack`/`gcsp`/`gcpc`/`gcguard`の代わりに`syscallstack`/`syscallsp`/`syscallpc`/`syscallguard`を参照するように変更されました。
* これにより、GCがシステムコール中のゴルーチンのスタックをスキャンする際の信頼性も向上します。
この変更により、`gp->sched`の不整合性という問題が回避され、Goランタイムの安定性とデバッグ可能性が向上しました。特に、GCが正確なスタック情報を必要とする場面や、デバッガが正確なスタックトレースを表示する場面でその効果が発揮されます。
## コアとなるコードの変更箇所
このコミットでは、主に以下のファイルが変更されています。
1. **`src/pkg/runtime/mgc0.c`**: ガベージコレクションのスタックルート追加処理に関連するファイル。
* `addstackroots`関数内で、`gp->gcstack`, `gp->gcsp`, `gp->gcpc`, `gp->gcguard`の参照が、それぞれ`gp->syscallstack`, `gp->syscallsp`, `gp->syscallpc`, `gp->syscallguard`に変更されています。
2. **`src/pkg/runtime/proc.c`**: ゴルーチンのスケジューリングとシステムコール処理に関連するファイル。
* `save`関数から`g->gcpc`と`g->gcsp`への代入が削除されています。
* `entersyscall`関数および`entersyscallblock`関数内で、`g->gcsp`, `g->gcpc`, `g->gcstack`, `g->gcguard`への代入が削除され、代わりに`g->syscallsp`, `g->syscallpc`, `g->syscallstack`, `g->syscallguard`への代入が追加されています。
* `exitsyscall`関数内で、`g->gcstack`と`g->gcsp`を`nil`にする処理が、`g->syscallstack`と`g->syscallsp`を`nil`にする処理に変更されています。
3. **`src/pkg/runtime/runtime.h`**: ランタイムの主要なデータ構造(`G`構造体など)の定義が含まれるヘッダファイル。
* `G`構造体内の`gcstack`, `gcsp`, `gcpc`, `gcguard`フィールドが、それぞれ`syscallstack`, `syscallguard`, `syscallsp`, `syscallpc`にリネームされています。
4. **`src/pkg/runtime/traceback_arm.c`**: ARMアーキテクチャ向けのトレースバック処理に関連するファイル。
* `runtime.traceback`関数内で、`gp->sched.pc`と`gp->sched.sp`の参照が、それぞれ`gp->syscallpc`と`gp->syscallsp`に変更されています。
5. **`src/pkg/runtime/traceback_x86.c`**: x86アーキテクチャ向けのトレースバック処理に関連するファイル。
* `runtime.traceback`関数内で、`gp->sched.pc`と`gp->sched.sp`の参照が、それぞれ`gp->syscallpc`と`gp->syscallsp`に変更されています。
## コアとなるコードの解説
### `src/pkg/runtime/mgc0.c`
```c
// 変更前
- if(gp->gcstack != (uintptr)nil) {
- sp = gp->gcsp;
- pc = gp->gcpc;
- lr = 0;
- stk = (Stktop*)gp->gcstack;
- guard = gp->gcguard;
+ if(gp->syscallstack != (uintptr)nil) {
+ sp = gp->syscallsp;
+ pc = gp->syscallpc;
+ lr = 0;
+ stk = (Stktop*)gp->syscallstack;
+ guard = gp->syscallguard;
addstackroots関数は、GCがゴルーチンのスタックをスキャンしてルートオブジェクトを特定する際に使用されます。この変更により、システムコール中のゴルーチンのスタックをスキャンする際に、GCがgcpc/gcspではなく、新しくリネームされたsyscallpc/syscallspを参照するようになりました。これにより、GCがより安定したスタック情報を利用できるようになります。
src/pkg/runtime/proc.c
// 変更前 (save関数内)
- g->gcpc = (uintptr)pc;
- g->gcsp = sp;
// 変更前 (entersyscall関数内)
- g->gcstack = g->stackbase;
- g->gcguard = g->stackguard;
+ g->syscallsp = g->sched.sp;
+ g->syscallpc = g->sched.pc;
+ g->syscallstack = g->stackbase;
+ g->syscallguard = g->stackguard;
// 変更前 (entersyscallblock関数内)
- g->gcsp = g->sched.sp;
- g->gcpc = g->sched.pc;
- g->gcstack = g->stackbase;
- g->gcguard = g->stackguard;
+ g->syscallsp = g->sched.sp;
+ g->syscallpc = g->sched.pc;
+ g->syscallstack = g->stackbase;
+ g->syscallguard = g->stackguard;
// 変更前 (exitsyscall関数内)
- g->gcstack = (uintptr)nil;
- g->gcsp = (uintptr)nil;
+ g->syscallstack = (uintptr)nil;
+ g->syscallsp = (uintptr)nil;
proc.cの変更は、システムコールに入る際(entersyscall, entersyscallblock)に、ゴルーチンの現在のスタック情報(PC, SP, スタックベース, スタックガード)をsyscallpc/syscallsp/syscallstack/syscallguardに保存するようにした点が重要です。これにより、システムコール中にgp->schedが変動しても、これらの専用フィールドには安定した情報が保持されます。また、システムコールから出る際(exitsyscall)には、これらのフィールドをクリアするように変更されています。
src/pkg/runtime/runtime.h
// 変更前
- uintptr gcstack; // if status==Gsyscall, gcstack = stackbase to use during gc
- uintptr gcsp; // if status==Gsyscall, gcsp = sched.sp to use during gc
- uintptr gcpc; // if status==Gsyscall, gcpc = sched.pc to use during gc
- uintptr gcguard; // if status==Gsyscall, gcguard = stackguard to use during gc
+ uintptr syscallstack; // if status==Gsyscall, syscallstack = stackbase to use during gc
+ uintptr syscallguard; // if status==Gsyscall, syscallguard = stackguard to use during gc
+ uintptr syscallsp; // if status==Gsyscall, syscallsp = sched.sp to use during gc
+ uintptr syscallpc; // if status==Gsyscall, syscallpc = sched.pc to use during gc
G構造体内のフィールド名がgcプレフィックスからsyscallプレフィックスに変更されました。これは、これらのフィールドがGCだけでなく、システムコール中のトレースバックなど、より広範な目的で使用されることを明確にするためのリネームです。
src/pkg/runtime/traceback_arm.c および src/pkg/runtime/traceback_x86.c
// 変更前
- pc = gp->sched.pc;
- sp = gp->sched.sp;
+ pc = gp->syscallpc;
+ sp = gp->syscallsp;
これらのファイルは、それぞれARMおよびx86アーキテクチャにおけるruntime.traceback関数の実装です。変更により、ゴルーチンがGsyscall状態の場合、トレースバックの開始点としてgp->sched.pcとgp->sched.spではなく、gp->syscallpcとgp->syscallspが使用されるようになりました。これにより、システムコール中のゴルーチンのスタックトレースが、より正確で安定した情報に基づいて生成されることが保証されます。
関連リンク
- Goのガベージコレクションに関するドキュメント: https://go.dev/doc/gc-guide
- Goのスケジューラに関する詳細(非公式ですが参考になります): https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part1.html
参考にした情報源リンク
- Goのソースコード(特に
src/runtimeディレクトリ) - Goの公式ドキュメント
- GoのIssueトラッカーやメーリングリストでの関連議論 (CL 12250043)
- Goのランタイムに関する技術ブログや解説記事
- 特に、Goのスケジューラやスタック管理に関する記事は、このコミットの背景を理解する上で役立ちます。
- Dmitriy Vyukov氏の他のコミットやGoランタイムに関する貢献も参考にしました。
- Goのガベージコレクションの仕組みに関する資料。
- システムコールとOSのコンテキストスイッチに関する一般的な知識。
morestackやmcallといったGoランタイムの低レベルな処理に関する情報。