[インデックス 15594] ファイルの概要
このコミットは、Goランタイムのガベージコレクタ(GC)とスタックトレースバックに関連する変更を元に戻すものです。具体的には、以前のコミット(CL 7301062)によって導入されたARMアーキテクチャでのガベージコレクタの破損を修正するために、その変更をロールバックしています。
変更されたファイルは以下の通りです。
src/pkg/runtime/mgc0.c
src/pkg/runtime/mprof.goc
src/pkg/runtime/proc.c
src/pkg/runtime/runtime.h
src/pkg/runtime/sigqueue.goc
src/pkg/runtime/traceback_arm.c
src/pkg/runtime/traceback_x86.c
コミット
commit e0deb2ef7fe857b2541496197b0e5ad7882990f2
Author: Russ Cox <rsc@golang.org>
Date: Tue Mar 5 15:36:40 2013 -0500
undo CL 7301062 / 9742f722b558
broke arm garbage collector
traceback_arm fails with a missing pc. It needs CL 7494043.
But that only makes the build break later, this time with
"invalid freelist". Roll back until it can be fixed correctly.
««« original CL description
runtime: restrict stack root scan to locals and arguments
R=rsc
CC=golang-dev
https://golang.org/cl/7301062
»»»
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/7493044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e0deb2ef7fe857b2541496197b0e5ad7882990f2
元コミット内容
このコミットが元に戻している変更(CL 7301062)の元の説明は以下の通りです。
runtime: restrict stack root scan to locals and arguments
これは、Goランタイムにおけるスタックのルートスキャン(ガベージコレクタが参照を追跡するためにスタックを走査するプロセス)を、ローカル変数と引数に限定しようとする試みであったことを示唆しています。
変更の背景
このコミットは、以前の変更(CL 7301062)を元に戻す(undo)ために作成されました。元の変更は、ARMアーキテクチャにおけるGoのガベージコレクタを破損させてしまったためです。
具体的には、CL 7301062が適用された後、traceback_arm
関数がPC(プログラムカウンタ)の欠落により失敗する問題が発生しました。この問題はCL 7494043で修正される予定でしたが、その修正を適用しても、今度は「invalid freelist」という別のビルドエラーが発生し、問題が解決しませんでした。
これらの問題が正しく修正されるまで、一時的に元の状態に戻すことが決定されました。これは、Goランタイムの安定性を確保するための緊急的なロールバック措置です。
前提知識の解説
Goのガベージコレクタ(GC)
Go言語は、自動メモリ管理のためにガベージコレクタ(GC)を内蔵しています。GoのGCは、主にマーク&スイープ方式を採用しており、プログラムが使用しているメモリ(到達可能なオブジェクト)を特定し、それ以外の不要になったメモリを解放します。GCが正しく機能するためには、プログラムが使用しているすべてのメモリ参照(ルート)を正確に識別し、追跡する必要があります。スタック上のポインタは、これらのルートの重要な部分を構成します。
スタックとスタックスキャン
Goの各ゴルーチン(軽量スレッド)は独自のスタックを持っています。関数が呼び出されると、その関数のローカル変数、引数、戻りアドレスなどがスタックに積まれます。ガベージコレクタは、これらのスタックをスキャンして、ヒープ上のオブジェクトへのポインタを見つけ出し、それらを「到達可能」としてマークします。このプロセスを「スタックスキャン」と呼びます。スタックスキャンは、GCの正確性とパフォーマンスに直接影響します。
トレースバック(Stack Traceback)
トレースバックとは、プログラムの実行中に、現在の関数呼び出しの連鎖(コールスタック)を逆順にたどって、どの関数がどの関数を呼び出したかを示す情報のことです。デバッグやエラー報告の際に非常に役立ちます。Goランタイムでは、runtime·gentraceback
のような関数がこのトレースバック情報を生成するために使用されます。ガベージコレクタのスタックスキャンとトレースバックは、どちらもスタックを走査するという点で密接に関連しています。
ARMアーキテクチャ
ARM(Advanced RISC Machine)は、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。x86アーキテクチャとは異なる命令セットやレジスタの利用方法を持つため、低レベルのランタイムコード(特にスタックの扱いやレジスタの保存・復元)はアーキテクチャ固有の実装が必要になります。ガベージコレクタやトレースバックのような低レベルの機能は、これらのアーキテクチャ固有の特性を考慮する必要があります。
Stktop
構造体とスタックセグメント
Goのランタイムは、スタックを固定サイズのセグメントに分割して管理することがあります。ゴルーチンのスタックが必要に応じて拡大・縮小できるようにするためです。Stktop
のような構造体は、これらのスタックセグメントの境界や関連情報(例えば、前のスタックセグメントへのポインタなど)を管理するために使用されます。スタックスキャン時には、これらのセグメント境界を正しく認識し、複数のセグメントにまたがるスタック全体を走査する必要があります。
技術的詳細
このコミットの主要な目的は、CL 7301062によって導入された変更を元に戻すことです。元の変更は、ガベージコレクタのスタックルートスキャンを最適化し、ローカル変数と引数のみに限定しようとしました。しかし、この最適化がARMアーキテクチャで問題を引き起こし、ガベージコレクタの破損やビルドエラー("invalid freelist")につながったため、その変更をロールバックしています。
具体的な変更点は以下の通りです。
-
addframeroots
関数の削除:src/pkg/runtime/mgc0.c
からaddframeroots
関数が完全に削除されました。この関数は、特定のフレームのローカル変数と引数をルートとして追加するために使用されていたと考えられます。この削除は、元のCL 7301062が導入したスタックルートスキャンの制限を元に戻すことを意味します。 -
addstackroots
関数のロジックの変更:src/pkg/runtime/mgc0.c
内のaddstackroots
関数は、スタックをスキャンしてルートを見つけるための主要な関数です。このコミットでは、元のCL 7301062が導入した複雑なロジック(特にruntime·gentraceback
を呼び出してフレームごとのルートを追加する部分)が削除され、よりシンプルで以前のスタックセグメントベースの走査ロジックに戻されています。- 変更前は
runtime·gentraceback
を呼び出し、addframeroots
コールバック関数を使ってフレームごとのルートを追加していました。 - 変更後は、
Stktop
構造体を使ってスタックセグメントを順にたどり、各セグメントの範囲全体をルートとして追加する方式に戻っています。これにより、スタック上のすべてのポインタが確実にスキャンされるようになります。 gp->gcstack
,gp->gcsp
,gp->gcguard
といったシステムコール中のスタック情報を使用するロジックも、より堅牢な形に戻されています。
- 変更前は
-
runtime·gentraceback
関数のシグネチャ変更:src/pkg/runtime/runtime.h
、src/pkg/runtime/mprof.goc
、src/pkg/runtime/proc.c
、src/pkg/runtime/traceback_arm.c
、src/pkg/runtime/traceback_x86.c
において、runtime·gentraceback
関数のシグネチャが変更されました。- 変更前:
int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32, void (*fn)(Func*, byte*, byte*, void*), void *arg);
- 変更後:
int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32);
fn
とarg
という2つの引数(関数ポインタとその引数)が削除されています。これは、runtime·gentraceback
がスタックフレームごとに特定のコールバック関数を呼び出すという、元のCL 7301062で導入されたメカニズムが廃止されたことを意味します。これにより、トレースバックの汎用性が高まり、ガベージコレクタのスタックスキャンとは独立した機能として扱われるようになります。
- 変更前:
-
traceback_arm.c
とtraceback_x86.c
の変更: これらのファイルでは、runtime·gentraceback
の呼び出し箇所が新しいシグネチャに合わせて修正されています。また、fn != nil
のチェックや、(*fn)(f, (byte*)pc, sp, arg);
のようなコールバック関数の呼び出しロジックが削除されています。 さらに、pc == (uintptr)runtime·goexit
でのスタックアンワインドの停止ロジックも削除されています。これは、元のCL 7301062がruntime·goexit
をスタックの底として扱っていたことの名残であり、その変更が元に戻されたことを示します。
これらの変更は、スタックスキャンとトレースバックのロジックを、元のCL 7301062が導入する前の、より広範で安全な(しかしおそらくは効率が劣る)状態に戻すものです。特にARMアーキテクチャでの安定性を優先した結果と言えます。
コアとなるコードの変更箇所
src/pkg/runtime/mgc0.c
:addframeroots
関数の削除。addstackroots
関数の内部ロジックが、runtime·gentraceback
を使ったフレームごとのスキャンから、Stktop
を使ったスタックセグメント全体の走査に戻された。
src/pkg/runtime/runtime.h
:runtime·gentraceback
関数のプロトタイプ宣言からfn
とarg
引数が削除された。
src/pkg/runtime/traceback_arm.c
:runtime·gentraceback
関数のシグネチャ変更と、内部でのコールバック関数関連ロジックの削除。- スタックアンワインドにおける
runtime·goexit
のチェックの削除。
src/pkg/runtime/traceback_x86.c
:runtime·gentraceback
関数のシグネチャ変更と、内部でのコールバック関数関連ロジックの削除。- スタックアンワインドにおける
runtime·goexit
のチェックの削除。
コアとなるコードの解説
src/pkg/runtime/mgc0.c
におけるaddstackroots
の変更
元のCL 7301062では、addstackroots
関数内でruntime·gentraceback
を呼び出し、addframeroots
というコールバック関数を通じて、各スタックフレームのローカル変数と引数のみをガベージコレクタのルートとして登録しようとしていました。これは、スタックスキャンの精度を高め、不要な領域をスキャンしないことでパフォーマンスを向上させる狙いがあったと考えられます。
しかし、このコミットではそのロジックが元に戻され、Stktop
構造体を利用した従来のスタックセグメント走査方式が採用されています。
// 変更前 (CL 7301062が導入したロジックの簡略化)
// runtime·gentraceback(pc, sp, nil, gp, 0, nil, 0x7fffffff, addframeroots, nil);
// 変更後 (このコミットで元に戻されたロジック)
n = 0;
while(stk) {
// ... (スタックの一貫性チェック) ...
addroot((Obj){sp, (byte*)stk - sp, 0}); // スタックセグメント全体をルートとして追加
sp = (byte*)stk->gobuf.sp;
guard = stk->stackguard;
stk = (Stktop*)stk->stackbase;
n++;
}
この変更により、ガベージコレクタはスタックセグメント全体をルートとしてスキャンするようになります。これは、特定のフレーム内のローカル変数や引数に限定するよりも広範なスキャンであり、ポインタの見落としを防ぐことでGCの正確性を保証します。ARMアーキテクチャでの「broke arm garbage collector」や「invalid freelist」といった問題は、おそらくこの限定的なスキャンが原因で、本来到達可能であるべきオブジェクトへのポインタを見落としてしまったために発生したと考えられます。
runtime·gentraceback
のシグネチャ変更と利用箇所の修正
runtime·gentraceback
は、スタックトレースを生成するための汎用的な関数です。元のCL 7301062では、この関数にfn
(コールバック関数)とarg
(コールバック関数の引数)が追加され、トレースバック中に特定の処理(例えばGCのルート追加)を実行できるように拡張されていました。
このコミットでは、これらの引数が削除され、runtime·gentraceback
は純粋にスタックトレース情報を収集する機能に特化する形に戻されました。
// runtime.h における変更
// 変更前: int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32, void (*fn)(Func*, byte*, byte*, void*), void *arg);
// 変更後: int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32);
この変更は、ガベージコレクタのスタックスキャンロジックと、一般的なスタックトレース生成ロジックを分離することを意味します。これにより、それぞれの機能がより独立し、互いの変更による影響を受けにくくなります。ARMでの問題がruntime·gentraceback
の新しい利用方法に起因していた可能性を考えると、この分離は安定性向上のための重要なステップです。
traceback_arm.c
とtraceback_x86.c
では、runtime·gentraceback
の呼び出し箇所が新しいシグネチャに合わせて修正され、コールバック関数関連のロジックが削除されています。これにより、トレースバック処理が簡素化され、元のCL 7301062が導入した複雑さが取り除かれました。
関連リンク
- 元の変更(CL 7301062):
https://golang.org/cl/7301062
- このコミット(CL 7493044):
https://golang.org/cl/7493044
- 関連する修正(CL 7494043):
https://golang.org/cl/7494043
参考にした情報源リンク
- Goのガベージコレクタに関する一般的な情報源(Web検索結果より)
- Goのガベージコレクタの仕組みに関するドキュメントやブログ記事
- Goランタイムのスタック管理に関する技術解説
- Goのソースコード(このコミットのdiff)
- Goのコードレビューシステム(Gerrit)のCL(Change-List)に関する情報
- ただし、今回のWeb検索では、提供されたCL番号に直接関連する詳細な情報は得られませんでした。