[インデックス 18196] ファイルの概要
このコミットは、Go言語のツールチェインに含まれる cmd/nm
コマンドに、シンボルのサイズでソートする機能 (-sort=size
) を追加するものです。これにより、実行可能ファイルやライブラリ内の大きなシンボル(関数や変数など)を効率的に特定できるようになります。
コミット
commit fca453e062a39f64b0760aa4555bb974f504aba3
Author: Rob Pike <r@golang.org>
Date: Wed Jan 8 15:56:40 2014 -0800
cmd/nm: add -sort=size
When printing the size, we often want to sort on that key.
Because it's used when looking for large things, make the
sort go from largest to smallest.
Perfect recreation of CL 45150044, which was lost to some blunder.
R=golang-codereviews, gobot, rsc
CC=golang-codereviews
https://golang.org/cl/48500044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fca453e062a39f64b0760aa4555bb974f504aba3
元コミット内容
cmd/nm: add -sort=size
When printing the size, we often want to sort on that key.
Because it's used when looking for large things, make the
sort go from largest to smallest.
Perfect recreation of CL 45150044, which was lost to some blunder.
変更の背景
このコミットの背景には、cmd/nm
コマンドの利便性向上が挙げられます。nm
コマンドは、コンパイルされたオブジェクトファイルや実行可能ファイル内のシンボル(関数、変数など)の情報を表示するために使用されます。既存の nm
コマンドには、シンボルのアドレス (-sort=address
) や名前 (-sort=name
) でソートする機能がありましたが、シンボルのサイズでソートする機能は存在しませんでした。
開発者がプログラムのパフォーマンス最適化やメモリ使用量の分析を行う際、特に大きなバイナリサイズを持つシンボルを特定することは非常に重要です。例えば、予期せず大きなサイズを持つ関数やグローバル変数が存在する場合、それがパフォーマンスのボトルネックやメモリリークの原因となっている可能性があります。このような状況で、シンボルをサイズ順にソートできる機能は、問題の特定とデバッグを大幅に効率化します。
コミットメッセージにある「Perfect recreation of CL 45150044, which was lost to some blunder.」という記述は、この機能が以前にも実装されようとしたが、何らかの理由(おそらくバージョン管理システム上の問題や誤操作)で失われてしまったことを示唆しています。これは、この機能の必要性が以前から認識されており、再度の実装が強く望まれていたことを意味します。Rob Pike氏がこのコミットを行ったことは、Go言語のコア開発チームがこの機能の重要性を認識していた証拠と言えるでしょう。
前提知識の解説
1. cmd/nm
コマンド
nm
は "name list" の略で、Unix系のシステムで広く使われているコマンドラインツールです。コンパイルされたオブジェクトファイル、共有ライブラリ、または実行可能ファイル内のシンボルテーブルの内容を表示するために使用されます。シンボルテーブルには、関数名、変数名、それらのアドレス、サイズ、型などの情報が含まれています。
Go言語の cmd/nm
は、Goのバイナリに特化してシンボル情報を抽出するツールです。Goのコンパイラとリンカが生成するバイナリ形式を理解し、Go特有のシンボル(例えば、Goの関数やメソッド、グローバル変数など)を正確に解析して表示します。
nm
コマンドの一般的な出力形式は以下のようになります。
アドレス T シンボル名
- アドレス: シンボルがメモリにロードされるアドレス。
- T: シンボルの型(例:
T
はテキストセクションのグローバルシンボル、D
はデータセクションのグローバルシンボル、U
は未定義シンボルなど)。 - シンボル名: 関数名や変数名。
このコミットで追加された -size
オプションを使用すると、シンボルのサイズも表示されるようになります。
2. シンボルテーブル
シンボルテーブルは、コンパイラやアセンブラが生成するオブジェクトファイルや実行可能ファイル内に含まれるデータ構造です。このテーブルは、プログラム内の識別子(変数名、関数名など)と、それらがメモリ内でどこに配置されているか(アドレス)や、そのサイズ、型などの関連情報とのマッピングを保持します。リンカは、複数のオブジェクトファイルを結合する際に、このシンボルテーブルを参照して、未解決のシンボル参照を解決します。デバッガもまた、シンボルテーブルを利用して、ソースコードの変数名や関数名と実行時のメモリ位置を関連付け、人間が理解しやすい形でデバッグ情報を提供します。
3. Go言語におけるソート
Go言語では、sort
パッケージを使用してスライスをソートします。sort.Interface
インターフェースを実装することで、任意の型のスライスをソートできます。このインターフェースは以下の3つのメソッドを定義しています。
Len() int
: スライスの要素数を返します。Swap(i, j int)
: スライスのi
番目とj
番目の要素を入れ替えます。Less(i, j int) bool
:i
番目の要素がj
番目の要素よりも小さい場合にtrue
を返します。
このコミットでは、シンボル情報を表す Sym
構造体のスライスをソートするために、この sort.Interface
を実装した新しい型が追加されています。
技術的詳細
このコミットの技術的な核心は、cmd/nm
コマンドにシンボルサイズによるソート機能を追加することです。これは主に以下の変更によって実現されています。
- 新しいソートオプションの追加:
doc.go
ファイルのヘルプメッセージと、nm.go
ファイルのコマンドライン引数解析部分に-sort=size
オプションが追加されました。 bySize
型の導入:nm.go
にbySize
という新しい型が定義されました。この型は[]Sym
(シンボルのスライス) のエイリアスであり、sort.Interface
インターフェースを実装しています。- サイズによる比較ロジック:
bySize
型のLess
メソッドは、2つのシンボルx[i]
とx[j]
のSize
フィールドを比較し、x[i].Size > x[j].Size
の場合にtrue
を返します。これにより、ソート順が降順(大きいサイズから小さいサイズへ)になります。これは、大きなシンボルを特定するという目的のために意図された動作です。 - ソート処理の統合:
main
関数内のソートロジックに、-sort=size
オプションが指定された場合にsort.Sort(bySize(syms))
を呼び出す処理が追加されました。
これにより、ユーザーが nm -size -sort=size <binary>
のようにコマンドを実行すると、シンボルがそのサイズに基づいて降順にソートされて表示されるようになります。
コアとなるコードの変更箇所
src/cmd/nm/doc.go
--- a/src/cmd/nm/doc.go
+++ b/src/cmd/nm/doc.go
@@ -31,8 +31,9 @@
// for compatibility with other nm commands
// -size
// print symbol size in decimal between address and type
-// -sort {address,name,none}\n
+// -sort {address,name,none,size}
// sort output in the given order (default name)
+// size orders from largest to smallest
// -type
// print symbol type after name
//
src/cmd/nm/nm.go
--- a/src/cmd/nm/nm.go
+++ b/src/cmd/nm/nm.go
@@ -58,7 +58,7 @@ func main() {
flag.Parse()
switch *sortOrder {
- case "address", "name", "none":
+ case "address", "name", "none", "size":
// ok
default:
fmt.Fprintf(os.Stderr, "nm: unknown sort order %q\n", *sortOrder)
@@ -135,6 +135,8 @@ HaveSyms:
sort.Sort(byAddr(syms))
case "name":
sort.Sort(byName(syms))
+ case "size":
+ sort.Sort(bySize(syms))
}
w := bufio.NewWriter(os.Stdout)
@@ -170,3 +172,9 @@ type byName []Sym
func (x byName) Len() int { return len(x) }\n func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }\n func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name }\n+\n+type bySize []Sym\n+\n+func (x bySize) Len() int { return len(x) }\n+func (x bySize) Swap(i, j int) { x[i], x[j] = x[j], x[i] }\n+func (x bySize) Less(i, j int) bool { return x[i].Size > x[j].Size }\n```
## コアとなるコードの解説
### `src/cmd/nm/doc.go` の変更
* `// -sort {address,name,none}` が `// -sort {address,name,none,size}` に変更されました。これは、`nm` コマンドの `-sort` オプションに `size` という新しい値が追加されたことをユーザーに伝えるためのドキュメントの更新です。
* `// size orders from largest to smallest` という行が追加されました。これは、`size` ソートが降順(大きいサイズから小さいサイズへ)で行われることを明示しています。これは、大きなシンボルを特定するという `nm` の一般的な使用目的と合致しています。
### `src/cmd/nm/nm.go` の変更
1. **`flag.Parse()` 後の `switch *sortOrder` ブロック**:
* `case "address", "name", "none":` の行に `, "size"` が追加され、`case "address", "name", "none", "size":` となりました。これは、コマンドラインで `-sort=size` が指定された場合に、それが有効なソートオプションとして認識されるようにするための変更です。もしこの変更がなければ、`"size"` は `default` ケースにフォールバックし、「unknown sort order」エラーが発生していました。
2. **`HaveSyms:` ラベル後のソートロジック**:
* 新しい `case "size":` ブロックが追加されました。
* このブロック内で `sort.Sort(bySize(syms))` が呼び出されています。これは、`-sort=size` オプションが指定された場合に、`syms` スライス(シンボル情報のリスト)を `bySize` 型として `sort.Sort` 関数に渡し、サイズに基づいてソートを実行する指示です。
3. **`bySize` 型の追加**:
* `type bySize []Sym` という新しい型が定義されました。これは、`Sym` 構造体のスライス `[]Sym` のエイリアスです。
* この `bySize` 型は、Goの `sort.Interface` インターフェースの3つのメソッド (`Len`, `Swap`, `Less`) を実装しています。
* `func (x bySize) Len() int { return len(x) }`: スライスの長さを返します。
* `func (x bySize) Swap(i, j int) { x[i], x[j] = x[j], x[i] }`: スライスの `i` 番目と `j` 番目の要素を交換します。
* `func (x bySize) Less(i, j int) bool { return x[i].Size > x[j].Size }`: **このメソッドがソートの核心です。** `x[i]` の `Size` フィールドが `x[j]` の `Size` フィールドよりも大きい場合に `true` を返します。これにより、`sort.Sort` 関数は要素を降順(大きいサイズが先頭に来る)に並べ替えます。
これらの変更により、`cmd/nm` はシンボルサイズによるソートを正確に実行できるようになり、特にバイナリのサイズ分析において非常に有用な機能が追加されました。
## 関連リンク
* Go言語の `sort` パッケージのドキュメント: [https://pkg.go.dev/sort](https://pkg.go.dev/sort)
* Go言語の `cmd/nm` のソースコード (このコミットが適用された後の状態): [https://github.com/golang/go/tree/master/src/cmd/nm](https://github.com/golang/go/tree/master/src/cmd/nm) (現在の最新版とは異なる可能性があります)
## 参考にした情報源リンク
* Go言語のコミット履歴: [https://github.com/golang/go/commits/master](https://github.com/golang/go/commits/master)
* `nm` コマンドに関する一般的な情報 (Unix man pageなど): `man nm`
* Go言語の `sort` パッケージの公式ドキュメント
* Go言語の `cmd/nm` のソースコード
* CL 45150044に関する情報(Web検索結果): このコミットメッセージにある「Perfect recreation of CL 45150044, which was lost to some blunder.」という記述から、過去に同様の変更が試みられたが失われた経緯があることが示唆されます。しかし、公開されているGoのChange List (CL) の検索では、この特定のCL番号に関する詳細な情報を見つけることは困難でした。これは、内部的な問題や、公開される前に失われた変更であった可能性を示唆しています。