徹底検証!PHP最適化Tips

第2回 文字列置換関数の比較とgdbの使い方

この記事を読むのに必要な時間:およそ 5 分

gdbを使ってPHPのコードを読む

検証に役立つ方法として,gdbを使ってPHPのコードを読む方法をご紹介します。例として,gdbを使ってstrtrの動きを追ってみたいと思います。

ブレークポイントを設定してみましょう。あたりまえですがstrtrでは定義されてません。

(gdb) b strtr
Function "strtr" not defined.

strtrは PHP_FUNCTION(strtr) で定義されています。このPHP_FUNCTIONはマクロ定義されており,ヘッダファイルから定義されてるところを探すと,以下のコードがでてきます。

main/php.h

332 /* PHP-named Zend macro wrappers */
333 #define PHP_FN                  ZEND_FN
334 #define PHP_MN                  ZEND_MN
335 #define PHP_NAMED_FUNCTION      ZEND_NAMED_FUNCTION
336 #define PHP_FUNCTION            ZEND_FUNCTION
337 #define PHP_METHOD              ZEND_METHOD

Zend/zend_API.h

43 #define ZEND_FN(name) zif_##name
44 #define ZEND_MN(name) zim_##name
45 #define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
46 #define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
47 #define ZEND_METHOD(classname, name)    ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))

最終的に⁠zif_##name⁠となっているので,zif_strtrとして内部で扱っていることがわかりました。zif_strtrでbreakpointを設定すればPHPのstrtr()にbreakpointを設定することができます。

(gdb) b zif_strtr
Breakpoint 1 at 0x826c000: file /home/kajidai/work/php5-5.2.6/ext/standard/string.c, line 2807.

ソースファイルと行数が分かっていれば下記のように指定することも可能です。

b stfing.c:2807

また,クラスメソッドとして定義されてるPHP_METHOD()については,zim_(classname)_(name)で設定できます。例えば,PDOクラスのprepareメソッドはzim_PDO_prepareとして扱われます。

499 static PHP_METHOD(PDO, prepare)

次に実際の処理を行っているphp_strtr()にもbreakpointを設定します。

(gdb) b php_strtr
Breakpoint 2 at 0x81fd81c: file /home/kajidai/work/php-5.2.6/ext/standard/string.c, line 2671.

先ほど使ったstrtrのベンチマーク用のプログラムを走らせてみます。

(gdb) run ~/benchmark_strtr.php
Starting program: /usr/local/bin/php ~/benchmark_strtr.php
[Thread debugging using libthread_db enabled]
[New Thread 0xb7c519e0 (LWP 25628)]
[Switching to Thread 0xb7c519e0 (LWP 25628)]

Breakpoint 1, zif_strtr (ht=3, return_value=0x8489904, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1)
   at /home/kajidai/work/php-5.2.6/ext/standard/string.c:2805
2805    {

期待通りにzif_strtrで処理が中断されました。

再開させます。

(gdb) c
Continuing.

Breakpoint 2, php_strtr (str=0x848991c "abcdefghijklmn", len=14,  str_from=0x848a6ac "abc", str_to=0x848a6bc "ABC", trlen=3)
   at /home/kajidai/work/php-5.2.6/ext/standard/string.c:2671
2671    {

php_strtrで処理が中断されました。与えられる引数の中身を確認できます。

php_strtrの処理終了まで進めます。

(gdb) finish
Run till exit from #0  php_strtr (str=0x848991c "abcdefghijklmn", len=14,  str_from=0x848a6ac "abc", str_to=0x848a6bc "ABC", trlen=3)
   at /home/kajidai/work/php-5.2.6/ext/standard/string.c:2671
zif_strtr (ht=3, return_value=0x8489904, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1)
   at /home/kajidai/work/php-5.2.6/ext/standard/string.c:2840
2840    }
Value returned is $1 = 0x848991c "ABCdefghijklmn"

return_valueの中身をprintします。

(gdb) p *return_value
$2 = {value = {lval = 138975516, dval = 2.9776604101582473e-313, str = {val = 0x848991c "ABCdefghijklmn", len = 14}, 
   ht = 0x848991c, obj = {handle = 138975516, handlers = 0xe}}, refcount = 1,  type = 6 '¥006', is_ref = 0 '¥0'}

さらに見やすく出力するためにzval出力用のprintzvを使ってみます。 printzvはphpのソースを展開すると出てくる.gdbinitに定義されています。 .gdbinitをホームへコピーすることで使用できるようになります。

(gdb) printzv return_value
[0x08489904] (refcount=1) string(14): "ABCdefghijklmn"

無事に置換後の文字列が確認できました。

まとめ

今回はstrtr(), str_replace(), preg_replace()といった文字列置換関数の検証とgdbの解説を行いました。

普段何気なく使っている関数も,gdbなどを使って,関数の中で何が起きているのかを調べてみると新しい発見があり楽しいと思います。

著者プロフィール

梶原大輔(かじわらだいすけ)

大学卒業後,ヤフー株式会社に入社し,Yahoo!ビデオキャストなどのサービス開発に従事。

現在はグリー株式会社でバックエンドシステムの開発を担当。

URLhttp://d.hatena.ne.jp/kajidai/