Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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")につながったため、その変更をロールバックしています。

具体的な変更点は以下の通りです。

  1. addframeroots関数の削除: src/pkg/runtime/mgc0.cからaddframeroots関数が完全に削除されました。この関数は、特定のフレームのローカル変数と引数をルートとして追加するために使用されていたと考えられます。この削除は、元のCL 7301062が導入したスタックルートスキャンの制限を元に戻すことを意味します。

  2. addstackroots関数のロジックの変更: src/pkg/runtime/mgc0.c内のaddstackroots関数は、スタックをスキャンしてルートを見つけるための主要な関数です。このコミットでは、元のCL 7301062が導入した複雑なロジック(特にruntime·gentracebackを呼び出してフレームごとのルートを追加する部分)が削除され、よりシンプルで以前のスタックセグメントベースの走査ロジックに戻されています。

    • 変更前はruntime·gentracebackを呼び出し、addframerootsコールバック関数を使ってフレームごとのルートを追加していました。
    • 変更後は、Stktop構造体を使ってスタックセグメントを順にたどり、各セグメントの範囲全体をルートとして追加する方式に戻っています。これにより、スタック上のすべてのポインタが確実にスキャンされるようになります。
    • gp->gcstack, gp->gcsp, gp->gcguardといったシステムコール中のスタック情報を使用するロジックも、より堅牢な形に戻されています。
  3. runtime·gentraceback関数のシグネチャ変更: src/pkg/runtime/runtime.hsrc/pkg/runtime/mprof.gocsrc/pkg/runtime/proc.csrc/pkg/runtime/traceback_arm.csrc/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); fnargという2つの引数(関数ポインタとその引数)が削除されています。これは、runtime·gentracebackがスタックフレームごとに特定のコールバック関数を呼び出すという、元のCL 7301062で導入されたメカニズムが廃止されたことを意味します。これにより、トレースバックの汎用性が高まり、ガベージコレクタのスタックスキャンとは独立した機能として扱われるようになります。
  4. traceback_arm.ctraceback_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関数のプロトタイプ宣言からfnarg引数が削除された。
  • 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.ctraceback_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番号に直接関連する詳細な情報は得られませんでした。