[インデックス 17436] ファイルの概要
このコミットは、Go言語のプロファイリングツールであるpprof
スクリプト(Perlで書かれている)が、HTTPリクエストを行う際にLWP::UserAgent
モジュールとcurl
コマンドの両方に対応するように改善したものです。これにより、異なるオペレーティングシステム(特にWindowsとmacOS)での互換性が向上し、ユーザーがプロファイリングデータをより確実に取得できるようになります。
コミット
commit ccfe1bfd92461e2743d1004da0365ac2b33f2a6a
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Thu Aug 29 16:42:13 2013 -0700
misc/pprof: work with either LWP::UserAgent or curl
Use either LWP::UserAgent or curl to make HTTP requests so it
works on Windows (most Perl distros include LWP::UserAgent),
and also on OS X (whose Perl at least sometimes doesn't
include LWP::UserAgent).
Fixes #6273
R=golang-dev, alex.brainman, cldorian
CC=golang-dev
https://golang.org/cl/13330044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ccfe1bfd92461e2743d1004da0365ac2b33f2a6a
元コミット内容
misc/pprof: work with either LWP::UserAgent or curl
Use either LWP::UserAgent or curl to make HTTP requests so it
works on Windows (most Perl distros include LWP::UserAgent),
and also on OS X (whose Perl at least sometimes doesn't
include LWP::UserAgent).
Fixes #6273
変更の背景
この変更の背景には、Go言語のプロファイリングツールであるpprof
スクリプトが、異なるオペレーティングシステム環境下でHTTPリクエストを正常に実行できないという問題がありました。具体的には、以下の点が挙げられます。
- Windows環境での問題: 多くのPerlディストリビューションには
LWP::UserAgent
モジュールが含まれているため、Windows上ではこのモジュールを使用してHTTPリクエストを行うことが期待されていました。しかし、一部の環境では問題が発生する可能性がありました。 - macOS環境での問題: macOSにプリインストールされているPerl環境では、
LWP::UserAgent
モジュールが常に利用可能であるとは限りませんでした。これにより、macOSユーザーがpprof
を使用してリモートプロファイリングを行う際に、HTTPリクエストが失敗するという問題に直面していました。 - 互換性の向上:
pprof
スクリプトがより多くの環境で安定して動作するように、HTTPリクエストの実行方法に柔軟性を持たせる必要がありました。既存のLWP::UserAgent
に加えて、広く利用されているコマンドラインツールであるcurl
を代替手段として導入することで、モジュールの有無に依存しない堅牢なメカニズムを提供することが目的でした。
この変更は、Go issue #6273で報告された問題を解決するために行われました。ユーザーが直面していたプロファイリングデータの取得に関する問題を解消し、pprof
ツールの利便性と信頼性を向上させることが、このコミットの主要な動機となっています。
前提知識の解説
このコミットを理解するためには、以下の技術要素に関する知識が役立ちます。
Go言語のpprofツール
pprof
は、Go言語のプログラムのパフォーマンスプロファイリングを行うためのツールです。CPU使用率、メモリ割り当て、ゴルーチン、ブロック、ミューテックス競合など、様々な種類のプロファイルデータを収集し、視覚化することができます。GoプログラムはHTTPエンドポイントを通じてプロファイルデータを提供し、pprof
スクリプト(このコミットで変更されているPerlスクリプト)がそのデータを取得して解析します。
Perlスクリプト
このコミットで変更されているmisc/pprof
ファイルは、Perlで書かれたスクリプトです。Perlは、テキスト処理やシステム管理タスクに広く使用される高水準の汎用プログラミング言語です。pprof
スクリプトは、Goプログラムからプロファイルデータを取得し、それを解析してグラフやテキスト形式で表示する役割を担っています。
LWP::UserAgent
LWP::UserAgent
は、Perlの標準的なモジュール群であるlibwww-perl
の一部であり、HTTPクライアント機能を提供します。これにより、PerlスクリプトからWebサーバーに対してHTTPリクエスト(GET、POSTなど)を送信し、そのレスポンスを受け取ることができます。PerlでWebコンテンツを取得したり、Web APIと連携したりする際によく使用されます。
curl
curl
は、URL構文を使用してデータを転送するためのコマンドラインツールおよびライブラリです。HTTP、HTTPS、FTPなど、多くのプロトコルをサポートしています。curl
は、スクリプトやコマンドラインからWebリソースにアクセスする際に非常に広く利用されており、特にHTTPリクエストのデバッグや自動化に便利です。多くのUnix系システム(macOSを含む)にプリインストールされており、Windowsでも利用可能です。
HTTP GET/POSTリクエスト
- GETリクエスト: サーバーからリソースを取得するために使用されます。URLのクエリパラメータを通じてデータを送信できます。
- POSTリクエスト: サーバーにデータを送信するために使用されます。通常、リクエストボディにデータを含めて送信し、新しいリソースの作成や既存リソースの更新などに使われます。
pprof
スクリプトは、Goプログラムからプロファイルデータを取得するためにこれらのHTTPリクエストを使用します。例えば、シンボル情報やプログラム名を取得するためにGETリクエストを、特定のプロファイルデータを取得するためにPOSTリクエストを使用することがあります。
技術的詳細
このコミットの技術的詳細の核心は、pprof
PerlスクリプトがHTTPリクエストを実行する際のフォールバックメカニズムを導入した点にあります。以前はLWP::UserAgent
モジュールに完全に依存していましたが、この変更により、LWP::UserAgent
が利用できない環境でもcurl
コマンドを代替として使用できるようになりました。
具体的な変更点は以下の通りです。
-
LWP::UserAgent
の依存関係の緩和:- スクリプトの冒頭から
use LWP::UserAgent;
の行が削除されました。これは、LWP::UserAgent
が常に利用可能であるという前提を取り除き、必要に応じて動的にロードする(またはフォールバックする)アプローチに切り替えるためです。
- スクリプトの冒頭から
-
FetchHTTP
サブルーチンの導入:- この新しいサブルーチンは、指定されたURLからHTTP GETリクエストを使用してコンテンツを取得します。
- 内部では、まず
eval "use LWP::UserAgent ();";
を使用してLWP::UserAgent
モジュールのロードを試みます。 LWP::UserAgent
のロードが成功した場合($@
が偽の場合)、従来のLWP::UserAgent
オブジェクトを作成し、get()
メソッドでリクエストを実行します。タイムアウトが指定されていればそれも設定します。LWP::UserAgent
のロードが失敗した場合($@
が真の場合)、curl
コマンドにフォールバックします。curl
コマンドは-s
(サイレントモード)オプションと、必要に応じて--max-time
オプション(タイムアウト用)を指定して実行されます。curl
の出力はすべて読み込まれ、エラーが発生した場合はプログラムが終了します。- どちらの方法でも、HTTPリクエストが成功しなかった場合はエラーメッセージを出力してプログラムを終了します。
-
PostHTTP
サブルーチンの導入:- このサブルーチンは、指定されたURLにHTTP POSTリクエストを送信し、
$post_data
をリクエストボディとして含めます。 FetchHTTP
と同様に、まずLWP::UserAgent
のロードを試みます。LWP::UserAgent
が利用可能な場合、HTTP::Request
オブジェクトを作成し、POST
メソッドと$post_data
を設定してリクエストを実行します。LWP::UserAgent
が利用できない場合、curl
コマンドにフォールバックします。curl
でPOSTデータを送信するために、$post_data
を一時ファイル($main::tmpfile_sym
)に書き込み、curl
の-d @filename
オプションを使用してそのファイルをリクエストボディとして送信します。これにより、コマンドライン引数の長さに制限されることなく、任意のサイズのPOSTデータを送信できます。- エラー処理も
FetchHTTP
と同様に行われます。
- このサブルーチンは、指定されたURLにHTTP POSTリクエストを送信し、
-
既存のHTTPリクエスト箇所の置き換え:
CheckSymbolPage
、FetchProgramName
、FetchSymbols
、FetchDynamicProfile
といった既存のサブルーチン内で直接LWP::UserAgent
を使用していた箇所が、新しく導入されたFetchHTTP
またはPostHTTP
サブルーチンへの呼び出しに置き換えられました。これにより、コードの重複が排除され、HTTPリクエストのロジックが一元化されました。
-
Init()
サブルーチンの修正:elsif (IsSymbolizedProfileFile($ARGV[0]))
の条件に$ARGV[0] &&
が追加されました。これは、コマンドライン引数が存在しない場合にIsSymbolizedProfileFile
が呼び出されるのを防ぐための、より堅牢なチェックです。
-
cleanup
サブルーチンの改善:- 一時ファイルの削除処理において、
unlink
の引数にdefined
チェックが追加されました。これにより、変数が未定義の場合にunlink
がエラーになるのを防ぎ、より安全なクリーンアップが保証されます。例えば、unlink($main::tmpfile_sym) if defined $main::tmpfile_sym;
のように変更されています。
- 一時ファイルの削除処理において、
この変更により、pprof
スクリプトは、ユーザーのシステムにLWP::UserAgent
がインストールされているかどうかにかかわらず、HTTPリクエストを実行できるようになり、より幅広い環境での互換性と信頼性が向上しました。
コアとなるコードの変更箇所
このコミットにおける主要なコードの変更箇所は、misc/pprof
ファイル内の以下の部分です。
-
LWP::UserAgent
のuse
文の削除:--- a/misc/pprof +++ b/misc/pprof @@ -79,7 +79,6 @@ use strict; use warnings; use Getopt::Long; use File::Temp; -use LWP::UserAgent; use File::Copy;
-
Init()
サブルーチンの条件修正:--- a/misc/pprof +++ b/misc/pprof @@ -502,7 +501,7 @@ sub Init() { # Remote profiling without a binary (using $SYMBOL_PAGE instead) if (IsProfileURL($ARGV[0])) { $main::use_symbol_page = 1; - } elsif (IsSymbolizedProfileFile($ARGV[0])) { + } elsif ($ARGV[0] && IsSymbolizedProfileFile($ARGV[0])) { $main::use_symbolized_profile = 1; $main::prog = $UNKNOWN_BINARY; # will be set later from the profile file }
-
HTTPリクエスト処理の共通化と
FetchHTTP
の導入:CheckSymbolPage
、FetchProgramName
、FetchDynamicProfile
内のLWP::UserAgent
を使ったHTTP GETリクエストがFetchHTTP
呼び出しに置き換えられました。- 例:
CheckSymbolPage
--- a/misc/pprof +++ b/misc/pprof @@ -2979,11 +2978,7 @@ sub CheckSymbolPage { my $url = SymbolPageURL(); print STDERR "Read $url\n"; - my $ua = LWP::UserAgent->new; - my $response = $ua->get($url); - error("Failed to get symbol page from $url\n") unless $response->is_success; - - my $line = $response->content; + my $line = FetchHTTP($url); $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines unless (defined($line)) { error("$url doesn't exist\n");
-
HTTP POSTリクエスト処理の共通化と
PostHTTP
の導入:FetchSymbols
内のLWP::UserAgent
を使ったHTTP POSTリクエストがPostHTTP
呼び出しに置き換えられました。
--- a/misc/pprof +++ b/misc/pprof @@ -3091,19 +3082,12 @@ sub FetchSymbols { $symbol_map = {}; my $post_data = join("+", sort((map {"0x" . "$_"} @pcs))); - open(POSTFILE, ">$main::tmpfile_sym"); - print POSTFILE $post_data; - close(POSTFILE); -\n my $url = SymbolPageURL(); - my $req = HTTP::Request->new(POST => $url); - $req->content($post_data); - my $lwp = LWP::UserAgent->new; - my $response = $lwp->request($req); + my $content = PostHTTP($url, $post_data); my $tmp_symbol = File::Temp->new()->filename; open(SYMBOL, ">$tmp_symbol"); - print SYMBOL $response->content; + print SYMBOL $content; close(SYMBOL); open(SYMBOL, "<$tmp_symbol") || error("$tmp_symbol");
-
FetchHTTP
サブルーチンの追加:--- a/misc/pprof +++ b/misc/pprof @@ -4680,12 +4658,59 @@ sub ConfigureTool { return $path; } -sub cleanup { - unlink($main::tmpfile_sym); - unlink(keys %main::tempnames); - if (defined($main::collected_profile)) { - unlink($main::collected_profile); +# FetchHTTP retrieves a URL using either curl or LWP::UserAgent. +# It returns the entire body of the page on success, or exits the program +# with an error message on any failure. +sub FetchHTTP { + my $url = shift; + my $timeout = shift; # optional, in seconds + eval "use LWP::UserAgent ();"; + if ($@) { + my @max; + push @max, "--max-time", $timeout if $timeout; + open(my $fh, "-|", "curl", @max, "-s", $url) or error("Neither LWP::UserAgent nor curl is installed: $!\\n"); + my $slurp = do { local $/; <$fh> }; + close($fh); + if ($? != 0) { + error("Error fetching $url with curl: exit $?") + } + return $slurp; + }\n+ my $ua = LWP::UserAgent->new;\n+ $ua->timeout($timeout) if $timeout;\n+ my $res = $ua->get($url);\n+ error("Failed to fetch $url\\n") unless $res->is_success();\n+ return $res->content(); +}
-
PostHTTP
サブルーチンの追加:--- a/misc/pprof +++ b/misc/pprof @@ -4680,12 +4658,59 @@ sub ConfigureTool { return $path; } -sub cleanup { - unlink($main::tmpfile_sym); - unlink(keys %main::tempnames); - if (defined($main::collected_profile)) { - unlink($main::collected_profile); +# ... (FetchHTTPのコード) ... + +sub PostHTTP { + my ($url, $post_data) = @_; + eval "use LWP::UserAgent ();"; + if ($@) { + open(POSTFILE, ">$main::tmpfile_sym"); + print POSTFILE $post_data; + close(POSTFILE); +\n+ open(my $fh, "-|", "curl", "-s", "-d", "\@$main::tmpfile_sym", $url) or error("Neither LWP::UserAgent nor curl is installed: $!\\n"); + my $slurp = do { local $/; <$fh> }; + close($fh);\n+ if ($? != 0) {\n+ error("Error fetching $url with curl: exit $?")\n+ }\n+ return $slurp;\n+ }\n+ my $req = HTTP::Request->new(POST => $url);\n+ $req->content($post_data);\n+ my $ua = LWP::UserAgent->new;\n+ my $res = $ua->request($req);\n+ error("Failed to POST to $url\\n") unless $res->is_success();\n+ return $res->content(); +}
-
cleanup
サブルーチンの安全性の向上:--- a/misc/pprof +++ b/misc/pprof @@ -4680,12 +4658,59 @@ sub ConfigureTool { return $path; } -sub cleanup { - unlink($main::tmpfile_sym); - unlink(keys %main::tempnames); - if (defined($main::collected_profile)) { - unlink($main::collected_profile); +# ... (FetchHTTP, PostHTTPのコード) ... + +sub cleanup {\n+ unlink($main::tmpfile_sym) if defined $main::tmpfile_sym;\n+ unlink(keys %main::tempnames) if %main::tempnames;\n+ unlink($main::collected_profile) if defined $main::collected_profile;
コアとなるコードの解説
このコミットのコアとなる変更は、HTTPリクエストを処理するための新しいサブルーチンFetchHTTP
とPostHTTP
の導入、そして既存のコードベースでのそれらの利用です。これにより、pprof
スクリプトはLWP::UserAgent
とcurl
のどちらか利用可能な方を使ってHTTP通信を行うことができるようになりました。
FetchHTTP
サブルーチン
このサブルーチンは、HTTP GETリクエストを処理します。
sub FetchHTTP {
my $url = shift;
my $timeout = shift; # optional, in seconds
eval "use LWP::UserAgent ();"; # LWP::UserAgentのロードを試みる
if ($@) { # ロードに失敗した場合 (LWP::UserAgentが利用できない場合)
my @max;
push @max, "--max-time", $timeout if $timeout; # タイムアウトがあればcurlのオプションに追加
open(my $fh, "-|", "curl", @max, "-s", $url) or error("Neither LWP::UserAgent nor curl is installed: $!\\n"); # curlコマンドを実行
my $slurp = do { local $/; <$fh> }; # curlの出力を全て読み込む
close($fh);
if ($? != 0) { # curlの実行が失敗した場合
error("Error fetching $url with curl: exit $?")
}
return $slurp; # curlの出力を返す
}
my $ua = LWP::UserAgent->new; # LWP::UserAgentが利用可能な場合
$ua->timeout($timeout) if $timeout; # タイムアウトを設定
my $res = $ua->get($url); # GETリクエストを実行
error("Failed to fetch $url\\n") unless $res->is_success(); # リクエストが成功しなかった場合エラー
return $res->content(); # レスポンスの内容を返す
}
eval "use LWP::UserAgent ();";
: これはPerlの強力な機能で、実行時にモジュールをロードしようとします。もしモジュールのロードに失敗した場合(例えば、モジュールがインストールされていない場合)、$@
変数にエラーメッセージが設定されます。if ($@)
:$@
が真(エラーが発生した)の場合、LWP::UserAgent
が利用できないと判断し、curl
コマンドにフォールバックします。open(my $fh, "-|", "curl", ...)
: これはPerlで外部コマンドを実行し、その標準出力をファイルハンドルとして読み込むための慣用句です。curl
コマンドは-s
(サイレントモード、プログレスバーなどを表示しない)と、オプションで--max-time
(タイムアウト)を指定して実行されます。do { local $/; <$fh> }
: これは、ファイルハンドルからすべての内容を一気に読み込むためのPerlのイディオムです。local $/;
はレコードセパレータを一時的に未定義にすることで、ファイル全体を一度に読み込ませます。if ($? != 0)
:$?
はPerlで最後に実行された外部コマンドの終了ステータスを保持します。0以外であればエラーが発生したことを意味します。LWP::UserAgent
の利用:$@
が偽の場合、LWP::UserAgent
が利用可能であるため、通常通りLWP::UserAgent
オブジェクトを作成し、get()
メソッドでHTTPリクエストを実行します。
PostHTTP
サブルーチン
このサブルーチンは、HTTP POSTリクエストを処理します。
sub PostHTTP {
my ($url, $post_data) = @_;
eval "use LWP::UserAgent ();"; # LWP::UserAgentのロードを試みる
if ($@) { # ロードに失敗した場合
open(POSTFILE, ">$main::tmpfile_sym"); # POSTデータを一時ファイルに書き込む
print POSTFILE $post_data;
close(POSTFILE);
open(my $fh, "-|", "curl", "-s", "-d", "\@$main::tmpfile_sym", $url) or error("Neither LWP::UserAgent nor curl is installed: $!\\n"); # curlコマンドで一時ファイルをPOSTデータとして送信
my $slurp = do { local $/; <$fh> };
close($fh);
if ($? != 0) {
error("Error fetching $url with curl: exit $?")
}
return $slurp;
}
my $req = HTTP::Request->new(POST => $url); # LWP::UserAgentが利用可能な場合
$req->content($post_data); # POSTデータを設定
my $ua = LWP::UserAgent->new;
my $res = $ua->request($req); # POSTリクエストを実行
error("Failed to POST to $url\\n") unless $res->is_success();
return $res->content();
}
PostHTTP
もFetchHTTP
と同様にLWP::UserAgent
の利用可能性をチェックします。curl
でのPOSTデータ送信:curl
でPOSTデータを送信する場合、-d
オプションを使用します。特に大きなデータや特殊文字を含むデータを送信する場合、-d @filename
の形式で一時ファイルからデータを読み込ませるのが一般的です。このコミットでは、$post_data
を$main::tmpfile_sym
という一時ファイルに書き込み、curl
にそのファイルを指定することで、安全かつ確実にPOSTデータを送信しています。HTTP::Request->new(POST => $url)
:LWP::UserAgent
を使用する場合、HTTP::Request
オブジェクトを作成し、そのcontent()
メソッドでPOSTデータを設定してからrequest()
メソッドで送信します。
その他の変更
Init()
の修正:$ARGV[0] && IsSymbolizedProfileFile($ARGV[0])
という変更は、コマンドライン引数$ARGV[0]
が存在するかどうかを先にチェックすることで、未定義値に対する関数呼び出しを防ぎ、スクリプトの堅牢性を高めています。cleanup
の修正:unlink($main::tmpfile_sym) if defined $main::tmpfile_sym;
のように、一時ファイルを削除する際に変数がdefined
(定義されている)かどうかを確認するようになりました。これにより、変数が未定義の状態でunlink
が呼び出されることによる警告やエラーを防ぎ、よりクリーンな終了処理を実現しています。
これらの変更により、pprof
スクリプトは、Perl環境に依存することなく、より多くのシステムで安定して動作するようになりました。
関連リンク
- Go issue #6273: https://github.com/golang/go/issues/6273
- Go CL 13330044: https://golang.org/cl/13330044
参考にした情報源リンク
- LWP::UserAgent - perldoc.perl.org: https://perldoc.perl.org/LWP/UserAgent
- curl man page: https://curl.se/docs/manpage.html
- Go pprof documentation: https://pkg.go.dev/runtime/pprof
- Perl eval function: https://perldoc.perl.org/functions/eval
- Perl open function: https://perldoc.perl.org/functions/open
- Perl special variable
$?
: https://perldoc.perl.org/perlvar#%24%3F - Perl special variable
$@
: https://perldoc.perl.org/perlvar#%24%40 - Perl
do { local $/; <$fh> }
idiom: https://stackoverflow.com/questions/1007048/what-does-do-local-slurp-do-in-perl - curl -d option: https://curl.se/docs/manpage.html#-d