[インデックス 18839] ファイルの概要
このコミットは、Goランタイムのスタックテスト TestStackMem
における一時的な変更を導入しています。具体的には、スタックメモリ使用量の計算方法を調整し、既存のバグ(Go issue #7468として参照されているが、現在はアクセス不可または誤記の可能性あり)によるテストの失敗を回避するためのものです。この変更は、スタックメモリの正確なアカウンティングに関する根本的な問題が解決されるまでの暫定的な措置として行われました。
コミット
commit 00e6fc1e9e709a5c29b79d9aed8ae135cd54a445
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Wed Mar 12 10:20:58 2014 +0400
runtime: temporary weaken a check in test
Currently the test fails as:
$ go test -v -cpu 1,1,1,1 runtime -test.run=TestStack
stack_test.go:1584: Stack inuse: want 4194304, got 18446744073709547520
Update #7468
LGTM=rsc
R=golang-codereviews, bradfitz
CC=golang-codereviews, khr, rsc
https://golang.org/cl/74010043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/00e6fc1e9e709a5c29b79d9aed8ae135cd54a445
元コミット内容
runtime: temporary weaken a check in test
Currently the test fails as:
$ go test -v -cpu 1,1,1,1 runtime -test.run=TestStack
stack_test.go:1584: Stack inuse: want 4194304, got 18446744073709547520
Update #7468
LGTM=rsc
R=golang-codereviews, bradfitz
CC=golang-codereviews, khr, rsc
https://golang.org/cl/74010043
変更の背景
このコミットは、Goランタイムのスタックテスト TestStackMem
が特定の条件下で失敗するという問題に対処するために作成されました。テストの失敗メッセージ Stack inuse: want 4194304, got 18446744073709547520
が示すように、期待されるスタック使用量と実際のスタック使用量との間に大きな乖離がありました。コミットメッセージには「Due to broken stack memory accounting (http://golang.org/issue/7468)」と明記されており、これはスタックメモリのアカウンティング(使用量の正確な追跡)にバグが存在することを示唆しています。
このバグは、関数実行中に StackInuse
の値が減少する可能性があり、その結果、スタック使用量の計算が負の値になったり、非常に大きな正の値になったりする可能性がありました。テストがこの不正な計算結果によって失敗するのを防ぐため、根本的なアカウンティングの問題が解決されるまでの間、一時的にテストのチェックを緩和する必要がありました。
前提知識の解説
Goランタイムとスタック管理
Go言語は、独自のランタイムシステムを持っています。このランタイムは、ガベージコレクション、スケジューリング、そしてメモリ管理(ヒープとスタックを含む)といった低レベルの操作を処理します。Goのスタックは、他の多くの言語とは異なり、固定サイズではなく、必要に応じて動的に拡張・縮小します。これは「goroutine stack growth」として知られる機能で、小さなスタックでgoroutineを開始し、関数呼び出しが深くなるにつれてスタックを拡張し、関数から戻るとスタックを縮小することで、メモリ効率を高めます。
スタックメモリのアカウンティング
スタックメモリのアカウンティングとは、Goランタイムが各goroutineが現在使用しているスタックメモリの量を正確に追跡するメカニズムを指します。これには、スタックの割り当て、解放、および現在の使用状況の監視が含まれます。正確なアカウンティングは、メモリリークの検出、メモリ使用量の最適化、およびデバッグにおいて非常に重要です。
runtime.StackInuse
runtime.StackInuse
は、Goランタイムが内部的に管理するスタックメモリの使用量を示す値であると推測されます。この値は、特定の時点でのスタックの「in use」状態、つまり現在アクティブに使用されているメモリの量をバイト単位で表すことを意図しています。
TestStackMem
TestStackMem
は、Goランタイムのスタック管理機能の正確性を検証するためのテスト関数です。この種のテストは、特定の操作(例えば、関数呼び出しやデータ構造の割り当て)がスタックメモリに与える影響を測定し、その結果が期待される範囲内にあることを確認します。テストが失敗するということは、ランタイムのスタック管理ロジックに予期せぬ動作があることを示します。
技術的詳細
このコミットの技術的詳細は、src/pkg/runtime/stack_test.go
ファイル内の TestStackMem
関数に対する変更に集約されます。
元のコードでは、s1.StackInuse - s0.StackInuse
という計算で、ある操作の前後の StackInuse
の差分を取り、その操作によって消費されたスタックメモリ量を測定しようとしていました。ここで s0
と s1
は、それぞれ操作前と操作後のランタイムの状態をキャプチャしたスナップショットのようなものと推測されます。
しかし、コミットメッセージに記載されているように、「Due to broken stack memory accounting (http://golang.org/issue/7468), StackInuse can decrease during function execution」。これは、スタックメモリのアカウンティングのバグにより、関数実行中に StackInuse
の値が実際に減少する可能性があることを意味します。通常、関数が実行され、ローカル変数や引数がスタックにプッシュされると、スタック使用量は増加するはずです。しかし、このバグにより、何らかの理由で StackInuse
が不正確に報告され、場合によっては減少したかのように見えてしまうのです。
この問題が発生すると、s1.StackInuse - s0.StackInuse
の計算結果が負の値になる可能性があります。Goの uintptr
型(ポインタを保持できる符号なし整数型)で表現される StackInuse
のような値の場合、負の値は非常に大きな正の値として解釈されることがあります(アンダーフロー)。テストの失敗メッセージ got 18446744073709547520
は、まさにこのアンダーフローが発生し、期待される値 4194304
(4MB) とはかけ離れた巨大な値が報告されたことを示しています。
この問題を回避するため、コミットでは s1.StackInuse
と s0.StackInuse
を int64
にキャストしています。
inuse := int64(s1.StackInuse) - int64(s0.StackInuse)
int64
は符号付き整数型であるため、s1.StackInuse
が s0.StackInuse
より小さい場合でも、計算結果は正確な負の値として表現されます。これにより、アンダーフローによる巨大な正の値の発生を防ぎ、テストが不正な値で失敗するのを一時的に回避しています。この変更は、根本的なアカウンティングのバグを修正するものではなく、そのバグによるテストの失敗をマスクするための暫定的な措置です。
コアとなるコードの変更箇所
src/pkg/runtime/stack_test.go
ファイルの以下の行が変更されました。
--- a/src/pkg/runtime/stack_test.go
+++ b/src/pkg/runtime/stack_test.go
@@ -1576,7 +1576,9 @@ func TestStackMem(t *testing.T) {
if consumed > estimate {
t.Fatalf("Stack mem: want %v, got %v", estimate, consumed)
}
- inuse := s1.StackInuse - s0.StackInuse
+ // Due to broken stack memory accounting (http://golang.org/issue/7468),
+ // StackInuse can decrease during function execution, so we cast the values to int64.
+ inuse := int64(s1.StackInuse) - int64(s0.StackInuse)
t.Logf("Inuse %vMB for stack mem", inuse>>20)
if inuse > 4<<20 {
t.Fatalf("Stack inuse: want %v, got %v", 4<<20, inuse)
コアとなるコードの解説
変更された行は、inuse
変数の計算方法です。
-
変更前:
inuse := s1.StackInuse - s0.StackInuse
s1.StackInuse
とs0.StackInuse
はuintptr
型(符号なし整数型)であると推測されます。uintptr
型での減算は、s1.StackInuse
がs0.StackInuse
より小さい場合にアンダーフローを引き起こし、非常に大きな正の値になる可能性があります。これがテスト失敗の原因でした。
-
変更後:
inuse := int64(s1.StackInuse) - int64(s0.StackInuse)
s1.StackInuse
とs0.StackInuse
の値をそれぞれint64
型(符号付き64ビット整数型)に明示的にキャストしています。int64
にキャストすることで、減算結果が負の値になる場合でも、それが正確な負の値として保持されます。これにより、アンダーフローによる不正な巨大な値の発生を防ぎ、テストが期待される範囲内で動作するようにしています。- コメント
// Due to broken stack memory accounting (http://golang.org/issue/7468), // StackInuse can decrease during function execution, so we cast the values to int64.
が追加され、この変更がスタックメモリのアカウンティングのバグに対する一時的な回避策であることを明確に示しています。
この変更は、テストが一時的にパスするようにするためのものであり、スタックメモリのアカウンティングの根本的なバグ自体を修正するものではありません。根本的な問題は、後続のコミットで解決されることが期待されます。
関連リンク
- Go issue #7468: コミットメッセージで参照されているが、現在のところアクセスできないか、誤記の可能性があります。
- Go CL 74010043: このコミットに対応するGoのコードレビューシステム(Gerrit)上のチェンジリスト。
参考にした情報源リンク
- Go言語の公式ドキュメント (Go runtime, stack management)
- Go言語のソースコード (特に
src/pkg/runtime/
ディレクトリ内の関連ファイル) - Go言語のIssueトラッカー (過去のスタック関連のバグ報告など)