2009年4月24日

Tips

PHPでのデバッグ方法

  • このエントリーをはてなブックマークに追加

おひさしぶりです。オークション担当の山崎 賢です。

前回はPHP Serialize についてでしたが、 今回はPHPでのデバッグに関してお話します。

基本PHPはインタプリタ(厳密にはPHPは内部で一度コンパイルしていますのでインタプリタとは言い切れませんが) のデバッグではログ埋め込みが手軽です。

しかし、まれにSIGSEGVやSIGBUSなどでPHPスクリプトが落ちることがあり、途方にくれます。 地道にログを埋め込んでいき、箇所を特定するのも手法の1つですが、今回はgdbを用いたデバッグ方法を記載したいと思います。

■STEP1

まずは、プログラムが落ちることを目的として以下のようなPHP Moduleを作成します。

・
・
extern "C" {
ZEND_FUNCTION(down_method);
}
function_entry Down_functions[] =
{
    PHP_FE(down_method, NULL)
    {NULL, NULL, NULL}
};
PHP_MINIT_FUNCTION(Down) ;
PHP_MSHUTDOWN_FUNCTION(Down) ;
zend_module_entry Down_module_entry =
{
    STANDARD_MODULE_HEADER,
    "Down",
    Down_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NO_VERSION_YET,
    STANDARD_MODULE_PROPERTIES
};
PHP_FUNCTION(down_method)
{
    char *ptr = NULL ;
    strcpy(ptr, "down") ;
}

作成したPHP ModuleをPHP実行環境に導入します。

■STEP2

上記、「必ずNULLアドレスを参照して落ちる」関数を呼ぶPHPスクリプトを作成します。 ここでは/home/down.phpという名前で保存したと仮定します。

<?php
down_method();
?>

■STEP3

いよいよ実行です。

PHPが/usr/local/binにインストールされていると仮定しています。

/usr/local/bin/php /home/down.php
Segmentation fault (core dumped)

見事に落ちてくれました。では、いよいよgdbで解析していきましょう。

■STEP4

・GDB起動

{gdb-path}/gdb

・コマンド読み込み

(gdb) file /usr/local/bin/php

・引数を渡して実行

(gdb) run /home/down.php
Program received signal SIGSEGV, Segmentation fault.
0x21c03c2d in zif_down_method (ht=0, return_value=0x9baccc, return_value_ptr=0x0, this_ptr=0x0, return_value_used=0)
at Down.cc:32

実行しただけで、PHP Moduleの何行目で落ちたのかまで情報が出力されます。
#作成したPHP Moduleがstripされていないことが必須です。

今回は、自作のPHP Moduleですので落ちた箇所を見つけ易いですが、 さらに踏み込んでPHPスクリプトの落ちている箇所まで特定していきます。

・PHPスクリプト上での落ちた箇所の特定

(gdb) print (char *)(executor_globals.function_state_ptr->function)->common.function_name
$1 = 0x21c2314a "down_method"

これで落ちた関数名までが判明します。
また、落ちたファイル名は

(gdb) print (char *)executor_globals.active_op_array->filename
$3 = 0x9c7ccc "/home/down.php"

で確認可能です。

実際に落ちた行は

print executor_globals.current_execute_data.opline->lineno
$5 = 2

と、確認可能です。

以上の手順で

  • 何のファイルの
  • 何の関数の
  • 何行目で落ちたのか

まで確認可能です。

■おまけ

apache組み込みDSOのPHPを用いて、ウェブアプリケーションとしてPHPを利用している場合にも、 以下のようにGDBを利用可能です。

あらかじめデバッグの前に、apacheのMaxClientを1に設定しておくと、 子プロセスのPIDを特定できるのでよりデバッグしやすくなります。

{gdb-path}/gdb /usr/local/bin/apache [apache子プロセスID]
(gdb) continue
    --- ここまで実行したら、ブラウザで問題の発生している画面を表示します
Program received signal SIGSEGV, Segmentation fault.
0x21d94c2d in zif_down_method (ht=0, return_value=0x81010c, return_value_ptr=0x0, this_ptr=0x0, return_value_used=0)
    at Down.cc:32

このようにSIGSEGVをGDBが検出しますので、後は同じようにPHPの行数などを求めて
いってください。

このように、シグナル検出によるプログラム不正終了も簡単に確認が可能ですのでぜひお試しください。

Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

  • このエントリーをはてなブックマークに追加