[インデックス 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の初期のツールチェーンでは、アーキテクチャごとにツール名がプレフィックス(例:6g
foramd64
compiler,8g
for386
compiler)で区別されていました。このコミットでは、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
pprof
tool on Windows. - Historically, the
pprof
tool 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 tool
not correctly handling the Perl script or finding necessary external tools likefile
oraddr2line
.
- Perl Dependency: Users often needed to install a compatible Perl environment (e.g., 64-bit Perl, msys-perl) to run
- Modern
pprof
on Windows: The good news is that the Gopprof
tool has evolved significantly since issue 3879 was reported. Thepprof
functionality is now largely integrated into the Go toolchain itself, meaninggo tool pprof
is a Go binary and no longer relies on a separate Perl installation. - For modern Go versions,
pprof
is 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)