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

[インデックス 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 の差分を取り、その操作によって消費されたスタックメモリ量を測定しようとしていました。ここで s0s1 は、それぞれ操作前と操作後のランタイムの状態をキャプチャしたスナップショットのようなものと推測されます。

しかし、コミットメッセージに記載されているように、「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.StackInuses0.StackInuseint64 にキャストしています。

inuse := int64(s1.StackInuse) - int64(s0.StackInuse)

int64 は符号付き整数型であるため、s1.StackInuses0.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.StackInuses0.StackInuseuintptr 型(符号なし整数型)であると推測されます。
    • uintptr 型での減算は、s1.StackInuses0.StackInuse より小さい場合にアンダーフローを引き起こし、非常に大きな正の値になる可能性があります。これがテスト失敗の原因でした。
  • 変更後: inuse := int64(s1.StackInuse) - int64(s0.StackInuse)

    • s1.StackInuses0.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トラッカー (過去のスタック関連のバグ報告など)