こんにちは。エンジニアの吉田です。

今回は久々にPHPに関する話題を書いてみるです。(PHPが本業のくせに久々とは何事か。。)


Windows上のPHPでpreg_match_all()した際、マッチ対象文字列が大きいと、Apacheごと
プロセスがクラッシュしてしまう事象に遭遇したので、原因を調べてみました。

ちなみに、動作環境は、Windows 7 + XAMPP 1.7.4 です。


詳細は続きから。



Apacheが落ちたという事を確認する


XAMPPのApacheが落ちた場合、Firefoxの場合だと「接続がリセットされました」
「ページの読み込み中にサーバへの接続がリセットされました。」といった感じで、
サーバに接続できなかったかのような画面が表示されました。

Apacheのエラーログを見てみると、下記のようなメッセージが表示されており、
1行目の内容から「Apacheの子プロセスが落ちたので再起動するよ」という挙動が伺えます。

[Sun Mar 25 16:46:04 2012] [notice] Parent: child process exited with status 3221225725 -- Restarting.
[Sun Mar 25 16:46:04 2012] [notice] Digest: generating secret for digest authentication ...
[Sun Mar 25 16:46:04 2012] [notice] Digest: done
[Sun Mar 25 16:46:04 2012] [notice] Apache/2.2.17 (Win32) mod_ssl/2.2.17 OpenSSL/0.9.8o PHP/5.3.4 configured -- resuming normal operations
[Sun Mar 25 16:46:04 2012] [notice] Server built: Oct 18 2010 01:58:12
[Sun Mar 25 16:46:04 2012] [notice] Parent: Created child process 5160
[Sun Mar 25 16:46:05 2012] [notice] Digest: generating secret for digest authentication ...
[Sun Mar 25 16:46:05 2012] [notice] Digest: done
[Sun Mar 25 16:46:05 2012] [notice] Child 5160: Child process is running
[Sun Mar 25 16:46:05 2012] [notice] Child 5160: Acquired the start mutex.
[Sun Mar 25 16:46:05 2012] [notice] Child 5160: Starting 150 worker threads.
[Sun Mar 25 16:46:05 2012] [notice] Child 5160: Starting thread to listen on port 443.
[Sun Mar 25 16:46:05 2012] [notice] Child 5160: Starting thread to listen on port 10081.
[Sun Mar 25 16:46:05 2012] [notice] Child 5160: Starting thread to listen on port 80.


クラッシュした箇所を特定する


なんかApacheが落ちたけど、どこで落ちたのかがわからないと調査を進められません。

というわけで、今回はEclipseとXDebugでステップ実行して落ちた場所を特定しました。

※デバッグのステップ実行については以前のブログで書いたので割愛です。
  http://www.s-arcana.co.jp/tech/2011/02/eclipse-pdt-xdebug-php.html

ステップ実行をした結果、 preg_match_all() で落ちていることが判明。


クラッシュした原因をググってみる


同じ事象に遭遇している人がいないかどうかをググってみます。
「3221225725」で検索してみると、下記のようなサイトが見つかりました。

  Apache2(Win32) のスタックサイズを増やす。-徒然日記/2005-02-07-Wiki [自由帳] - XOOPSマニア
    http://xoops.hypweb.net/modules/pukiwiki/1723.html

  PHP :: Bug #60231 :: child process exited with status 3221225725
    https://bugs.php.net/bug.php?id=60231

どうやら、スタックが足りずに落ちているような雰囲気の方がいるようです。


スタックが足りずにクラッシュしたかどうかを確認する


ググってみた結果、スタックが足りなくなりApacheがクラッシュしてしまったという説が
有力ですが、自分のクラッシュの理由が本当に同じかどうかを確認する必要があります。
※似てるけど違う事象の可能性もありますので、裏付けが必要です。

というわけで、PHPがクラッシュした際のバックトレースを取得してみます。

詳しい手順は下記のサイトに載っていたので、こちらのサイトを参照のこと。

  PHP :: Generating a backtrace on Win32
   https://bugs.php.net/bugs-generating-backtrace-win32.php


こちらのサイトによると、バックトレース生成には下記4つが必要です。

- PHP実行環境
- PHP Debug Pack
- Microsoft Debug Diagnostic Tools
- PHPをクラッシュするEvilなコード


PHP Debug Packは下記のサイトからダウンロード可能です。

  PHP For Windows: Binaries and sources Snapshots
    http://windows.php.net/snapshots/

  古いバージョンのPHPは下記のサイトにアーカイブされています。
    http://windows.php.net/downloads/releases/archives/

私の環境のXAMPPは、PHP5.3.5で、MSVC6(Visual C++ 6.0)でコンパイル
されていましたので、アーカイブサイトの php-debug-pack-5.3.5-Win32-VC6-x86.zip
というファイルを使用しました。
※MSVC6のようなコンパイラの情報は、phpinfo() の出力で確認可能です。


Microsoft Debug Diagnostic Toolsは下記のサイトからダウンロード可能です。

  Debug Diagnostic Tool v1.2
    http://www.microsoft.com/Download/en/details.aspx?id=26798


Debug Diagnostic Toolの設定については、先述した下記サイトに載っているため割愛します。

  PHP :: Generating a backtrace on Win32
   https://bugs.php.net/bugs-generating-backtrace-win32.php


Debug Diagnostic Toolの設定が完了すると、Apacheのプロセスがクラッシュした際に
ダンプファイルが生成されるようになります。

また、ダンプファイルを分析する機能も備わっており、HTMLファイルにてレポートを
生成することも可能です。




今回のクラッシュでは、下記のようなレポートが出力されましたので、予測通り
スタックオーバーフローが原因でApacheのプロセスがクラッシュしたと考えられます。

In httpd__PID__7012__Date__03_25_2012__Time_05_54_08PM__410__Second_Chance_Exception_C00000FD.dmp 
the assembly instruction at php5ts!match+6 in C:\xampp\php\php5ts.dll has caused a stack overflow
exception (0xC00000FD) when trying to write to memory location 0x050d2ed4 on thread


スタックサイズを大きくして対応する


スタックオーバーフローでApacheが落ちたと原因が特定できたので、対策を講じます。

少し調べてみると、Windowsではeditbinというコマンドでスタックサイズを変更できるようです。
※ editbinコマンドはVisual Studioに同梱されているコマンドラインツールです。

というわけで、現状のスタックサイズを確認し、スタックサイズを変更し、変更が適用
されたかどうかを確認してみましょう。



●現状のスタックサイズを確認する


   実行
  dumpbin /headers httpd.exe

   結果
(~略~)
OPTIONAL HEADER VALUES
           (~略~)
           40000 size of stack reserve
(~略~)
※40000は16進数のため、10進数になおすと262144 (256*1024) → 256k


●スタックサイズを変更する


   実行
  editbin /STACK:524288 httpd.exe
※512kにするため、512*1024=524288 を指定。


●変更後のスタックサイズを確認する


   実行
  dumpbin /headers httpd.exe

   結果
(~略~)
OPTIONAL HEADER VALUES
           (~略~)
           80000 size of stack reserve
(~略~)
※16進数の80000を10進数になおすと 524288 (512*1024) → 512k


これで、Apacheがクラッシュしないようになりました。

久しぶりの低レイヤのエラー調査はなかなか骨が折れたです。。

デワデワ。

 
Recent Comment
Categories
Writer
  • hidetarou
  • acha_maro
  • gazza069
  • sin_ya
  • yossy222
  • marionnettezero
  • TUYO
  • shiovo
  • yani_arcana
  • momiji0510
  • keiko_www
  • toma_max
  • kyonmaru
  • __senta
  • kenzo
  • t_suzuki
  • hikari_f
  • macchii
  • mashu