PHP的代码包中提供了一个 .gdbinit 的gdb脚本文件,里面提供了20多个 gdb 的自定义命令,用于方便PHP的调试,下面举几个例子:
测试脚本a.php:
$a = "AAA";
$b = "BBB";
test("phpor");
function test($name) {
$m = "MMM";
$n = "NNN";
sleep(1);
echo$name;
}
1
2
3
4
5
6
7
8
9
10
$a="AAA";
$b="BBB";
test("phpor");
functiontest($name){
$m="MMM";
$n="NNN";
sleep(1);
echo$name;
}
gdb 调试命令:
———————–
gdb php
set args a.php
break sleep
r
…
———————–
1. print_cvs 打印当前执行环境中已编译的PHP变量, 如:
(gdb) print_cvs
Compiled variables count: 3
0 = name
[0x9543f7c] (refcount=2) string(5): "phpor"
1 = m
[0x9543f98] (refcount=1) string(3): "MMM"
2 = n
[0x9543fb4] (refcount=1) string(3): "NNN"
(gdb)
1
2
3
4
5
6
7
8
9
(gdb)print_cvs
Compiledvariablescount:3
0=name
[0x9543f7c](refcount=2)string(5):"phpor"
1=m
[0x9543f98](refcount=1)string(3):"MMM"
2=n
[0x9543fb4](refcount=1)string(3):"NNN"
(gdb)
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的已编译变量
define print_cvs
____executor_globals
set $f = $eg.current_execute_data
if $argc == 1
set $j = 1
while $j < $arg0 && $f.prev_execute_data != 0
set $f = $f.prev_execute_data
set $j = $j + 1
end
end
set $p = $f.CVs
set $c = $f.op_array.last_var
set $v = $f.op_array.vars
set $i = 0
printf "Compiled variables count: %d\n", $c
while $i < $c
printf "%d = %s\n", $i, $v[$i].name
if $p[$i] != 0
printzv *$p[$i]
else
printf "*uninitialized*\n"
end
set $i = $i + 1
end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
define print_cvs
____executor_globals
set $f = $eg.current_execute_data
if $argc == 1
set $j = 1
while $j < $arg0 && $f.prev_execute_data != 0
set $f = $f.prev_execute_data
set $j = $j + 1
end
end
set $p = $f.CVs
set $c = $f.op_array.last_var
set $v = $f.op_array.vars
set $i = 0
printf "Compiled variables count: %d\n", $c
while $i < $c
printf "%d = %s\n", $i, $v[$i].name
if $p[$i] != 0
printzv *$p[$i]
else
printf "*uninitialized*\n"
end
set $i = $i + 1
end
end
define print_cvs ____executor_globals set $f = $eg.current_execute_data if $argc == 1 set $j = 1 while $j < $arg0 && $f.prev_execute_data != 0 set $f = $f.prev_execute_data set $j = $j + 1 end end set $p = $f.CVs set $c = $f.op_array.last_var set $v = $f.op_array.vars set $i = 0 printf "Compiled variables count: %d\n", $c while $i < $c printf "%d = %s\n", $i, $v[$i].name if $p[$i] != 0 printzv *$p[$i] else printf "*uninitialized*\n" end set $i = $i + 1 end end
1
defineprint_cvs ____executor_globals set$f=$eg.current_execute_data if$argc==1 set$j=1 while$j<$arg0&&$f.prev_execute_data!=0 set$f=$f.prev_execute_data set$j=$j+1 end end set$p=$f.CVs set$c=$f.op_array.last_var set$v=$f.op_array.vars set$i=0 printf"Compiled variables count: %d\n",$c while$i<$c printf"%d = %s\n",$i,$v[$i].name if$p[$i]!=0 printzv *$p[$i] else printf"*uninitialized*\n" end set$i=$i+1 endend
使用方法:
———————
(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变量
define print_pval
____executor_globals
set $p = $eg.current_execute_data.CVs
set $c = $eg.current_execute_data.op_array.last_var
set $v = $eg.current_execute_data.op_array.vars
set $i = 0
set $name = $arg0
#printf "search php var: %s c:%d\n", $name, $c
while $i < $c
#printf "name: %s\n", $name
set $n = $v[$i].name
# use strcmp but ==
if strcmp($n, $name) == 0
if $p[$i] != 0
printzv *$p[$i]
# use loop_break but break
loop_break
end
end
#printf "i %d, %s\n", $i, $v[$i].name
set $i = $i + 1
end
if $i == $c
printf "no found var named: %s\n", $name
end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
defineprint_pval
____executor_globals
set$p=$eg.current_execute_data.CVs
set$c=$eg.current_execute_data.op_array.last_var
set$v=$eg.current_execute_data.op_array.vars
set$i=0
set$name=$arg0
#printf "search php var: %s c:%d\n", $name, $c
while$i
#printf "name: %s\n", $name
set$n=$v[$i].name
# use strcmp but ==
ifstrcmp($n,$name)==0
if$p[$i]!=0
printzv *$p[$i]
# use loop_break but break
loop_break
end
end
#printf "i %d, %s\n", $i, $v[$i].name
set$i=$i+1
end
if$i==$c
printf"no found var named: %s\n",$name
end
end