gdb 调试PHP
root@debian:~/php# php a.php 段错误 root@debian:~/php# ulimit -c unlimited root@debian:~/php# php a.php 段错误 (core dumped) root@debian:~/php# ls a.php core root@debian:~/php# apt-get install gdb root@debian:~/php# gdb php -c core Core was generated by `php a.php'. Program terminated with signal 11, Segmentation fault. #0 0xb6a7bfb8 in zif_smtpmail_connect (ht=5, return_value=0xb6f149dc, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1) at /root/php/php-5.4.7/ext/smtpmail/smtpmail.c:281 281 if(strcmp(substring(lastmessage, 1, 3), hen) !=0 || strlen(lastmessage)==0) { (gdb) (gdb) source ./php-5.4.7/.gdbinit (gdb) zbacktrace [0xb6efb030] smtpmail_connect("smtp.qq.com", "xxxx", "xxx", "xxxx@qq.com", 25) /root/php/a.php:5
PHP的代码包中提供了一个 .gdbinit 的gdb脚本文件,里面提供了20多个 gdb 的自定义命令,用于方便PHP的调试,下面举几个例子:
测试脚本a.php:
gdb 调试命令:
———————–
gdb php
set args a.php
break sleep
r
…
———————–
1. print_cvs 打印当前执行环境中已编译的PHP变量, 如:
2. printzv 打印指定的PHP变量, 需要指定地址, 如:
(gdb) printzv 0x9543f98
[0x9543f98] (refcount=1) string(3): “MMM”
(gdb)
3. 打印PHP的函数调用栈, 如:
(gdb) zbacktrace
[0x95770a4] sleep(1) /usr/home/junjie2/a.php:11
[0x9576fe0] test(“phpor”) /usr/home/junjie2/a.php:7
(gdb)
4. print_ft 打印函数表( HashTable )
(gdb) set $eg = executor_globals
(gdb) print $eg.function_table
$6 = (HashTable *) 0xa5bd450
(gdb) print_ft $eg.function_table
[0xa5bd450] {
“zend_version\0″ => “zend_version”
“func_num_args\0″ => “func_num_args”
“func_get_arg\0″ => “func_get_arg”
“func_get_args\0″ => “func_get_args”
“strlen\0″ => “strlen”
“strcmp\0″ => “strcmp”
“strncmp\0″ => “strncmp”
“strcasecmp\0″ => “strcasecmp”
“strncasecmp\0″ => “strncasecmp”
“each\0″ => “each”
…
学习:
1. 通过阅读 print_cvs 命令的实现,可以知道可以通过prev_execute_data来打印上一执行空间的PHP变量,如:
(gdb) printzv *executor_globals.current_execute_data->prev_execute_data->CVs[1]
[0x9543408] (refcount=1) string(3): “AAA”
(gdb) printzv *executor_globals.current_execute_data->prev_execute_data->CVs[2]
[0x95433ec] (refcount=1) string(3): “BBB”
(gdb)
2. 通过 op_array->vars 来找到对应的变量的名字
(gdb) printf “%s\n” ,executor_globals.current_execute_data->prev_execute_data->op_array->vars[1].name
a
(gdb) printf “%s\n” ,executor_globals.current_execute_data->prev_execute_data->op_array->vars[2].name
b
3. 修改了一下 print_cvs 命令,通过参数来获取每个执行空间的PHP的已编译变量
使用方法:
———————
(gdb) zbacktrace #查看PHP的调用栈
[0x8ce2078] sleep(1) /usr/home/junjie2/a.php:9
[0x8ce1fe0] test(“phpor”) /usr/home/junjie2/a.php:5
(gdb) print_cvs #查看当前执行空间中的PHP变量
Compiled variables count: 3
0 = name
[0x8cae3d0] (refcount=2) string(5): “phpor”
1 = m
[0x8cae93c] (refcount=1) string(3): “MMM”
2 = n
[0x8cae958] (refcount=1) string(3): “NNN”
(gdb) print_cvs 2 # 查看指定执行空间中的PHP变量, 这个参数给大了也没关系,最多打印最外层的PHP变量
Compiled variables count: 2
0 = a
[0x8cae408] (refcount=1) string(3): “AAA”
1 = b
[0x8cae3ec] (refcount=1) string(3): “BBB”
(gdb)
———————
4. 有时候需要借助环境变量,如:
(gdb) print_ft (HashTable *)0xa5bd450
A syntax error in expression, near `’.
(gdb) set $phpor=(HashTable *)0xa5bd450
(gdb) print_ft $phpor
[0xa5bd450] {
“zend_version\0″ => “zend_version”
“func_num_args\0″ => “func_num_args”
“func_get_arg\0″ => “func_get_arg”
“func_get_args\0″ => “func_get_args”
“strlen\0″ => “strlen”
“strcmp\0″ => “strcmp”
“strncmp\0″ => “strncmp”
5. 打印指定的PHP变量