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

[インデックス 1677] ファイルの概要

このコミットは、Go言語のランタイムにおけるビルドスクリプトとメモリ管理コードに対する軽微な修正を含んでいます。具体的には、src/run.bash からコメントアウトされたビルドステップを削除し、src/runtime/malloc.c 内のデバッグ出力におけるprintfのフォーマット指定子を修正しています。

コミット

commit 78a6d68c860b0cef0a089abe93fc43214dfdf2f7
Author: Russ Cox <rsc@golang.org>
Date:   Sun Feb 15 13:41:48 2009 -0800

    build nits
    
    R=r
    DELTA=8  (0 added, 6 deleted, 2 changed)
    OCL=25045
    CL=25045

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/78a6d68c860b0cef0a089abe93fc43214dfdf2f7

元コミット内容

build nits

R=r
DELTA=8  (0 added, 6 deleted, 2 changed)
OCL=25045
CL=25045

変更の背景

このコミットは「build nits」(ビルドに関する些細な修正)と題されており、Go言語の初期開発段階におけるコードベースのクリーンアップとデバッグ出力の改善を目的としています。

src/run.bash からのコメントアウトされたコードの削除は、おそらく不要になった古いビルド手順やテスト手順を整理するためです。コメントアウトされたコードは、将来的に混乱を招いたり、誤って有効化されたりする可能性があるため、不要であれば削除するのが良いプラクティスです。

src/runtime/malloc.c の変更は、メモリ割り当てに関するデバッグ出力の正確性を向上させるためのものです。printf のフォーマット指定子を %d から %D に変更することで、出力される数値の型と表示形式がより適切になるように調整されています。これは、特に異なるアーキテクチャやコンパイラ環境において、数値の表示が正しく行われるようにするための重要な修正です。Go言語のランタイムはC言語で書かれており、C言語のprintfの挙動はプラットフォームやコンパイラによって微妙に異なる場合があるため、このような細かな調整が必要となることがあります。

前提知識の解説

  • Go言語ランタイム: Go言語で書かれたプログラムを実行するために必要な基盤ソフトウェアです。ガベージコレクション、スケジューラ、メモリ管理など、言語の低レベルな機能を提供します。Go言語のランタイムの多くはC言語で書かれています。
  • src/run.bash: Go言語プロジェクトのビルドやテストを実行するためのシェルスクリプトです。Go言語の初期段階では、このようなスクリプトがビルドプロセスを管理していました。
  • src/runtime/malloc.c: Go言語ランタイムのメモリ割り当て(malloc)に関するC言語のソースファイルです。Goのガベージコレクタと連携し、ヒープメモリの管理を行います。
  • printf フォーマット指定子: C言語の標準ライブラリ関数 printf は、指定されたフォーマットに従ってデータを標準出力に出力します。フォーマット指定子(例: %d, %s, %f)は、出力するデータの型と表示形式を指示します。
    • %d: 符号付き10進整数(int型)の出力に使用されます。
    • %D: この指定子は標準C言語のprintfには存在しません。しかし、特定のコンパイラ(例: Plan 9 Cコンパイラ)やカスタムのprintf実装において、long long型やuintptr_t型のような、より大きな整数型を出力するために使用されることがあります。Go言語の初期開発はPlan 9環境で行われていたため、この%DはPlan 9 Cコンパイラの拡張機能である可能性が高いです。これは、uintptruint32といった型が、通常のintでは表現しきれない、あるいはプラットフォーム間でサイズが異なる場合に、安全に表示するための措置と考えられます。
  • mlookup 関数: src/runtime/malloc.c 内の関数で、与えられたポインタがどのメモリ領域(スパン)に属するかを検索し、そのスパンに関する情報(ベースアドレス、サイズ、参照カウンタなど)を返す役割を担っています。これはガベージコレクションやメモリデバッグにおいて重要な機能です。
  • PageShift: メモリページサイズをビットシフトで表現した定数です。例えば、PageShiftが12であれば、ページサイズは1 << 12 = 4096バイト(4KB)となります。
  • RefcountOverhead: 参照カウンタのオーバーヘッドを示す定数です。ガベージコレクションの方式によっては、各オブジェクトに参照カウンタを格納するための追加領域が必要になります。
  • sizeclass: メモリ割り当てにおいて、オブジェクトのサイズを分類するための概念です。Goのメモリ管理では、異なるサイズのオブジェクトに対して効率的な割り当てを行うために、いくつかのサイズクラスが定義されています。
  • nobj: 特定のメモリ領域(スパン)内に収まるオブジェクトの数を示します。
  • throw 関数: Goランタイムにおけるパニック(エラー)発生時に呼び出される関数です。通常、回復不可能なエラーが発生した場合にプログラムを終了させます。

技術的詳細

このコミットは、Go言語のランタイムの安定性とデバッグ可能性を向上させるための、2つの異なる側面からの修正を含んでいます。

  1. src/run.bash のクリーンアップ: 削除された行は、コメントアウトされたシェルスクリプトのブロックです。

    # (xcd ../usr/gri/gosrc
    # make clean
    # time make
    # # make test
    # ) || exit $?
    

    このブロックは、../usr/gri/gosrc ディレクトリでのビルドとテストに関するもので、おそらくGo言語の初期開発における実験的な、あるいは一時的なビルド手順だったと考えられます。xcd は、Goのビルドシステムで使われるカスタムのディレクトリ変更コマンドかもしれません。このコードがコメントアウトされており、もはや必要ないと判断されたため、コードベースの整理の一環として削除されました。これにより、ビルドスクリプトの可読性が向上し、将来的なメンテナンスが容易になります。

  2. src/runtime/malloc.cprintf フォーマット指定子修正: mlookup 関数内のデバッグ出力において、printf のフォーマット指定子が %d から %D に変更されました。

    変更前:

    printf("odd span state=%d span=%p base=%p sizeclass=%d n=%d size=%d npages=%d\n",
    printf("s->base sizeclass %d v=%p base=%p gcref=%p blocksize=%D nobj=%d size=%D end=%p end=%p\n",
    

    変更後:

    printf("odd span state=%d span=%p base=%p sizeclass=%d n=%D size=%D npages=%D\n",
    printf("s->base sizeclass %d v=%p base=%p gcref=%p blocksize=%D nobj=%D size=%D end=%p end=%p\n",
    

    この変更の核心は、%d が通常 int 型の引数に対応するのに対し、%D が Plan 9 C コンパイラにおける long long 型(または uintptr_t のような、より大きな整数型)の引数に対応するという点です。Go言語のランタイムは、メモリ管理において uintptruint32 といった符号なし整数型を多用します。これらの型は、システムによっては int の範囲を超える可能性があり、また int とは異なるサイズを持つことがあります。

    具体的に変更された引数は以下の通りです:

    • nobj: スパン内のオブジェクト数。これは uint32 または uintptr に相当する可能性があり、%D で出力することで正確性が保証されます。
    • n: オブジェクトのサイズ。これも同様に uint32uintptr の可能性があります。
    • s->npages: スパンが占めるページ数。これも uint32uintptr の可能性があります。

    これらの変数が int よりも大きい型である場合、%d を使用すると、値が切り捨てられたり、未定義の動作を引き起こしたりする可能性があります。%D を使用することで、これらの変数の完全な値が正しくデバッグ出力に表示されるようになり、メモリ管理のデバッグがより信頼性の高いものになります。これは、特に異なるアーキテクチャ(32ビットと64ビットなど)での移植性を考慮した修正でもあります。

コアとなるコードの変更箇所

src/run.bash

--- a/src/run.bash
+++ b/src/run.bash
@@ -52,12 +52,6 @@ time make
 make smoketest
 ) || exit $?
 
-# (xcd ../usr/gri/gosrc
-# make clean
-# time make
-# # make test
-# ) || exit $?
-
 (xcd ../doc/progs
 time ./run
 ) || exit $?

src/runtime/malloc.c

--- a/src/runtime/malloc.c
+++ b/src/runtime/malloc.c
@@ -162,9 +162,9 @@ mlookup(void *v, byte **base, uintptr *size, uint32 **ref)\n 		*size = n;\n 		nobj = (s->npages << PageShift) / (n + RefcountOverhead);\n 		if((byte*)s->gcref < p || (byte*)(s->gcref+nobj) > p+(s->npages<<PageShift)) {\n-\t\tprintf(\"odd span state=%d span=%p base=%p sizeclass=%d n=%d size=%d npages=%d\\n\",\n+\t\tprintf(\"odd span state=%d span=%p base=%p sizeclass=%d n=%D size=%D npages=%D\\n\",\n \t\t\ts->state, s, p, s->sizeclass, nobj, n, s->npages);\n-\t\tprintf(\"s->base sizeclass %d v=%p base=%p gcref=%p blocksize=%D nobj=%d size=%D end=%p end=%p\\n\",\n+\t\tprintf(\"s->base sizeclass %d v=%p base=%p gcref=%p blocksize=%D nobj=%D size=%D end=%p end=%p\\n\",\n \t\t\ts->sizeclass, v, p, s->gcref, s->npages<<PageShift,\n \t\t\tnobj, n, s->gcref + nobj, p+(s->npages<<PageShift));\n \t\tthrow(\"bad gcref\");\n```

## コアとなるコードの解説

### `src/run.bash` の変更

この変更は、単にコメントアウトされたコードブロックを削除するものです。これは機能的な変更ではなく、コードベースの整理とクリーンアップを目的としています。削除されたコードは、Go言語の初期開発中に使用された可能性のある、古いまたは実験的なビルド/テスト手順を示唆しています。

### `src/runtime/malloc.c` の変更

`mlookup` 関数は、メモリ割り当てスパンの整合性をチェックする際にデバッグ情報を出力します。このコミットでは、その `printf` ステートメント内のフォーマット指定子が変更されています。

元のコードでは、`nobj`, `n`, `s->npages` といった変数を `%d` で出力していました。これらの変数は、メモリ管理のコンテキストでは `uintptr` や `uint32` のような符号なし整数型、またはプラットフォーム依存のサイズを持つ整数型である可能性が高いです。

変更後のコードでは、これらの変数の出力に `%D` を使用しています。これは、Plan 9 C コンパイラにおける `long long` 型のフォーマット指定子として機能します。これにより、これらの変数が `int` 型の範囲を超える値を持つ場合でも、正確にデバッグ出力に表示されるようになります。

例えば、`nobj` は `(s->npages << PageShift) / (n + RefcountOverhead)` の計算結果であり、これは割り当てられたメモリブロックの総サイズをオブジェクトのサイズで割ったものです。メモリサイズが大きくなると、`nobj` の値も大きくなり、`int` の最大値を超える可能性があります。同様に、`n` (オブジェクトサイズ) や `s->npages` (ページ数) も大きな値を取り得ます。

この修正は、Goランタイムのメモリ管理におけるデバッグ情報の信頼性を高め、特に異なるシステムアーキテクチャ間での移植性やデバッグの正確性を確保するために重要です。`throw("bad gcref");` の行は、`gcref`(ガベージコレクション参照)の状態が不正であると判断された場合にパニックを引き起こすことを示しており、`printf` の出力はそのパニックに至る前の診断情報として利用されます。

## 関連リンク

*   Go言語のメモリ管理に関する公式ドキュメント(現在のバージョン): [https://go.dev/doc/effective_go#allocation_with_make](https://go.dev/doc/effective_go#allocation_with_make)
*   Plan 9 C コンパイラの `printf` フォーマットに関する情報(一般的なC言語の`printf`とは異なる場合があるため、参考程度に): [https://9p.io/sys/doc/compiler.html](https://9p.io/sys/doc/compiler.html)

## 参考にした情報源リンク

*   Go言語のソースコード(GitHub): [https://github.com/golang/go](https://github.com/golang/go)
*   C言語 `printf` フォーマット指定子に関する一般的な情報: [https://en.cppreference.com/w/c/io/fprintf](https://en.cppreference.com/w/c/io/fprintf)
*   Plan 9 from Bell Labs: [https://9p.io/plan9/](https://9p.io/plan9/)
*   Go言語の初期開発に関する歴史的情報(Russ Coxのブログなど)