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

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

このコミットは、Go言語のプロファイリングツールであるpprofaddr2lineユーティリティを正しく利用できるようにするため、cmd/addr2lineコマンドが--helpオプションを受け取った際に終了コード0で終了するように修正するものです。これにより、pprofaddr2lineのヘルプ出力を正常なものとして認識し、プロファイリングデータのシンボル解決を適切に行えるようになります。

コミット

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であるかどうかをチェックします。非ゼロの終了コードはエラーと見なされるため、pprofaddr2lineが正常に実行されなかったと判断し、その結果、アドレスからソースコードへのシンボル解決が行われず、プロファイリング結果が正しく表示されないという問題が発生していました。

この問題は、GoのIssue #4818として報告されており、このコミットはその問題を解決することを目的としています。pprofaddr2lineを呼び出す際に、内部的に--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を呼び出します。この際、pprofaddr2lineが正常に動作するかどうかを確認するために、--helpオプションを付けてaddr2lineを呼び出すことがあります。もしaddr2line--helpで非ゼロの終了コードを返した場合、pprofaddr2lineの実行が失敗したと判断し、シンボル解決のプロセスを中断するか、不完全な結果を返します。これにより、プロファイリング結果が「??:0」のような未解決のシンボルで埋め尽くされ、分析が困難になるという問題が発生していました。

この修正により、addr2line--helpで終了コード0を返すようになるため、pprofaddr2lineの実行を正常と判断し、期待通りにシンボル解決を進めることができるようになります。これは、pprofaddr2line間の連携をスムーズにし、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つの変更が行われています。

  1. printusage 関数の導入:

    • これまでのusage()関数内に直接記述されていた使用方法のメッセージ出力ロジックが、新しく導入されたprintusage(int fd)関数に切り出されました。
    • printusage関数は、引数としてファイルディスクリプタfdを受け取り、そのディスクリプタに対して使用方法のメッセージを出力します。これにより、標準出力(fd=1)にも標準エラー出力(fd=2)にもメッセージを出力できるようになり、柔軟性が向上しました。
    • 元のusage()関数は、printusage(2)を呼び出すように変更され、引き続き標準エラー出力にメッセージを出力し、その後exits("usage")で非ゼロ終了します。これは、不正な引数が渡された場合のエラー終了の挙動を維持するためです。
  2. --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との連携がスムーズになりました。

関連リンク

参考にした情報源リンク