[インデックス 15700] ファイルの概要
このコミットは、Go言語のプロファイリングツールであるpprof
がaddr2line
ユーティリティを正しく利用できるようにするため、cmd/addr2line
コマンドが--help
オプションを受け取った際に終了コード0で終了するように修正するものです。これにより、pprof
がaddr2line
のヘルプ出力を正常なものとして認識し、プロファイリングデータのシンボル解決を適切に行えるようになります。
コミット
commit d2be8f29485f80d41d84970aa32329b3621c9dab
Author: Russ Cox <rsc@golang.org>
Date: Mon Mar 11 18:12:07 2013 -0400
cmd/addr2line: exit 0 for --help
This is what pprof expects, or else it won't use the program.
And if it doesn't use the program, it gets very bad results.
Fixes #4818.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/7728043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d2be8f29485f80d41d84970aa32329b3621c9dab
元コミット内容
cmd/addr2line: exit 0 for --help
pprof
が期待する動作であり、そうしないとプログラムを使用しない。
そして、プログラムを使用しないと、非常に悪い結果になる。
Fixes #4818.
R=golang-dev, r CC=golang-dev https://golang.org/cl/7728043
変更の背景
この変更は、Go言語のプロファイリングツールであるpprof
が、アドレスをソースコードの行番号に変換するユーティリティであるaddr2line
と連携する際の不具合を修正するために行われました。
従来のaddr2line
は、--help
オプションが指定された際に、ヘルプメッセージを表示した後に非ゼロの終了コード(通常はエラーを示す)で終了していました。しかし、pprof
は、外部ツール(この場合はaddr2line
)が正常に動作したかどうかを判断するために、そのツールの終了コードが0であるかどうかをチェックします。非ゼロの終了コードはエラーと見なされるため、pprof
はaddr2line
が正常に実行されなかったと判断し、その結果、アドレスからソースコードへのシンボル解決が行われず、プロファイリング結果が正しく表示されないという問題が発生していました。
この問題は、GoのIssue #4818として報告されており、このコミットはその問題を解決することを目的としています。pprof
がaddr2line
を呼び出す際に、内部的に--help
オプションを渡してツールの機能をチェックするような挙動があったため、この修正が必要となりました。
前提知識の解説
addr2line
addr2line
は、実行ファイルやオブジェクトファイル内のメモリアドレスを、対応するソースコードのファイル名と行番号に変換するユーティリティです。デバッグにおいて非常に重要なツールであり、クラッシュログやコアダンプから得られた生のアドレスを人間が読める形式に変換する際に利用されます。addr2line
が正しく機能するためには、実行ファイルがデバッグ情報(通常はコンパイラに-g
フラグを付けてコンパイルすることで生成される)を含んでいる必要があります。
pprof
pprof
は、Goプログラムのプロファイリングデータを視覚化し、分析するための強力なツールです。CPU使用率、メモリ割り当て、ゴルーチン、ブロックなど、さまざまな種類のプロファイリングデータを収集し、グラフやテキスト形式で表示できます。pprof
は、プロファイリングデータに含まれるマシンアドレスを、addr2line
のようなシンボル解決ツール(Goの場合、go tool addr2line
)を利用して、人間が読めるソースコードの場所(ファイル名と行番号)に変換します。これにより、開発者はパフォーマンスのボトルネックを特定の関数やコード行に特定しやすくなります。
終了コード (Exit Status)
Unix系オペレーティングシステムでは、コマンドが実行を終了する際に「終了コード」または「終了ステータス」と呼ばれる整数値を返します。この値は、コマンドの実行結果を示します。
- 0: 慣例的に、コマンドがエラーなく正常に実行されたことを示します。
- 非ゼロ (1以上): コマンドの実行中に何らかのエラーが発生したことを示します。エラーの種類によって異なる非ゼロの値が返されることがあります。
多くのプログラムやスクリプトは、外部コマンドの終了コードをチェックして、そのコマンドが成功したか失敗したかを判断します。
--help
オプションの慣例
Unix系コマンドラインツールでは、--help
または-h
オプションが指定された際に、そのツールの使い方や利用可能なオプションのリストを表示するのが一般的な慣例です。通常、ヘルプメッセージを表示した後は、プログラムは正常に終了したと見なされ、終了コード0を返します。これは、ユーザーが情報を求めてツールを呼び出しただけであり、エラーが発生したわけではないためです。
技術的詳細
このコミットの技術的な核心は、addr2line
が--help
オプションを受け取った際に、その慣例に従って終了コード0で終了するように変更することです。
pprof
は、プロファイリングデータのシンボル解決を行う際に、内部的にaddr2line
を呼び出します。この際、pprof
はaddr2line
が正常に動作するかどうかを確認するために、--help
オプションを付けてaddr2line
を呼び出すことがあります。もしaddr2line
が--help
で非ゼロの終了コードを返した場合、pprof
はaddr2line
の実行が失敗したと判断し、シンボル解決のプロセスを中断するか、不完全な結果を返します。これにより、プロファイリング結果が「??:0」のような未解決のシンボルで埋め尽くされ、分析が困難になるという問題が発生していました。
この修正により、addr2line
が--help
で終了コード0を返すようになるため、pprof
はaddr2line
の実行を正常と判断し、期待通りにシンボル解決を進めることができるようになります。これは、pprof
とaddr2line
間の連携をスムーズにし、Goプログラムのプロファイリング体験を向上させるための重要な変更です。
コアとなるコードの変更箇所
変更は src/cmd/addr2line/main.c
ファイルに対して行われています。
--- a/src/cmd/addr2line/main.c
+++ b/src/cmd/addr2line/main.c
@@ -11,13 +11,19 @@
#include <bio.h>
#include <mach.h>
+void
+printusage(int fd)
+{
+ fprint(fd, "usage: addr2line binary\n");
+ fprint(fd, "reads addresses from standard input and writes two lines for each:\n");
+ fprint(fd, "\tfunction name\n");
+ fprint(fd, "\tfile:line\n");
+}
+
void
usage(void)
{
-\tfprint(2, "usage: addr2line binary\\n");
-\tfprint(2, "reads addresses from standard input and writes two lines for each:\\n");
-\tfprint(2, "\\tfunction name\\n");
-\tfprint(2, "\\tfile:line\\n");
+\tprintusage(2);
\texits("usage");
}
@@ -32,6 +38,11 @@ main(int argc, char **argv)
Biobuf bin, bout;\n\tchar file[1024];
+\tif(argc > 1 && strcmp(argv[1], "--help") == 0) {
+\t\tprintusage(1);
+\t\texits(0);
+\t}
+\
\tARGBEGIN{\n \tdefault:\n \t\tusage();
コアとなるコードの解説
このコミットでは、主に以下の2つの変更が行われています。
-
printusage
関数の導入:- これまでの
usage()
関数内に直接記述されていた使用方法のメッセージ出力ロジックが、新しく導入されたprintusage(int fd)
関数に切り出されました。 printusage
関数は、引数としてファイルディスクリプタfd
を受け取り、そのディスクリプタに対して使用方法のメッセージを出力します。これにより、標準出力(fd=1
)にも標準エラー出力(fd=2
)にもメッセージを出力できるようになり、柔軟性が向上しました。- 元の
usage()
関数は、printusage(2)
を呼び出すように変更され、引き続き標準エラー出力にメッセージを出力し、その後exits("usage")
で非ゼロ終了します。これは、不正な引数が渡された場合のエラー終了の挙動を維持するためです。
- これまでの
-
--help
オプションのハンドリング:main
関数の冒頭に、コマンドライン引数をチェックする新しい条件分岐が追加されました。if(argc > 1 && strcmp(argv[1], "--help") == 0)
: これは、プログラムが少なくとも1つの引数(プログラム名を除く)を受け取り、かつその最初の引数が文字列"--help"
と完全に一致するかどうかをチェックします。- もし
--help
オプションが検出された場合、printusage(1)
が呼び出され、使用方法のメッセージが標準出力(fd=1
)に出力されます。 - その後、
exits(0)
が呼び出され、プログラムは終了コード0で終了します。これにより、--help
オプションが指定された場合は、プログラムが正常に終了したとpprof
などの呼び出し元に通知されます。
この変更により、addr2line
は--help
オプションに対して標準的なUnixコマンドの挙動(ヘルプメッセージを標準出力に出力し、終了コード0で終了)を示すようになり、pprof
との連携がスムーズになりました。
関連リンク
- GitHubコミット: https://github.com/golang/go/commit/d2be8f29485f80d41d84970aa32329b3621c9dab
- Go CL (Code Review): https://golang.org/cl/7728043
- Go Issue #4818: https://github.com/golang/go/issues/4818 (コミットメッセージに記載されているため、関連する可能性が高い)
参考にした情報源リンク
addr2line
の概要: https://www.geeksforgeeks.org/addr2line-command-in-linux/pprof
とaddr2line
の連携: https://github.com/google/pprof/blob/main/doc/README.md- Goの
go tool addr2line
: https://go.dev/doc/go1.2#go_tool_addr2line - Unix系における終了コードの慣例: https://www.studytonight.com/linux-commands/exit-status-in-linux
--help
オプションの慣例: https://www.man7.org/linux/man-pages/man7/cli.7.html