unoh.github.com

Valgrindを使って楽々PHP拡張モジュール開発のデバッグ

Wed Jan 05 00:53:18 -0800 2011

明けましておめでとうございます。@emorinsです。


みなさんモジュール開発ではどのようにデバッギングしていますか?モジュールになると普段使い慣れたデバッグツールが使えず、手動の動作確認だけで済まされている方もいらっしゃるのではないでしょうか。


今回はC/C++開発ではメジャーなメモリデバッガ『Valgrind』をPHP拡張モジュールの開発で活用することで、普段使い慣れたツールでのデバッグの例をご紹介します。



1.PHP拡張モジュールの作成


まず開発用にPHPをデバッグモードでビルド。


#cd ./php-5.x.x
#./configure --enable-debug
#make
#make install


PHP拡張モジュールを作成


#cd ./ext
#./ext_skel --extname=zynga_func
#cd zynga_func


16行目から18行目のコメントアウトを外す


#emacs config.m
- dnl PHP_ARG_ENABLE(zynga_func, whether to enable zynga_func support,
- dnl Make sure that the comment is aligned:
- dnl [  --enable-zynga_func           Enable zynga_func support])
+ PHP_ARG_ENABLE(zynga_func, whether to enable zynga_func support,
+ Make sure that the comment is aligned:
+ [  --enable-zynga_func           Enable zynga_func support])


拡張モジュールのビルド


#phpize
#./configure
#make
#make install


php.iniに以下を追加


extension=zynga_func.so


同一ディレクトリ内に生成されているテスト用PHPコードを実行


#php zynga_func.php


以下のように出力されたらモジュールの正常な読み込みに成功。


Functions available in the test extension:
confirm_zynga_func_compiled

Congratulations! You have successfully modified ext/zynga_func/config.m4.
Module zynga_func is now compiled into PHP.


これで正常にPHP拡張モジュールの生成と読み込みが確認できたので、Valgrindでデバッギングしてみましょう。



2.Valgrindによるデバッグ


Valgrindにはいくつかのツールが付属したツール群になっていますが、まず最も使用するであろうMemcheckによるメモリーリーク、初期化・開放などのメモリ周りのデバッグログを出力してみましょう。



#valgrind php zynga_func.php
==2107==
==2107== HEAP SUMMARY:
==2107==     in use at exit: 104,669 bytes in 9 blocks
==2107==   total heap usage: 10,973 allocs, 10,964 frees, 2,127,924 bytes  allocated
==2107==
==2107== LEAK SUMMARY:
==2107==    definitely lost: 0 bytes in 0 blocks
==2107==    indirectly lost: 0 bytes in 0 blocks
==2107==      possibly lost: 0 bytes in 0 blocks
==2107==    still reachable: 104,669 bytes in 9 blocks
==2107==         suppressed: 0 bytes in 0 blocks
==2107== Rerun with --leak-check=full to see details of leaked memory
==2107== 
==2107== For counts of detected and suppressed errors, rerun with: -v
==2107== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)


※ログの見方




ログから、メモリーリークは一切起きておらず、ヒープ領域も僅かに開放されていない箇所もあるが、正常に動作していると言えます。



次にわざとメモリーリークを起こすコードを関数内に埋め込んでみます。zynga_func.c内の、PHP_FUNCTION(confirm_zynga_func_compiled)関数が、先ほどzynga_func.phpで実行された関数になるので、その中の処理を書き換えます。



mallocで確保しているにも関わらずfreeで開放していないため、メモリーリークが起きるコード。


#emacs zynga_func.c
void f(void)
{
    int* x = malloc(10 * sizeof(int));
    x[10] = 0;        // problem 1: heap block overrun
}                    // problem 2: memory leak -- x not freed
PHP_FUNCTION(confirm_zynga_func_compiled)
{
    f();
    ....

ビルドして反映。


#make
#make install


valgrindで確認。


#valgrind php zynga_func.php
==4934== Invalid write of size 4
==4934==    at 0x101231C14: f (in /usr/local/lib/php/extensions/debug-non-zts-20060613/zynga_func.so)
==4934==    by 0x101231C3B: zif_confirm_zynga_func_compiled (in /usr/local/lib/php/extensions/debug-non-zts-20060613/zynga_func.so)
==4934==    by 0x100308AF3: zend_do_fcall_common_helper_SPEC (in /usr/local/bin/php)
==4934==    by 0x100309A06: ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER (in /usr/local/bin/php)
==4934==    by 0x1003084FB: execute (in /usr/local/bin/php)
==4934==    by 0x1002DD728: zend_execute_scripts (in /usr/local/bin/php)
==4934==    by 0x100279F64: php_execute_script (in /usr/local/bin/php)
==4934==    by 0x10036A507: main (in /usr/local/bin/php)
==4934==  Address 0x1010b1298 is 0 bytes after a block of size 40 alloc'd
==4934==    at 0x10062D79F: malloc (vg_replace_malloc.c:236)
==4934==    by 0x101231C07: f (in /usr/local/lib/php/extensions/debug-non-zts-20060613/zynga_func.so)
==4934==    by 0x101231C3B: zif_confirm_zynga_func_compiled (in /usr/local/lib/php/extensions/debug-non-zts-20060613/zynga_func.so)
==4934==    by 0x100308AF3: zend_do_fcall_common_helper_SPEC (in /usr/local/bin/php)
==4934==    by 0x100309A06: ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER (in /usr/local/bin/php)
==4934==    by 0x1003084FB: execute (in /usr/local/bin/php)
==4934==    by 0x1002DD728: zend_execute_scripts (in /usr/local/bin/php)
==4934==    by 0x100279F64: php_execute_script (in /usr/local/bin/php)
==4934==    by 0x10036A507: main (in /usr/local/bin/php)
==4934==    Congratulations! You have successfully modified ext/zynga_func/config.m4. Module zynga_func is now compiled into PHP.
==4934== 
==4934== HEAP SUMMARY:
==4934==     in use at exit: 104,709 bytes in 10 blocks
==4934==   total heap usage: 10,974 allocs, 10,964 frees, 2,127,964 bytes allocated
==4934== 
==4934== LEAK SUMMARY:
==4934==    definitely lost: 40 bytes in 1 blocks
==4934==    indirectly lost: 0 bytes in 0 blocks
==4934==      possibly lost: 0 bytes in 0 blocks
==4934==    still reachable: 104,669 bytes in 9 blocks
==4934==         suppressed: 0 bytes in 0 blocks
==4934== Rerun with --leak-check=full to see details of leaked memory


今度はリークしている箇所と、byte数・block数がログに出力されるのを確認できました。


Valgrindはメモリーリークの検出以外に、初期化されていないメモリ領域の使用、freeされたメモリ領域の使用、ヒーププロファイラ、キャッシュプロファイラなど多用の機能を備えています。


普段使い慣れたツールで、開発に負荷のかからないデバッグを行っていきましょう。