[インデックス 13648] ファイルの概要
このコミットは、Go言語のプロファイリングツールである pprof がWindows環境で正しく動作するようにするための修正を含んでいます。具体的には、pprof がPerlスクリプトとして実行される際の挙動と、シンボル解決に使用される nm ツールの呼び出し方法がWindows向けに調整されています。
コミット
commit 58064a7cab9d39ff9b58c94e76e441dc238343b9
Author: Alex Brainman <alex.brainman@gmail.com>
Date: Sat Aug 18 17:03:32 2012 +1000
pprof: make it work on windows again
- pprof is a perl script, so go command should invoke
perl instead of trying to run pprof directly;
- pprof should use "go tool nm" unconditionally on windows,
no one else can extract symbols from Go program;
- pprof should use "go tool nm" instead of "6nm".
Fixes #3879.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6445082
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/58064a7cab9d39ff9b58c94e76e441dc238343b9
元コミット内容
このコミットは、pprof ツールがWindows上で再び動作するようにするための変更です。主な変更点は以下の通りです。
pprofはPerlスクリプトであるため、goコマンドはpprofを直接実行しようとするのではなく、perlインタープリタを介して呼び出すように修正されました。- Windows上では、Goプログラムからシンボルを抽出できるのは
go tool nmのみであるため、pprofは無条件にgo tool nmを使用するように変更されました。 pprofは6nmの代わりにgo tool nmを使用するように修正されました。
この変更は、Issue #3879 を修正するものです。
変更の背景
このコミットが作成された2012年当時、Go言語の pprof ツールはPerlスクリプトとして実装されており、Windows環境での互換性に問題がありました。具体的には、以下の点が課題となっていました。
- Perl依存性:
pprofを実行するためには、ユーザーが別途Perl環境(例: 64-bit Perl, msys-perl)をインストールする必要がありました。これは、Goツールチェーンの一部として提供されるツールとしては、追加の依存関係となり、ユーザーにとって負担でした。 - パスの問題:
pprofスクリプトは、Windowsのパス形式や一時ファイルの作成において、Unixライクなパスを期待することがあり、問題を引き起こしていました。 - ツール連携の問題:
go toolコマンドがPerlスクリプトを正しく扱えなかったり、fileやaddr2lineといった外部ツールを見つけられなかったりする問題が発生していました。特に、Goバイナリからシンボル情報を抽出する際に、Windows環境では6nmのような特定のツールが利用できない、あるいはgo tool nmのようなGoツールチェーンが提供する専用のツールを使用する必要がありました。
これらの問題により、Windowsユーザーは pprof を利用してGoプログラムのプロファイリングを行うことが困難でした。このコミットは、これらの互換性の問題を解決し、Windows上での pprof の利用を可能にすることを目的としています。
現代のGo言語では、pprof の機能はGoツールチェーン自体に統合されており、go tool pprof はGoバイナリとして提供されるため、Perlのインストールは不要になっています。しかし、このコミットがなされた時点では、Perlスクリプトとしての pprof の挙動をWindows環境に適合させることが喫緊の課題でした。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
-
pprof: Go言語のプロファイリングツール。CPU使用率、メモリ割り当て、ゴルーチン、ブロック、ミューテックス競合などのプロファイルデータを収集し、可視化するために使用されます。このコミットがなされた時点では、
pprofはPerlで書かれたスクリプトでした。 -
Perl: スクリプト言語の一つ。テキスト処理やシステム管理によく用いられます。
pprofがPerlスクリプトであったため、実行にはPerlインタープリタが必要でした。 -
go tool: Go言語のツールチェーンの一部で、様々な補助ツール(例:
go tool compile,go tool link,go tool nmなど)を実行するためのコマンドです。 -
nm: オブジェクトファイルや実行可能ファイルからシンボル(関数名、変数名など)をリストアップするためのUnix系コマンドラインツール。Go言語のツールチェーンには、Goバイナリに特化した
go tool nmが存在します。 -
6nm: 2012年当時のGo言語のツールチェーンにおいて、
amd64アーキテクチャ向けのnmツールを指す可能性のある名称です。Goの初期のツールチェーンでは、アーキテクチャごとにツール名がプレフィックス(例:6gforamd64compiler,8gfor386compiler)で区別されていました。このコミットでは、6nmの代わりにgo tool nmを使用するように変更されています。 -
Windowsの実行可能ファイル: Windowsでは、実行可能ファイルは通常
.exe拡張子を持ちます。また、Perlスクリプトを直接実行するのではなく、perl.exeを介してスクリプトファイルを指定して実行する必要があります。 -
Issue #3879: Go言語のGitHubリポジトリで管理されている、
pprofスクリプトがWindowsで動作しないという問題報告です。このコミットはこのIssueを解決するために作成されました。
技術的詳細
このコミットは、主に misc/pprof (Perlスクリプト) と src/cmd/go/tool.go (Goコマンドのツール実行ロジック) の2つのファイルにわたる変更を含んでいます。
misc/pprof の変更点
misc/pprof は、pprof ツールのPerlスクリプト本体です。このスクリプトは、プロファイルデータを解析し、シンボル情報を取得するために nm などの外部ツールを呼び出します。
-
Windows判定ロジックの追加:
+ $obj_tool_map{"is_windows"} = "true";ConfigureObjToolsサブルーチン内で、Windows環境である場合に$obj_tool_map{"is_windows"}を"true"に設定する行が追加されました。これにより、スクリプト内でWindows固有の挙動を制御するためのフラグが提供されます。 -
GetProcedureBoundariesサブルーチンの変更: このサブルーチンは、実行可能ファイルからプロシージャ(関数)の境界情報を取得するために、様々なnmコマンドを試行します。 変更前は、Unix系のnmコマンドのバリエーションや、Goバイナリ向けの6nmを試していました。また、nm_pdb(WindowsのPDBファイルからシンボルを抽出するツール) が存在する場合に、それも試行するロジックがありました。変更後、特に重要なのは以下の部分です。
- # 6nm is for Go binaries - "6nm $image 2>/dev/null | sort"); + # go tool nm is for Go binaries + "go tool nm $image 2>/dev/null | sort");6nmの代わりにgo tool nmを使用するように変更されました。これは、Goバイナリのシンボル抽出にはgo tool nmがより適切であり、Windows環境でも利用可能であることを示唆しています。さらに、Windows環境でのシンボル抽出ロジックが簡素化されました。
- # If the executable is an MS Windows PDB-format executable, we'll - # have set up obj_tool_map("nm_pdb"). In this case, we actually - # want to use both unix nm and windows-specific nm_pdb, since - # PDB-format executables can apparently include dwarf .o files. - if (exists $obj_tool_map{"nm_pdb"}) { - my $nm_pdb = $obj_tool_map{"nm_pdb"}; - push(@nm_commands, "$nm_pdb --demangle $image 2>/dev/null"); + # If the executable is an MS Windows Go executable, we'll + # have set up obj_tool_map("is_windows"). + if (exists $obj_tool_map{"is_windows"}) { + @nm_commands = ("go tool nm $image 2>/dev/null | sort");nm_pdbの存在チェックから、is_windowsフラグのチェックに変更され、Windows環境ではgo tool nmのみをシンボル抽出コマンドとして使用するように強制されます。これは、Windows上ではGoプログラムのシンボル抽出にgo tool nmが唯一信頼できる方法であるという認識に基づいています。
src/cmd/go/tool.go の変更点
src/cmd/go/tool.go は、go tool コマンドがどのように外部ツール(この場合は pprof)を実行するかを定義するGo言語のソースファイルです。
-
Windowsでの
.exe拡張子付与の条件変更:func tool(name string) string { p := filepath.Join(toolDir, name) if toolIsWindows && name != "pprof" { // 変更点: name != "pprof" の追加 p += toolWindowsExtension } return p }tool関数は、ツール名から実行可能ファイルのパスを構築します。Windowsでは通常.exe拡張子が付与されますが、pprofの場合はPerlスクリプトであるため、直接.exeを付与すると問題が発生します。この変更により、pprofの場合は.exe拡張子が付与されないようになりました。これは、pprofがPerlインタープリタによって実行されることを前提としているためです。 -
pprofのPerl経由での実行ロジック:func runTool(cmd *Command, args []string) { // ... 既存のコード ... if toolIsWindows && toolName == "pprof" { args = append([]string{"perl", toolPath}, args[1:]...) var err error toolPath, err = exec.LookPath("perl") if err != nil { fmt.Fprintf(os.Stderr, "go tool: perl not found\\n") setExitStatus(3) return } } // ... 既存のコード ... }runTool関数は、実際にツールを実行するロジックを含んでいます。Windows環境で実行されるツールがpprofである場合、以下の処理が追加されました。argsスライス(コマンドライン引数)の先頭に"perl"とtoolPath(pprofスクリプトのパス) を追加します。これにより、perl /path/to/pprof [original_args]の形式でコマンドが構築されます。exec.LookPath("perl")を使用して、システムパスからperlインタープリタの実行可能ファイルを探します。perlが見つからない場合は、エラーメッセージを出力し、終了ステータスを設定して終了します。これは、pprofを実行するためにperlが必須であることを明示しています。
これらの変更により、go tool pprof コマンドがWindows上で実行された際に、pprof スクリプトが正しくPerlインタープリタによって呼び出され、かつGoバイナリのシンボル解決に適切な go tool nm が使用されるようになりました。
コアとなるコードの変更箇所
misc/pprof
--- a/misc/pprof
+++ b/misc/pprof
@@ -4599,6 +4599,7 @@ sub ConfigureObjTools {
# in the same directory as pprof.
$obj_tool_map{"nm_pdb"} = "nm-pdb";
$obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
+ $obj_tool_map{"is_windows"} = "true";
}
if ($file_type =~ /Mach-O/) {
@@ -4806,16 +4807,13 @@ sub GetProcedureBoundaries {
" $image 2>/dev/null $cppfilt_flag",
"$nm -D -n $flatten_flag $demangle_flag" .
" $image 2>/dev/null $cppfilt_flag",
- # 6nm is for Go binaries
- "6nm $image 2>/dev/null | sort");
-\
- # If the executable is an MS Windows PDB-format executable, we'll
- # have set up obj_tool_map("nm_pdb"). In this case, we actually
- # want to use both unix nm and windows-specific nm_pdb, since
- # PDB-format executables can apparently include dwarf .o files.
- if (exists $obj_tool_map{"nm_pdb"}) {
- my $nm_pdb = $obj_tool_map{"nm_pdb"};
- push(@nm_commands, "$nm_pdb --demangle $image 2>/dev/null");
+ # go tool nm is for Go binaries
+ "go tool nm $image 2>/dev/null | sort");
+\
+ # If the executable is an MS Windows Go executable, we'll
+ # have set up obj_tool_map("is_windows").
+ if (exists $obj_tool_map{"is_windows"}) {
+ @nm_commands = ("go tool nm $image 2>/dev/null | sort");
}
foreach my $nm_command (@nm_commands) {
src/cmd/go/tool.go
--- a/src/cmd/go/tool.go
+++ b/src/cmd/go/tool.go
@@ -47,7 +47,7 @@ const toolWindowsExtension = ".exe"
func tool(name string) string {
p := filepath.Join(toolDir, name)
- if toolIsWindows {
+ if toolIsWindows && name != "pprof" {
p += toolWindowsExtension
}
return p
@@ -76,6 +76,16 @@ func runTool(cmd *Command, args []string) {
setExitStatus(3)
return
}\
+\tif toolIsWindows && toolName == "pprof" {\
+\t\targs = append([]string{"perl", toolPath}, args[1:]...)\
+\t\tvar err error\
+\t\ttoolPath, err = exec.LookPath("perl")\
+\t\tif err != nil {\
+\t\t\tfmt.Fprintf(os.Stderr, "go tool: perl not found\\n")
+\t\t\tsetExitStatus(3)\
+\t\t\treturn
+\t\t}\
+\t}\
if toolN {
fmt.Printf("%s %s\\n", toolPath, strings.Join(args[1:], " "))
コアとなるコードの解説
misc/pprof の変更
-
$obj_tool_map{"is_windows"} = "true";の追加: これは、pprofスクリプトがWindows環境で実行されていることを内部的に認識するためのフラグです。このフラグの導入により、スクリプトはOSに依存する挙動をより適切に制御できるようになります。 -
6nmからgo tool nmへの変更:GetProcedureBoundariesサブルーチンは、実行可能ファイルからシンボル情報を抽出するために様々なnmコマンドを試します。以前はGoバイナリ向けに6nmを試していましたが、この変更によりgo tool nmを使用するようになりました。これは、go tool nmがGoツールチェーンの一部として提供され、Goバイナリのシンボル抽出に特化しているため、より信頼性が高いと判断されたためです。 -
Windows環境での
nmコマンドの強制:if (exists $obj_tool_map{"is_windows"})ブロック内で、@nm_commandsが("go tool nm $image 2>/dev/null | sort")のみで上書きされるようになりました。これは、Windows上ではGoバイナリのシンボル抽出にはgo tool nmが唯一の適切なツールであり、他のnmコマンドのバリエーションを試す必要がないことを意味します。これにより、Windows環境でのシンボル解決のロジックが簡素化され、堅牢性が向上しました。
src/cmd/go/tool.go の変更
-
tool関数のname != "pprof"条件追加:tool関数は、go toolコマンドが実行するツールのパスを構築します。Windowsでは通常、実行可能ファイルに.exe拡張子が付与されます。しかし、pprofはPerlスクリプトであるため、pprof.exeというファイルは存在しません。この変更により、pprofの場合は.exe拡張子が付与されなくなり、go tool pprofがpprofスクリプト自体を指すようになります。 -
runTool関数のPerl経由でのpprof実行ロジック: この変更は、Windows上でgo tool pprofが実行された際の挙動を根本的に変えます。args = append([]string{"perl", toolPath}, args[1:]...): これは、go tool pprof [args]というコマンドが、内部的にperl /path/to/pprof [args]という形式に変換されることを意味します。これにより、Windowsがpprofスクリプトを直接実行しようとするのではなく、perlインタープリタがpprofスクリプトを解釈して実行するようになります。exec.LookPath("perl"):perlインタープリタがシステムパス上に存在するかどうかを確認します。もし存在しない場合、pprofは実行できないため、エラーメッセージが表示されます。これは、pprofがPerlスクリプトであるという依存関係を明示的に処理するものです。
これらの変更は、Windows環境における pprof の実行フローを、Perlスクリプトとしての性質とWindowsの実行可能ファイルの扱いに合わせて調整し、互換性の問題を解決しています。
関連リンク
- Go Issue #3879: https://github.com/golang/go/issues/3879
- Go CL 6445082: https://golang.org/cl/6445082
参考にした情報源リンク
- The Go issue 3879, titled "pprof script doesn't work under Windows," is a very old issue from 2012 that highlighted compatibility problems with the
pproftool on Windows. - Historically, the
pproftool distributed with Go was a Perl script, which led to several challenges on Windows, including:- Perl Dependency: Users often needed to install a compatible Perl environment (e.g., 64-bit Perl, msys-perl) to run
pprof. - Pathing Issues: The script struggled with Windows-style paths and temporary file creation, often expecting Unix-like paths.
- Tooling Integration: Issues arose with
go toolnot correctly handling the Perl script or finding necessary external tools likefileoraddr2line.
- Perl Dependency: Users often needed to install a compatible Perl environment (e.g., 64-bit Perl, msys-perl) to run
- Modern
pprofon Windows: The good news is that the Gopproftool has evolved significantly since issue 3879 was reported. Thepproffunctionality is now largely integrated into the Go toolchain itself, meaninggo tool pprofis a Go binary and no longer relies on a separate Perl installation. - For modern Go versions,
pprofis a robust tool for visualizing and analyzing profiling data (CPU, memory, goroutine, etc.) on Windows. - https://go.dev/doc/diagnose (Go Diagnostics documentation)
- https://pkg.go.dev/cmd/go (Go command documentation)
- https://pkg.go.dev/cmd/pprof (pprof documentation)
- https://en.wikipedia.org/wiki/Perl (Perl on Wikipedia)
- https://en.wikipedia.org/wiki/Nm_(Unix) (nm (Unix) on Wikipedia)