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

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

このコミットは、Go言語のプロファイリングツール pprof におけるLinux環境でのアドレスルックアップの回帰バグを修正するものです。具体的には、addr2line コマンドの利用方法を変更し、常に go tool addr2line を使用するようにすることで、クロスプラットフォームでの互換性と信頼性を向上させています。

コミット

commit 8c2b6226f7ab867f9b55a655cd32018cec997df0
Author: Jeff R. Allen <jra@nella.org>
Date:   Wed Mar 13 09:40:38 2013 -0700

    misc/pprof: fix address lookup regression on Linux
    
    Just use "go tool addr2line" no matter what, since we know
    it works for all OSs.
    
    Fixes #4818.
    
    R=rsc, r
    CC=golang-dev
    https://golang.org/cl/7526047

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

https://github.com/golang/go/commit/8c2b6226f7ab867f9b55a655cd32018cec997df0

元コミット内容

misc/pprof: fix address lookup regression on Linux Just use "go tool addr2line" no matter what, since we know it works for all OSs. Fixes #4818.

変更の背景

このコミットは、Go言語のプロファイリングツール pprof がLinux環境でアドレスからシンボル情報を解決する際に発生していた回帰バグ(regression)を修正するために行われました。以前のバージョンでは正しく動作していたアドレスルックアップが、何らかの変更によって機能しなくなっていたと考えられます。

pprof は、プログラムの実行中にCPU使用率やメモリ使用量などのプロファイル情報を収集し、その結果を可視化するためのツールです。プロファイル情報には、プログラムがどの関数でどれくらいの時間を費やしたか、どのメモリ領域を消費したかなどが含まれます。これらの情報を人間が理解しやすい形で表示するためには、実行アドレス(メモリ上の位置)を対応するソースコードのファイル名、行番号、関数名に変換する「アドレスルックアップ」の機能が不可欠です。

このバグは、特にLinux環境で pprof を使用する際に、アドレスルックアップが正しく行われず、プロファイル結果の解析が困難になるという問題を引き起こしていました。コミットメッセージにある Fixes #4818 は、この問題がGoプロジェクトのIssueトラッカーで報告されていたことを示しています。

問題の根本原因は、pprof スクリプトが addr2line コマンドをどのように利用していたかにありました。addr2line は、実行可能ファイル内のアドレスをソースコードの行番号に変換するGNU Binutilsの一部として提供されるツールです。しかし、システムにインストールされている addr2line のバージョンや、Goツールチェインが提供する go tool addr2line との互換性の問題が、Linux環境での回帰バグを引き起こしていた可能性があります。

このコミットの目的は、このアドレスルックアップの信頼性を回復し、pprof がすべてのオペレーティングシステムで一貫して正しく動作するようにすることでした。

前提知識の解説

プロファイリングとpprof

プロファイリングとは、プログラムの実行時の振る舞いを測定・分析し、パフォーマンス上のボトルネックやリソース消費の状況を特定するプロセスです。Go言語には、標準でプロファイリング機能が組み込まれており、pprof ツールはそのプロファイルデータを解析・可視化するための強力なツールです。

pprof は、CPUプロファイル、メモリプロファイル、ゴルーチンプロファイルなど、様々な種類のプロファイルデータをサポートしています。これらのプロファイルデータは、プログラムの実行中に特定のイベント(例: 関数呼び出し、メモリ割り当て)が発生した際のアドレス情報を記録することで生成されます。

アドレスルックアップとaddr2line

プログラムがコンパイルされると、ソースコードは機械語に変換され、メモリ上の特定のアドレスに配置されます。プロファイリングツールが収集するデータは、通常、これらのメモリ上のアドレスを含んでいます。しかし、開発者がパフォーマンスの問題を特定するためには、これらのアドレスがどのソースコードのどの行に対応するのかを知る必要があります。このアドレスからソースコードの情報を逆引きするプロセスが「アドレスルックアップ」です。

addr2line は、このアドレスルックアップを行うためのコマンドラインツールです。通常、デバッグ情報が埋め込まれた実行可能ファイルに対して、特定のアドレスがどのファイル、どの関数、どの行番号に対応するかを問い合わせるために使用されます。

go tool addr2line

Go言語のツールチェインには、go tool addr2line という独自の addr2line 実装が含まれています。これは、Go言語のバイナリに特化して最適化されており、Goのランタイムやコンパイラが生成するデバッグ情報を正確に解析できます。システムにインストールされている汎用の addr2line とは異なり、go tool addr2line はGoのバイナリに対して常に正しい結果を返すことが期待されます。

回帰バグ (Regression Bug)

回帰バグとは、以前のバージョンでは正しく動作していた機能が、新しい変更(コードの追加、修正、リファクタリングなど)によって再び壊れてしまうバグのことです。このコミットの背景にある問題は、まさにこの回帰バグの一例であり、pprof のアドレスルックアップ機能が以前は動作していたにもかかわらず、ある時点で動作しなくなったことを示しています。

技術的詳細

このコミットの技術的な核心は、pprof スクリプトがアドレスルックアップのために addr2line コマンドを選択するロジックの変更にあります。

変更前の pprof スクリプトでは、addr2line コマンドの存在を確認し、もしシステムに addr2line がインストールされていない場合にのみ、go tool addr2line にフォールバックするようなロジックが組まれていました。

  if (system("$addr2line --help >/dev/null 2>&1") != 0) {
    # addr2line must not exist.  Fall back to go tool addr2line.
    $addr2line = "go tool addr2line";
    $cmd = "$addr2line $image";
  }

このロジックにはいくつかの問題がありました。

  1. addr2line の存在チェックの不確実性: system("$addr2line --help >/dev/null 2>&1") != 0 というチェックは、addr2line コマンドがシステムに存在するかどうかを確認するためのものです。しかし、addr2line が存在しても、そのバージョンが古い、またはGoのバイナリのデバッグ情報と互換性がないなどの理由で、正しく機能しない可能性がありました。特にLinux環境では、ディストリビューションによって addr2line のバージョンや挙動が異なることがあり、これが回帰バグの原因となっていたと考えられます。
  2. Goバイナリへの非最適化: 汎用の addr2line は、Goのバイナリに特化して設計されているわけではありません。Goのコンパイラが生成するデバッグ情報の形式は、C/C++などの他の言語とは異なる場合があり、汎用の addr2line ではGoのデバッグ情報を完全に解析できないことがありました。
  3. クロスプラットフォーム互換性の問題: go tool addr2line は、Goツールチェインの一部として提供されるため、Goがサポートするすべてのプラットフォームで利用可能です。一方、汎用の addr2line は、GNU Binutilsの一部であり、すべてのオペレーティングシステムにデフォルトでインストールされているわけではありません。また、Windowsなどの環境では、addr2line の代替手段が必要になる場合があります。

このコミットでは、これらの問題を解決するために、addr2line の選択ロジックを大幅に簡素化しました。具体的には、システムに汎用の addr2line が存在するかどうかのチェックを完全に削除し、常に go tool addr2line を使用するように変更しました。

  # Use the go version because we know it works on all platforms
  $addr2line = "go tool addr2line";
  $cmd = "$addr2line $image";

この変更により、以下の利点が得られます。

  • 信頼性の向上: go tool addr2line はGoのバイナリに特化しているため、Goのプロファイルデータに対して常に正確なアドレスルックアップを提供します。これにより、Linux環境での回帰バグが修正され、将来的な互換性の問題も軽減されます。
  • クロスプラットフォーム互換性の保証: go tool addr2line はGoツールチェインの一部として提供されるため、Goが動作するすべてのプラットフォームで利用可能です。これにより、pprof がどのOSでも一貫して動作することが保証されます。
  • コードの簡素化: addr2line の存在チェックとフォールバックロジックが不要になり、スクリプトのコードがよりシンプルで理解しやすくなりました。

この変更は、pprof の安定性と信頼性を高める上で重要な修正であり、Go言語のプロファイリングエコシステム全体の健全性に貢献しています。

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

変更は misc/pprof ファイルに対して行われています。これは、Go言語のソースツリー内の misc ディレクトリにある pprof スクリプト(Perlで書かれています)です。

--- a/misc/pprof
+++ b/misc/pprof
@@ -4417,11 +4417,9 @@ sub MapToSymbols {
     $cmd = "$addr2line --demangle -f -C -e $image";
   }\n
-  if (system("$addr2line --help >/dev/null 2>&1") != 0) {
-    # addr2line must not exist.  Fall back to go tool addr2line.
-    $addr2line = "go tool addr2line";
-    $cmd = "$addr2line $image";
-  }\n
+  # Use the go version because we know it works on all platforms
+  $addr2line = "go tool addr2line";
+  $cmd = "$addr2line $image";
\n
   # If "addr2line" isn't installed on the system at all, just use
   # nm to get what info we can (function names, but not line numbers).\n

具体的には、sub MapToSymbols サブルーチン内の addr2line コマンドの選択ロジックが変更されています。

  • 削除された行:

    • if (system("$addr2line --help >/dev/null 2>&1") != 0) {
    • # addr2line must not exist. Fall back to go tool addr2line.
    • $addr2line = "go tool addr2line";
    • $cmd = "$addr2line $image";
    • } これらの行は、システムに addr2line が存在しない場合に go tool addr2line にフォールバックするための条件分岐でした。
  • 追加された行:

    • # Use the go version because we know it works on all platforms
    • $addr2line = "go tool addr2line";
    • $cmd = "$addr2line $image"; これらの行は、コメントとともに、常に go tool addr2line を使用するように addr2line 変数と cmd 変数を設定しています。

コアとなるコードの解説

この変更は、pprof スクリプトの MapToSymbols というサブルーチン内で行われています。このサブルーチンは、プロファイルデータ内のアドレスをシンボル情報(関数名、ファイル名、行番号など)にマッピングする役割を担っています。

変更前のコードでは、まず $addr2line という変数に、システムにインストールされている可能性のある addr2line コマンドのパスが設定されていました(このdiffにはその初期設定部分は含まれていませんが、文脈から推測できます)。その後、system("$addr2line --help >/dev/null 2>&1") != 0 という条件文で、その $addr2line コマンドがヘルプオプションを正常に実行できるかどうかをチェックしていました。このチェックが失敗した場合(つまり、addr2line が存在しないか、実行できない場合)、$addr2line 変数を "go tool addr2line" に再設定し、$cmd 変数もそれに応じて更新していました。これは、汎用の addr2line が利用できない場合のフォールバックメカニズムでした。

しかし、このコミットでは、このフォールバックロジック全体が削除され、代わりに以下の2行が追加されました。

  $addr2line = "go tool addr2line";
  $cmd = "$addr2line $image";

この変更により、MapToSymbols サブルーチンが呼び出された際、addr2line コマンドとして常に "go tool addr2line" が使用されるようになります。$image 変数には、プロファイル対象の実行可能ファイルのパスが格納されており、$cmd 変数には最終的に実行されるコマンドライン文字列が構築されます。

この修正の意図は、汎用の addr2line がGoのバイナリに対して常に正しく動作するとは限らないという経験に基づいています。特に、Goのコンパイラが生成するデバッグ情報の形式は、他の言語のそれとは異なる場合があり、汎用の addr2line ではその情報を完全に解釈できないことがありました。一方で、go tool addr2line はGoツールチェインの一部として開発されており、Goのバイナリのデバッグ情報を正確に解析するように設計されています。

したがって、この変更は、pprof のアドレスルックアップ機能の信頼性とクロスプラットフォーム互換性を向上させるための、シンプルかつ効果的な解決策です。これにより、ユーザーはどのオペレーティングシステムでGoのプロファイリングを行っても、正確なシンボル情報を得られるようになります。

関連リンク

参考にした情報源リンク