使用GDB调试PHP Core
使用GDB调试PHP Core
GDB
GDB工具的使用方法如下
简介
GDB是UNIX下程序调试工具,可以用来调试C/C++开发的程序
gdb调试是命令行交互式
使用方法
gdb使用的3种方式
- gdb -p 进程ID
跟踪正在运行的PHP程序,使用gdb -p 进程ID - gdb php
使用gdb运行并调试PHP程序,使用 gdb php -> run server.php 进行调试 - gdb php core
PHP程序发生coredump后,使用gdb加载core内存镜像进行调试, gdb php core
常用命令
- p: print,打印c变量的值
- c: coninue,继续运行被中止的程序
- b: breakpoint,设置断点,可以按照函数名设置,如 b zip_php_function,也可按照源代码的函数指定断点,如 b src/network/Server.c:1000
- t: thread,切换线程,如果进程拥有多个线程,可以使用t指令,切换到不同的线程
- n: next,执行下一行,单步调试
- info thread: 查看运行的所有线程
- l: list,查看源码,可以使用l 函数名或者 l 行号
- bt: backtrace,查看运行时的函数调用栈
- finish: 完成当前函数
- f: frame,与bt配合使用,可以切换到函数调用栈的某一层
- r: run,运行程序
php自定义的gdb指令
- zbacktrace
zbacktrace是php源码包提供的gdb自定义指令,功能与backtrace指令类似,与bt不同的是zbacktrace看到的调用栈是php函数调用栈,而不是c函数
在gdb shell中使用
source .gdbinit
zbractrace
跟踪调试正在运行的PHP程序
- 运行php脚本
准备php脚本
<?php
class Test {
}
function a($i) {
b(new Test, 2.3432, "reader");
}
function b($i) {
c(array(1,2,3));
}
function c($i) {
d(TRUE);
}
function d($i) {
$fp = fopen("/tmp/1.php", "r");
e($fp);
}
function e($i) {
sleep(1000);
}
a(2);
运行脚本
root@37abad564db2:/var/www/php/php# /var/www/php/php-7.4.20/sapi/cli/php 1.php
- 查看进程ID
ps aux | grep php
- 使用gdb跟踪该进程ID
gdb --pid 46121
- 加载php自定义gdb指令,执行zbacktrace指令
(gdb) source /var/www/php/php-7.4.20/.gdbinit
zbacktrace
- 查看array具体元素
print ((zval *)0x7fd3046121f0)
printzv $1
- 查看object
print ((zval *)0x7fd304612160)
printzv $3
运行并调试php程序
运行调试流程
gdb --args /var/www/php/php-7.4.20/sapi/cli/php -r "echo 'hello world';"
把php命令放在--args 选项后面,表示把 -r "echo 'hello world';" 这个字符串作为要调试的php可执行程序的参数,否则gdb会把它们当做gdb的参数
运行要调试的程序,执行gdb的run命令,由于此时没有设置断点,程序会运行完成并退出
run
设置断点
break main
run
程序在mian函数第一行代码中止
使用next命令单步执行
n
探索zend执行引擎
准备脚本
equal.php
<?php
\$i = 1;
\$j = 1.0;
echo \$i == \$j;
使用gdb调试
gdb --args /var/www/php/php-7.4.20/sapi/cli/php equal.php
设置断点
b ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER
运行程序
run
47265 是这个函数体中的第一行代码
使用list命令显示这个函数名开始的20行代码
list 47265,47285
打印变量值
- test.php
<?php
$a = 'hello';
var_dump($a);
- gdb调试
gdb --args /var/www/php/php-7.4.19/sapi/cli/php 22.php
(gdb) b zif_var_dump
(gdb) run
Starting program: /var/www/php/php-7.4.19/sapi/cli/php 22.php
Breakpoint 1, zif_var_dump (execute_data=0x801ae7a49db87483, return_value=0x175fffffc83)
at /var/www/php/php-7.4.19/ext/standard/var.c:218
(gdb) bt
#0 zif_var_dump (execute_data=0x801ae7a49db87483, return_value=0x175fffffc83) at /var/www/php/php-7.4.19/ext/standard/var.c:218
#1 0x00005555559a68ef in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER () at /var/www/php/php-7.4.19/Zend/zend_vm_execute.h:1269
#2 0x0000555555a10915 in execute_ex (ex=0x7ffff7a13020) at /var/www/php/php-7.4.19/Zend/zend_vm_execute.h:53873
#3 0x0000555555a158ff in zend_execute (op_array=0x7ffff7a7f300, return_value=0x0)
at /var/www/php/php-7.4.19/Zend/zend_vm_execute.h:57993
#4 0x0000555555934149 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /var/www/php/php-7.4.19/Zend/zend.c:1679
#5 0x0000555555898fdf in php_execute_script (primary_file=0x7fffffffd350) at /var/www/php/php-7.4.19/main/main.c:2621
#6 0x0000555555a1845d in do_cli (argc=2, argv=0x555556220b60) at /var/www/php/php-7.4.19/sapi/cli/php_cli.c:964
#7 0x0000555555a19574 in main (argc=2, argv=0x555556220b60) at /var/www/php/php-7.4.19/sapi/cli/php_cli.c:1359
(gdb) l
213 /* }}} */
214
215 /* {{{ proto void var_dump(mixed var)
216 Dumps a string representation of variable to output */
217 PHP_FUNCTION(var_dump)
218 {
219 zval *args;
220 int argc;
221 int i;
222
(gdb) u 227
zif_var_dump (execute_data=0x7ffff7a130a0, return_value=0x7fffffffabb0) at /var/www/php/php-7.4.19/ext/standard/var.c:227
227 for (i = 0; i < argc; i++) {
(gdb) p args[0].value.str.val@5
$3 = {"h", "e", "l", "l", "o"}
(gdb) p args[0].value.str.val
$4 = "h"
异常处理
- 使用Docker调试gdb
- 异常
Error disabling address space randomization: Operation not permitte
- 解决方法
docker run --privileged
- optimized out
- 异常
变量值被编译器优化
- 异常
- 解决方法,添加编译参数,启用调试模式
--enable-debug