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

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

このコミットは、Go言語のリンカ (cmd/ld) において、-v (verbose) フラグの出力内容を改善し、特に pclntabfuncdata のサイズを報告するように変更するものです。また、詳細なデバッグ出力 (addlib, autolib, ldobj) を -v -v (より詳細なverbose) に移動させることで、通常の -v 出力をより簡潔にしています。

コミット

commit b99fa8155514d4a5dad366dde9be8ae76333e6a9
Author: Russ Cox <rsc@golang.org>
Date:   Thu Aug 1 12:58:27 2013 -0400

    cmd/ld: report pclntab, funcdata sizes in 6l -v output
    Also move chatty recent additions to -v -v.
    
    For what it's worth:
    
    $ go build -o /dev/null -ldflags -v cmd/go
    ...
     0.87 pclntab=1110836 bytes, funcdata total 69700 bytes
    ...
    $
    
    This broke the ELF builds last time because I tried to dedup
    the funcdata in case the same funcdata was pointed at by
    multiple functions. That doesn't currently happen, so I've
    removed that test.
    
    If we start doing bitmap coalescing we'll need to figure out
    how to measure the size more carefully, but I think at that
    point the bitmaps will be an extra indirection away from the
    funcdata anyway, so the dedup I used before wouldn't help.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/12269043

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

https://github.com/golang/go/commit/b99fa8155514d4a5dad366dde9be8ae76333e6a9

元コミット内容

cmd/ld: 6l -v 出力で pclntabfuncdata のサイズを報告するように変更。 最近追加された冗長な出力を -v -v に移動。

参考までに:

$ go build -o /dev/null -ldflags -v cmd/go
...
 0.87 pclntab=1110836 bytes, funcdata total 69700 bytes
...
$

以前、同じ funcdata が複数の関数から参照される場合に funcdata の重複排除を試みたため、ELFビルドが壊れた。現在はそのようなことは起こらないため、そのテストを削除した。

もしビットマップの結合を開始するならば、サイズの測定方法をより慎重に検討する必要があるだろうが、その時点ではビットマップは funcdata からさらに間接的な参照になるため、以前使用した重複排除は役に立たないだろう。

変更の背景

このコミットの主な背景は、Goプログラムのバイナリサイズに関するリンカの出力情報を改善することにあります。特に、Goのランタイムが使用する重要なデータ構造である pclntab (PC-line table) と funcdata (function data) のサイズを、リンカの冗長出力 (-v フラグ) に含めることで、開発者がバイナリの構成をより深く理解し、最適化の機会を見つけやすくすることが目的です。

以前のリンカの -v 出力は、addlibldobj といったリンキングプロセスの詳細なステップに関する情報が多すぎ、本当に重要な情報が埋もれてしまう傾向がありました。このコミットでは、これらの冗長な出力をより詳細なデバッグレベル (-v -v) に移動させることで、通常の -v 出力をよりクリーンで、かつ pclntabfuncdata のサイズという新しい重要な情報を含むように再構成しています。

また、コミットメッセージには、過去に funcdata の重複排除を試みた際にELFビルドが失敗した経緯が述べられています。これは、リンカがバイナリを生成する際に、データ構造の配置や最適化が複雑であることを示唆しています。このコミットでは、現状では funcdata の重複は発生しないため、以前の重複排除の試みに関連するコード(おそらくサイズ測定のロジック)を削除しています。これは、リンカのコードを簡素化し、現在のGoランタイムの挙動に合わせた調整と言えます。

将来的な「ビットマップ結合 (bitmap coalescing)」の可能性についても言及されており、その際には funcdata のサイズ測定方法を再考する必要があるという洞察も含まれています。これは、Goランタイムの進化を見据えた、長期的な視点でのリンカの改善を示唆しています。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびリンカに関する前提知識が必要です。

  1. Go言語のコンパイルとリンキング:

    • Goのソースコードは、まずコンパイラによってオブジェクトファイル(.o ファイル)にコンパイルされます。
    • その後、リンカ (cmd/ld) がこれらのオブジェクトファイルと必要なライブラリ(Goランタイム、標準ライブラリなど)を結合して、実行可能なバイナリを生成します。
    • リンキングの過程で、Goランタイムが必要とする様々なメタデータがバイナリに埋め込まれます。
  2. cmd/ld (Goリンカ):

    • Go言語の公式リンカです。Goのツールチェインの一部として提供されます。
    • go build コマンドの内部で呼び出され、最終的な実行ファイルを生成します。
    • -ldflags オプションを通じてリンカにフラグを渡すことができます。例えば、-ldflags -v はリンカの冗長出力を有効にします。
  3. pclntab (PC-line table):

    • "Program Counter to Line table" の略で、Goバイナリに埋め込まれる重要なデータ構造の一つです。
    • 実行中のプログラムカウンタ (PC) の値から、対応するソースコードのファイル名と行番号をマッピングするために使用されます。
    • 主に、スタックトレースの生成、プロファイリング、デバッグなどの目的でGoランタイムによって利用されます。
    • このテーブルは、Goプログラムのデバッグ可能性と可観測性を支える基盤となります。
  4. funcdata (Function Data):

    • Goバイナリに埋め込まれる、関数ごとの追加データです。
    • 各関数に関連付けられた特定の情報(例: ガベージコレクションのためのポインタマップ、スタックフレーム情報など)を格納します。
    • funcdata は、Goのガベージコレクタが正確に動作するために不可欠な情報を提供します。例えば、スタック上のどの位置にポインタがあるかをGCが知るために使用されます。
  5. -v フラグ (Verbose Output):

    • 多くのコマンドラインツールで一般的に使用されるフラグで、詳細な情報を出力するために使われます。
    • Goのリンカ (cmd/ld) における -v フラグは、リンキングプロセスの各ステップに関する情報や、最終的なバイナリの構成に関する統計情報などを表示します。
    • -v -v のように複数回指定することで、さらに詳細なデバッグ情報を表示する慣習があります。
  6. ELF (Executable and Linkable Format):

    • Unix系システム(Linuxなど)で広く使用されている、実行可能ファイル、オブジェクトファイル、共有ライブラリの標準ファイルフォーマットです。
    • Goのリンカは、LinuxなどのシステムでELF形式のバイナリを生成します。
    • コミットメッセージで「ELF builds last time」とあるのは、ELF形式のバイナリを生成する際に問題が発生したことを指しています。
  7. 重複排除 (Dedup):

    • データストレージや転送において、重複するデータを識別し、そのコピーを削除または単一の参照に置き換えるプロセスです。
    • このコミットの文脈では、リンカが funcdata のようなデータ構造をバイナリに埋め込む際に、同じ内容のデータが複数存在する場合にそれらを一つにまとめることで、バイナリサイズを削減しようとする試みを指します。
  8. ビットマップ結合 (Bitmap Coalescing):

    • コミットメッセージで言及されている将来的な最適化の可能性です。
    • Goのガベージコレクションに関連する概念で、メモリ上のポインタ情報を効率的に表現するためのビットマップを、より効率的に結合・管理する技術を指す可能性があります。これにより、GCの性能向上やメモリ使用量の削減が期待されます。

これらの概念を理解することで、コミットがGoバイナリの内部構造、リンキングプロセス、およびデバッグ・最適化の側面に対してどのような影響を与えるかが明確になります。

技術的詳細

このコミットは、Goリンカ (cmd/ld) の出力と内部ロジックにいくつかの重要な変更を加えています。

  1. -v 出力の粒度調整:

    • 以前は、addlib (ライブラリの追加)、autolib (自動ライブラリ解決)、ldobj (オブジェクトファイルのロード) といったリンキングプロセスの詳細なステップに関する情報が、通常の -v フラグで出力されていました。
    • このコミットでは、これらの出力の条件を debug['v'] から debug['v'] > 1 に変更しています。これは、リンカの内部デバッグフラグ v の値が2以上の場合にのみこれらのメッセージが出力されることを意味します。
    • 結果として、通常の go build -ldflags -v の出力はより簡潔になり、本当に重要な情報(後述の pclntabfuncdata のサイズ)が目立つようになります。より詳細なデバッグが必要な場合は、go build -ldflags "-v -v" のように -v を二回指定することで、これらの冗長なメッセージを表示できます。
  2. pclntabfuncdata サイズの報告:

    • pclntab はGoバイナリ内のPC-lineテーブルであり、funcdata は関数ごとのメタデータです。これらはGoランタイムのデバッグ、プロファイリング、ガベージコレクションに不可欠な情報を含んでいます。
    • このコミットでは、pclntab 関数内で funcdata_bytes という新しい変数を導入し、各関数の funcdata のサイズを合計するように変更しています。
    • pclntab の構築が完了した後、debug['v'] が有効な場合に、pclntab の最終的なサイズ (ftab->size) と funcdata_bytes の合計を Bprint 関数で標準出力に報告するように変更されました。
    • これにより、開発者はGoバイナリがこれらの重要なランタイムメタデータにどれだけのスペースを割り当てているかを直接確認できるようになります。これは、バイナリサイズの最適化や、特定のGoバージョンでのバイナリサイズの変動を分析する際に非常に有用な情報となります。
  3. funcdata 重複排除ロジックの削除と背景:

    • コミットメッセージには、過去に funcdata の重複排除を試みた際にELFビルドが壊れたという重要な情報が含まれています。
    • 以前のコードでは、p->to.sym->sizefuncdata_bytes に加算する際に、// TODO: Dedup. というコメントがあり、p->to.type == D_CONST でない場合に setaddrplus を呼び出す前に funcdata_bytes を加算していました。
    • このコミットでは、funcdata_bytes += p->to.sym->size; の行が else { ... } ブロック内に移動し、// TODO: Dedup. コメントが残されていますが、実質的に以前の重複排除の試みに関連するロジック(おそらく p->to.type == D_CONST でない場合の funcdata のサイズ計算方法)が簡素化されています。
    • コミットメッセージによると、現状では同じ funcdata が複数の関数から参照されることはないため、重複排除のテストやロジックは不要と判断されました。これにより、リンカのコードが簡素化され、現在のGoランタイムの挙動に合わせた調整が行われています。
    • この変更は、リンカの複雑性を軽減し、現在のGoの設計に合致させるための実用的な判断です。
  4. 将来の考慮事項:

    • コミットメッセージでは、「ビットマップ結合 (bitmap coalescing)」が将来的に導入された場合、funcdata のサイズ測定方法を再考する必要があることが示唆されています。
    • これは、Goのガベージコレクタが進化し、メモリ上のポインタ情報を表現する方法が変わる可能性があることを示しています。もしビットマップが funcdata からさらに間接的な参照になる場合、現在の funcdata のサイズ測定方法では不十分になる可能性があるという洞察です。
    • この言及は、Goランタイムとリンカの開発が継続的に行われ、将来の最適化を見据えていることを示しています。

これらの変更は、Goリンカのデバッグ出力のユーザビリティを向上させるとともに、Goバイナリの内部構造に関する透明性を高め、将来のランタイム最適化への道筋を示しています。

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

変更は src/cmd/ld/lib.c ファイルに集中しています。

--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -231,7 +231,7 @@ addlib(char *src, char *obj)
 		*p = '\0';
 
-	if(debug['v'])
+	if(debug['v'] > 1)
 		Bprint(&bso, "%5.2f addlib: %s %s pulls in %s\n", cputime(), obj, src, pname);
 
 	addlibpath(src, obj, pname, name);
@@ -330,7 +330,7 @@ loadlib(void)
 	}
 
 	for(i=0; i<libraryp; i++) {
-		if(debug['v'])
+		if(debug['v'] > 1)
 			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i].file, library[i].objref);
 		iscgo |= strcmp(library[i].pkg, "runtime/cgo") == 0;
 		objfile(library[i].file, library[i].pkg);
@@ -433,7 +433,7 @@ objfile(char *file, char *pkg)
 
 	pkg = smprint("%i", pkg);
 
-	if(debug['v'])
+	if(debug['v'] > 1)
 		Bprint(&bso, "%5.2f ldobj: %s (%s)\n", cputime(), file, pkg);
 	Bflush(&bso);
 	f = Bopen(file, 0);
@@ -2049,7 +2049,7 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
 		}
 	}
 	if(debug['v'] || debug['n'])
-		Bprint(&bso, "symsize = %ud\n", symsize);
+		Bprint(&bso, "%5.2f symsize = %ud\n", cputime(), symsize);
 	Bflush(&bso);
 }
 
@@ -2356,7 +2356,9 @@ pclntab(void)
 	uint32 *havepc, *havefunc;
 	Sym *ftab, *s;\n
 	int32 npcdata, nfuncdata, off, end;
+\tint64 funcdata_bytes;
 	
+\tfuncdata_bytes = 0;
 	ftab = lookup("pclntab", 0);
 	ftab->type = SPCLNTAB;
 	ftab->reachable = 1;
@@ -2478,8 +2480,11 @@ pclntab(void)
 				i = p->from.offset;
 				if(p->to.type == D_CONST)
 					setuintxx(ftab, off+PtrSize*i, p->to.offset, PtrSize);
-					else
+					else {
+						// TODO: Dedup.
+						funcdata_bytes += p->to.sym->size;
 					setaddrplus(ftab, off+PtrSize*i, p->to.sym, p->to.offset);
+					}
 				}
 			}
 		off += nfuncdata*PtrSize;
@@ -2506,4 +2511,7 @@ pclntab(void)
 		setuint32(ftab, start + s->value*4, ftabaddstring(ftab, s->name));
 
 	ftab->size = ftab->np;
+\t
+\tif(debug['v'])\n
+\t\tBprint(&bso, "%5.2f pclntab=%lld bytes, funcdata total %lld bytes\\n", cputime(), (vlong)ftab->size, (vlong)funcdata_bytes);
 }

コアとなるコードの解説

このコミットの主要な変更は、リンカの冗長出力の制御と、pclntab および funcdata のサイズ報告機能の追加にあります。

  1. 冗長出力の粒度変更 (debug['v'] から debug['v'] > 1):

    • src/cmd/ld/lib.caddlibloadlib (内部の autolib 出力)、objfile 関数内の Bprint 呼び出しの条件が、if(debug['v']) から if(debug['v'] > 1) に変更されています。
    • これは、これらのリンキングプロセスの詳細なステップに関するメッセージが、通常の -v フラグ(debug['v'] が1の場合)では出力されなくなり、-v -v のように -v フラグを複数回指定した場合(debug['v'] が2以上の場合)にのみ出力されるように変更されたことを意味します。
    • これにより、通常の -v 出力はより簡潔になり、本当に重要な情報に焦点を当てることができます。
  2. symsize 出力へのタイムスタンプ追加:

    • genasmsym 関数内の symsize の出力行が変更され、Bprint(&bso, "symsize = %ud\\n", symsize); から Bprint(&bso, "%5.2f symsize = %ud\\n", cputime(), symsize); になっています。
    • これにより、symsize の出力にも cputime() によるタイムスタンプが追加され、他の冗長出力と同様に、その情報が出力されたタイミングがわかるようになりました。
  3. pclntabfuncdata サイズの計算と報告:

    • pclntab 関数内に、int64 funcdata_bytes; という新しい変数が宣言され、funcdata_bytes = 0; で初期化されています。これは、すべての関数の funcdata の合計サイズを追跡するためのものです。
    • pclntab の構築ループ内で、p->to.type == D_CONST でない場合(つまり、funcdata がシンボルへの参照である場合)に、funcdata_bytes += p->to.sym->size; の行が追加されています。これは、各 funcdata シンボルのサイズを funcdata_bytes に加算することで、合計サイズを計算しています。
      • 注目すべきは、この行が else { ... } ブロック内に移動し、// TODO: Dedup. というコメントが残されている点です。これは、以前の重複排除の試みに関連するロジックが簡素化されたことを示唆しています。コミットメッセージにあるように、現状では funcdata の重複は発生しないため、この単純な加算で十分と判断されています。
    • pclntab 関数の最後に、新しい Bprint 呼び出しが追加されています。
      if(debug['v'])
          Bprint(&bso, "%5.2f pclntab=%lld bytes, funcdata total %lld bytes\\n", cputime(), (vlong)ftab->size, (vlong)funcdata_bytes);
      
      この行は、debug['v'] が有効な場合に、pclntab の最終的なサイズ (ftab->size) と、計算された funcdata_bytes の合計を、タイムスタンプとともに標準出力に報告します。

これらの変更により、Goリンカの -v 出力は、開発者にとってより有用で、バイナリのメモリフットプリントを理解するための重要な情報を提供するようになりました。

関連リンク

参考にした情報源リンク