web-dev-qa-db-ja.com

Perl CGIスクリプトをトラブルシューティングするにはどうすればよいですか?

動作していないPerlスクリプトがあり、問題の絞り込みを開始する方法がわかりません。私に何ができる?


注:Stackoverflowに非常に長い回答を追加したいので、質問を追加しています。私は他の答えで外部とリンクし続けており、ここにいるに値する。追加するものがある場合は、私の答えを編集することを恥ずかしがらないでください。

99
brian d foy

この回答は、Perl CGIスクリプトの問題を解決するための一般的なフレームワークとして意図されており、Perlmonksでは Troubleshooting Perl CGI Scripts として最初に登場しました。それはあなたが遭遇するかもしれないすべての問題への完全なガイドでも、バグ潰しに関するチュートリアルでもありません。これは、CGIスクリプトを20年間(プラス!)デバッグした経験の集大成です。このページには多くの異なる家があったようで、それが存在することを忘れているようですので、StackOverflowに追加しています。コメントや提案は[email protected]に送ってください。コミュニティWikiでもありますが、あまり気にしないでください。 :)


問題を見つけるのに役立つPerlの組み込み機能を使用していますか?

警告をオンにして、コードの疑わしい部分についてPerlに警告させます。コマンドラインから_-w_スイッチを使用してこれを行うことができるため、コードを変更したり、すべてのファイルにプラグマを追加したりする必要はありません。

_ % Perl -w program.pl
_

ただし、すべてのファイルにwarningsプラグマを追加して、疑わしいコードを常に消去するように強制する必要があります。

_ use warnings;
_

短い警告メッセージよりも多くの情報が必要な場合は、diagnosticsプラグマを使用して詳細情報を取得するか、 perldiag ドキュメントを参照してください。

_ use diagnostics;
_

最初に有効なCGIヘッダーを出力しましたか?

サーバーは、CGIスクリプトからの最初の出力がCGIヘッダーであることを期待しています。通常、これは_print "Content-type: text/plain\n\n";_のように単純な場合もあれば、 CGI.pm とその派生物print header()を使用する場合もあります。一部のサーバーは、(STDERRで)標準出力の前に表示される(STDOUTで)エラー出力に敏感です。

エラーをブラウザに送信してみてください

この行を追加

_ use CGI::Carp 'fatalsToBrowser';
_

あなたのスクリプトに。これにより、コンパイルエラーがブラウザウィンドウに送信されます。余分な情報はセキュリティリスクになる可能性があるため、運用環境に移行する前にこれを必ず削除してください。

エラーログには何と書かれていましたか?

サーバーはエラーログを保持します(または、少なくとも保持する必要があります)。サーバーおよびスクリプトからのエラー出力がそこに表示されるはずです。エラーログを見つけて、その内容を確認してください。ログファイルの標準的な場所はありません。サーバーの構成で場所を確認するか、サーバー管理者に問い合わせてください。 CGI :: Carp などのツールを使用して、独自のログファイルを保持することもできます。

スクリプトの権限は何ですか?

「アクセス許可が拒否されました」または「メソッドが実装されていません」などのエラーが表示される場合は、おそらく、Webサーバーユーザーがスクリプトを読み取りおよび実行できないことを意味します。 Unixのフレーバーでは、モードを755に変更することをお勧めします:_chmod 755 filename_。モードを777に設定しないでください!

_use strict_を使用していますか?

Perlは、最初に変数を使用するときに自動的に変数を作成することに注意してください。これは機能ですが、変数名を誤って入力するとバグが発生する場合があります。プラグマ _use strict_ は、これらの種類のエラーを見つけるのに役立ちます。慣れるまで面倒ですが、しばらくするとプログラミングが大幅に改善され、さまざまな間違いをすることができます。

スクリプトはコンパイルされますか?

_-c_スイッチを使用して、コンパイルエラーを確認できます。報告された最初のエラーに集中します。すすぎ、繰り返します。本当に奇妙なエラーが発生している場合は、スクリプトに正しい行末があることを確認してください。バイナリモード、CVSからのチェックアウト、または行末変換を処理しない他の何かでFTPを使用する場合、Webサーバーはスクリプトを1つの大きな行と見なすことがあります。 ASCIIモードでPerlスクリプトを転送します。

スクリプトは安全でない依存関係について文句を言っていますか?

スクリプトが安全でない依存関係について文句を言う場合は、おそらく_-T_スイッチを使用して汚染モードをオンにします。文句を言うなら、もっと安全なスクリプトを書くのを助けるために仕事をしている。プログラムの外部(つまり、環境)からのデータはすべて汚染されていると見なされます。 PATHや_LD_LIBRARY_PATH_などの環境変数は特に面倒です。私が推奨するように、これらを安全な値に設定するか、完全に設定解除する必要があります。とにかく絶対パスを使用する必要があります。汚染チェックが他の何かについて不平を言う場合、データを汚染していないことを確認してください。詳細については、 perlsec manページを参照してください。

コマンドラインから実行するとどうなりますか?

スクリプトは、コマンドラインから実行したときに期待したものを出力しますか?ヘッダーが最初に出力され、その後に空白行が続きますか?端末(対話型セッションなど)を使用している場合は、STDERRSTDOUTとマージされる可能性があり、バッファリングにより混乱した順序で表示される場合があることに注意してください。 _$|_をtrue値に設定して、Perlの自動フラッシュ機能をオンにします。通常、CGIプログラムで_$|++;_が表示される場合があります。設定すると、すべての印刷と書き込みはバッファリングされるのではなく、すぐに出力に送られます。ファイルハンドルごとにこれを設定する必要があります。 selectを使用して、次のようにデフォルトのファイルハンドルを変更します。

_$|++;                            #sets $| for STDOUT
$old_handle = select( STDERR );  #change to STDERR
$|++;                            #sets $| for STDERR
select( $old_handle );           #change back to STDOUT
_

いずれにしても、最初に出力されるのはCGIヘッダーで、その後に空白行が続くはずです。

CGIのような環境でコマンドラインから実行するとどうなりますか?

通常、Webサーバー環境はコマンドライン環境よりもはるかに制限されており、リクエストに関する追加情報があります。スクリプトがコマンドラインから正常に実行される場合は、Webサーバー環境をシミュレートしてみてください。問題が表示される場合、環境に問題があります。

これらの変数を設定解除または削除します

  • PATH
  • _LD_LIBRARY_PATH_
  • すべての_Oracle_*_変数

これらの変数を設定します

  • _REQUEST_METHOD_(必要に応じてGETHEAD、またはPOSTに設定)
  • _SERVER_PORT_(通常は80に設定)
  • _REMOTE_USER_(保護されたアクセスを行う場合)

_CGI.pm_(> 2.75)の最近のバージョンでは、古い(有用な)動作を得るために_-debug_フラグが必要なので、それを_CGI.pm_インポートに追加する必要があるかもしれません。

_use CGI qw(-debug)
_

die()またはwarnを使用していますか?

これらの関数は、再定義しない限り、STDERRに出力されます。 CGIヘッダーも出力しません。 CGI :: Carp などのパッケージでも同じ機能を利用できます

ブラウザのキャッシュをクリアするとどうなりますか?

スクリプトが正しいことを実行していると思われ、リクエストを手動で実行すると正しい出力が得られる場合、ブラウザが原因である可能性があります。キャッシュをクリアし、テスト中にキャッシュサイズをゼロに設定します。いくつかのブラウザは本当に愚かで、あなたがそうするように言ったとしても実際には新しいコンテンツをリロードしないことを覚えておいてください。これは、URLパスは同じであるが、コンテンツが変更されている場合(動的画像など)に特によく見られます。

スクリプトはあなたが思うところですか?

スクリプトへのファイルシステムパスは、必ずしもスクリプトへのURLパスに直接関連しているわけではありません。これをテストするために短いテストスクリプトを作成する必要がある場合でも、正しいディレクトリがあることを確認してください。さらに、正しいファイルを変更していると確信していますか?変更による効果が見られない場合は、別のファイルを変更しているか、間違った場所にファイルをアップロードしている可能性があります。 (これは、ちなみに、このようなトラブルの最も頻繁な原因です。)

_CGI.pm_ 、またはその派生物を使用していますか?

問題がCGI入力の解析に関連しており、_CGI.pm_、 _CGI::Request_などの広くテストされたモジュールを使用していない場合_CGI::Simple_ または _CGI::Lite_ を使用して、モジュールを使用して生活を続けます。 _CGI.pm_には_cgi-lib.pl_互換モードがあり、古いCGIパーサーの実装による入力の問題を解決するのに役立ちます。

絶対パスを使用しましたか?

system、バックティック、またはその他のIPC機能を使用して外部コマンドを実行している場合、外部プログラムへの絶対パスを使用する必要があります。読み取りまたは書き込み用にファイルを開く場合は、絶対パスを使用します。CGIスクリプトは、現在のディレクトリについて、あなたとは異なる考えを持っている場合があります。明示的なchdir()を使用すると、適切な場所に配置できます。

戻り値を確認しましたか?

ほとんどのPerl関数は、機能しているかどうかを通知し、失敗すると_$!_を設定します。戻り値を確認し、エラーメッセージの_$!_を調べましたか? evalを使用していた場合、_$@_を確認しましたか?

どのバージョンのPerlを使用していますか?

Perlの最新の安定バージョンは5.28です(または、最終編集日によって異なります)。古いバージョンを使用していますか? Perlのバージョンによって、警告の考え方が異なる場合があります。

どのWebサーバーを使用していますか?

同じ状況でも、異なるサーバーが異なる動作をする場合があります。同じサーバー製品は、構成が異なると動作が異なる場合があります。ヘルプのリクエストには、できるだけ多くの情報を含めてください。

サーバーのドキュメントを確認しましたか?

真面目なCGIプログラマーは、サーバーの機能と動作だけでなく、ローカルの構成も含めて、サーバーについて可能な限り詳しく知っている必要があります。商用製品を使用している場合、サーバーのドキュメントが利用できない場合があります。それ以外の場合、ドキュメントはサーバー上にあります。そうでない場合は、Webで探してください。

_comp.infosystems.www.authoring.cgi_ のアーカイブを検索しましたか?

これは有用ですが、すべての良いポスターは死んだかさまよいました。

誰かが以前にあなたの問題を抱えていた可能性があり、誰か(おそらく私)がこのニュースグループでそれに答えた可能性があります。このニュースグループは全盛期を過ぎましたが、過去から集められた知恵が役立つ場合があります。

短いテストスクリプトで問題を再現できますか?

大規模なシステムでは、非常に多くのことが起こっているため、バグを追跡することは困難です。可能な限り短いスクリプトで問題の動作を再現してみてください。問題を知ることはほとんどの修正です。これは確かに時間がかかるかもしれませんが、まだ問題を発見しておらず、オプションが不足しています。 :)

映画を見に行くことにしましたか?

真剣に。問題に巻き込まれて「知覚的狭小化」(トンネルビジョン)が発生することがあります。 [Duke Nukem、Quake、Doom、Halo、COD]で休憩したり、コーヒーを飲んだり、悪者を爆破したりすると、問題に再アプローチするために必要な新鮮な視点が得られます。

問題を発声しましたか?

真剣に。問題を声に出して説明することで、私たち自身の答えが得られることがあります。あなたの同僚は聞いていないので、ペンギン(ぬいぐるみ)と話してください。深刻なデバッグツールとしてこれに興味がある場合(そして今までに問題を発見していない場合はお勧めします)、 The Psychology of Computer Programming

125
brian d foy

CGI :: Debug も言及する価値があると思います。

10
Mikael S

RemotePortと呼ばれるPERLDB_OPTSオプションに誰も言及していないのはなぜだろうか。確かに、Webには実例が多くありません(RemotePortperldebug でも言及されていません)-これを思い付くのはちょっと問題がありました1つですが、ここに行きます(Linuxの例です)。

適切な例を実行するには、最初に、できれば1つのコマンドラインを使用して、CGI Webサーバーの非常に単純なシミュレーションを実行できるものが必要でした。 cgisを実行するためのシンプルなコマンドラインWebサーバーを見つけた後。 (perlmonks.org)IO :: All-Tiny Web Server がこのテストに適用可能であることがわかりました。

ここでは、/tmpディレクトリで作業します。 CGIスクリプトは/tmp/test.pl(以下に含まれます)になります。 IO::AllサーバーはCGIと同じディレクトリにある実行可能ファイルのみを提供するため、ここではchmod +x test.plが必要です。したがって、通常のCGIテストを実行するには、ターミナルでディレクトリを/tmpに変更し、そこでワンライナーWebサーバーを実行します。

$ cd /tmp
$ Perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'

Webserverコマンドはターミナルでブロックし、そうでなければWebサーバーをローカル(127.0.0.1またはlocalhost)で起動します-その後、Webブラウザーに移動してこのアドレスを要求できます。

http://127.0.0.1:8080/test.pl

...そして、test.plによって作成されたprintsがWebブラウザーにロードされ、表示されるのを観察する必要があります。


さて、このスクリプトをRemotePortでデバッグするには、まずネットワーク上にlistenerが必要です。これを介してPerlデバッガーと対話します。コマンドラインツールnetcatncを使用できます。ここで確認できました: Perl如何remote debug? )。したがって、最初に1つの端末でnetcatリスナーを実行します。このポートでは、ポート7234(デバッグポート)での接続をブロックして待機します。

$ nc -l 7234

次に、test.plが呼び出されたときに、PerlRemotePortでデバッグモードで開始する必要があります(CGIモードでも、サーバー経由で)。これは、Linuxでは、次の「Shebang wrapper」スクリプトを使用して実行できます-ここでも/tmpにあり、mustを実行可能にする必要があります。

cd /tmp

cat > perldbgcall.sh <<'EOF'
#!/bin/bash
PERLDB_OPTS="RemotePort=localhost:7234" Perl -d -e "do '$@'"
EOF

chmod +x perldbgcall.sh

これはちょっとしたトリッキーなものです- シェルスクリプトを参照してください-シェバンで環境変数を使用するにはどうすればよいですか? -UnixおよびLinux Stack Exchange 。しかし、ここでのトリックはtest.plを処理するPerlインタープリターをフォークするnotのようです-一度ヒットしたら、execですが、代わりにPerlを「プレーンに」呼び出し、基本的にdoを使用してtest.plスクリプトを「ソース」にします( Perlスクリプトを実行するにはPerlスクリプト内? )。

perldbgcall.sh/tmpがあるので、test.plファイルを変更して、Shebang行でこの実行可能ファイルを参照できるようにします(通常のPerlインタープリターではなく)。 /tmp/test.plこのように変更:

#!./perldbgcall.sh

# this is test.pl

use 5.10.1;
use warnings;
use strict;

my $b = '1';
my $a = sub { "hello $b there" };
$b = '2';
print "YEAH " . $a->() . " CMON\n";
$b = '3';
print "CMON " . &$a . " YEAH\n";

$DB::single=1;  # BREAKPOINT

$b = '4';
print "STEP " . &$a . " NOW\n";
$b = '5';
print "STEP " . &$a . " AGAIN\n";

現在、test.plとその新しいシバンハンドラーperldbgcall.shの両方が/tmpにあります。ポート7234でデバッグ接続をリッスンするncがあるため、最終的に別のターミナルウィンドウを開き、ディレクトリを/tmpに変更し、ワンライナーWebサーバーを実行します(ポートでWeb接続をリッスンします) 8080)そこ:

cd /tmp
Perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'

これが完了したら、Webブラウザーに移動して、同じアドレスhttp://127.0.0.1:8080/test.plを要求できます。ただし、Webサーバーがスクリプトを実行しようとすると、perldbgcall.sh Shebangを介して実行され、リモートデバッガーモードでPerlが開始されます。したがって、スクリプトの実行は一時停止します。したがって、Webブラウザーはロックされ、データを待機します。これでnetcat端子に切り替えることができ、おなじみのPerlデバッガーテキストが表示されるはずです-ただし、ncを介して出力されます。

$ nc -l 7234

Loading DB routines from Perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1):   do './test.pl'
  DB<1> r
main::(./test.pl:29):   $b = '4';
  DB<1>

スニペットが示すように、基本的にncを「端末」として使用します。したがって、「run」にr(およびEnter)を入力できます。スクリプトはブレークポイントステートメントを実行します(参照また、 Perlでは、$ DB :: single = 1と2の違いは何ですか? )、再び停止する前に(その時点で、ブラウザはまだロックします)。

そのため、残りのtest.plからncターミナルへと進むことができます。

....
main::(./test.pl:29):   $b = '4';
  DB<1> n
main::(./test.pl:30):   print "STEP " . &$a . " NOW\n";
  DB<1> n
main::(./test.pl:31):   $b = '5';
  DB<1> n
main::(./test.pl:32):   print "STEP " . &$a . " AGAIN\n";
  DB<1> n
Debugged program terminated.  Use q to quit or R to restart,
  use o inhibit_exit to avoid stopping after program termination,
  h q, h R or h o to get additional info.
  DB<1>

...ただし、この時点でも、ブラウザーはデータをロックして待機します。 qでデバッガーを終了した後にのみ:

  DB<1> q
$

...ブラウザはロックを停止します-最後にtest.plの(完全な)出力を表示します:

YEAH hello 2 there CMON
CMON hello 3 there YEAH
STEP hello 4 there NOW
STEP hello 5 there AGAIN

もちろん、この種のデバッグはWebサーバーを実行しなくても実行できます。ただし、ここでのすばらしいことは、Webサーバーにまったく触れないことです。 Webブラウザーから「ネイティブ」に実行をトリガーします(CGIの場合)。CGIスクリプト自体に必要な変更は、Shebangの変更(そしてもちろん、Shebangラッパースクリプトが存在することです。ディレクトリ)。

さて、これが誰かの助けになることを願っています-自分で:)と書く代わりに、これにつまずいたのは間違いないでしょう。
乾杯!

8
sdaau
デバッグ中にエラーハンドラを使用していますか?

dieステートメントやその他の致命的な実行時およびコンパイル時のエラーはSTDERRに出力されます。スクリプトのデバッグ中に、致命的なエラーメッセージをブラウザに表示することをお勧めします。

これを行う1つの方法は、

   use CGI::Carp qw(fatalsToBrowser);

スクリプトの上部。 その呼び出し は、$SIG{__DIE__}ハンドラーをインストールします( perlvar を参照)。必要に応じて有効なヘッダーを先頭に追加して、ブラウザーに致命的なエラーを表示します。 CGI::Carpを聞いた前に使用した別のCGIデバッグトリックは、スクリプトのevalおよび__END__機能でDATAを使用して、コンパイル時エラーをキャッチすることでした:

   #!/usr/bin/Perl
   eval join'', <DATA>;
   if ($@) { print "Content-type: text/plain:\n\nError in the script:\n$@\n; }
   __DATA__
   # ... actual CGI script starts here

このより冗長な手法は、CGI::Carpよりもわずかに利点があり、コンパイル時エラーをより多くキャッチします。

更新:使用したことはないが、Mikael Sが示唆したように CGI::Debug のように見えるまた、この目的のための非常に便利で設定可能なツールです。

7
mob

私の場合、 log4Perl を使用します。とても便利で簡単です。

use Log::Log4Perl qw(:easy);

Log::Log4Perl->easy_init( { level   => $DEBUG, file    => ">>d:\\tokyo.log" } );

my $logger = Log::Log4Perl::get_logger();

$logger->debug("your log message");
5
zawhtut

正直なところ、この投稿の上のすべての楽しいことをすることができます。もっとも、私が見つけた最も単純で最も積極的な解決策は、単に「印刷」することでした。

例:(通常のコード)

`$somecommand`;

私が本当にやりたいことをやっているかどうかを確認するには:(トラブルシューティング)

print "$somecommand";
1
Ilan Kleiman

コマンドラインからPerlスクリプトを実行すると、Perlはエラーが発生した行を常に通知することにも言及する価値があるでしょう。 (たとえば、SSHセッション)

他のすべてが失敗した場合、通常これを行います。サーバーにSSHで接続し、Perlスクリプトを手動で実行します。例えば:

% Perl myscript.cgi 

問題がある場合、Perlがそのことを通知します。このデバッグ方法は、ファイル許可関連の問題やWebブラウザーまたはWebサーバーの問題を排除します。

1
gpwr

以下のコマンドを使用して、ターミナルでPerl cgi-scriptを実行できます。

 $ Perl filename.cgi

コードを解釈し、結果をHTMLコードで提供します。エラーがあれば報告します。

0
D.Karthikeyan