[インデックス 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ランタイムの低レベルな処理に関する情報。