使用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

常用命令

  1. p: print,打印c变量的值
  2. c: coninue,继续运行被中止的程序
  3. b: breakpoint,设置断点,可以按照函数名设置,如 b zip_php_function,也可按照源代码的函数指定断点,如 b src/network/Server.c:1000
  4. t: thread,切换线程,如果进程拥有多个线程,可以使用t指令,切换到不同的线程
  5. n: next,执行下一行,单步调试
  6. info thread: 查看运行的所有线程
  7. l: list,查看源码,可以使用l 函数名或者 l 行号
  8. bt: backtrace,查看运行时的函数调用栈
  9. finish: 完成当前函数
  10. f: frame,与bt配合使用,可以切换到函数调用栈的某一层
  11. 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

参考

重现PHP Core调用栈

gdb使用

调试PHP源码

编译和调试php

GDB工具的使用

posted @ 2022-05-14 13:07  biby  阅读(560)  评论(0编辑  收藏  举报