搜索
首页后端开发php教程深入了解PHP:用gdb调试源码

本篇文章给大家带来了关于PHP的相关知识,其中主要介绍了关于使用gdb调试源码的相关内容,下面一起来看一下,希望对大家有帮助。

深入了解PHP:用gdb调试源码

推荐学习:《PHP视频教程

php编译时有一个debug模式,这个模式会关闭内存优化,提示内存泄露,屏蔽调用栈优化可以让我们看到完整的php c层面的调用栈。

通常我会编译两个php版(一个正常,一个打开debug)在不同的目录,通过export决定使用哪个。

通过php-config命令可以看到configure-options,修改其中的prefix 和 with-config-file-path 到新的目录,然后添加--enable-debug 命令

yongkbmaster ➜  ~ php-config
Usage: /data/env/runtime/php-7.1.33-debug/bin/php-config [OPTION]
Options:
  --prefix            [/data/env/runtime/php-7.1.33-debug]
  --includes          [-I/data/env/runtime/php-7.1.33-debug/include/php -I/data/env/runtime/php-7.1.33-debug/include/php/main -I/data/env/runtime/php-7.1.33-debug/include/php/TSRM -I/data/env/runtime/php-7.1.33-debug/include/php/Zend -I/data/env/runtime/php-7.1.33-debug/include/php/ext -I/data/env/runtime/php-7.1.33-debug/include/php/ext/date/lib]
  --ldflags           []
  --libs              [-lcrypt   -lz -lexslt -lresolv -lcrypt -lrt -lldap -llber -lpng -lz -ljpeg -lcurl -lbz2 -lz -lrt -lm -ldl -lnsl  -lxml2 -lz -lm -ldl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lssl -lcrypto -lcurl -lxml2 -lz -lm -ldl -lfreetype -lxml2 -lz -lm -ldl -lxml2 -lz -lm -ldl -lcrypt -lxml2 -lz -lm -ldl -lxml2 -lz -lm -ldl -lxml2 -lz -lm -ldl -lxml2 -lz -lm -ldl -lxslt -lxml2 -lz -ldl -lm -lssl -lcrypto -lcrypt ]
  --extension-dir     [/data/env/runtime/php-7.1.33-debug/lib/php/extensions/debug-non-zts-20160303]
  --include-dir       [/data/env/runtime/php-7.1.33-debug/include/php]
  --man-dir           [/data/env/runtime/php-7.1.33-debug/php/man]
  --php-binary        [/data/env/runtime/php-7.1.33-debug/bin/php]
  --php-sapis         [ cli fpm phpdbg cgi]
  --configure-options [--prefix=/data/env/runtime/php-7.1.33-debug --enable-debug --enable-phpdbg-debug --with-config-file-path=/data/env/runtime/php-7.1.33-debug/etc --with-curl --with-freetype-dir --with-gd --with-gettext --with-iconv-dir --with-kerberos --with-libdir=lib64 --with-libxml-dir --with-mysqli --with-openssl --with-pcre-regex --with-pdo-mysql --with-pdo-sqlite --with-pear --with-png-dir --with-jpeg-dir --with-xmlrpc --with-xsl --with-zlib --with-bz2 --with-mhash --enable-fpm --enable-bcmath --enable-libxml --enable-inline-optimization --enable-gd-native-ttf --enable-mbregex --enable-mbstring --enable-opcache --enable-pcntl --enable-shmop --enable-soap --enable-sockets --enable-sysvsem --enable-sysvshm --enable-xml --enable-zip --with-ldap]
  --version           [7.1.33]
  --vernum            [70133]

修改之后大概是这样子,然后编译安装 就可以得到debug版本了

--prefix=/data/env/runtime/php-7.1.33-debug --enable-debug --enable-phpdbg-debug --with-config-file-path=/data/env/runtime/php-7.1.33-debug/etc --with-curl --with-freetype-dir --with-gd --with-gettext --with-iconv-dir --with-kerberos --with-libdir=lib64 --with-libxml-dir --with-mysqli --with-openssl --with-pcre-regex --with-pdo-mysql --with-pdo-sqlite --with-pear --with-png-dir --with-jpeg-dir --with-xmlrpc --with-xsl --with-zlib --with-bz2 --with-mhash --enable-fpm --enable-bcmath --enable-libxml --enable-inline-optimization --enable-gd-native-ttf --enable-mbregex --enable-mbstring --enable-opcache --enable-pcntl --enable-shmop --enable-soap --enable-sockets --enable-sysvsem --enable-sysvshm --enable-xml --enable-zip --with-ldap

php --version中看到DEBUG 就可以了

yongkbmaster ➜  ~ /data/env/runtime/php-7.1.33-debug/bin/php --version
PHP 7.1.33 (cli) (built: Dec 29 2020 19:16:50) ( NTS DEBUG )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologie

注意:Debug版本的扩展需要再次编译安装,不能拷贝正常版本的so,安装方式和普通扩展一致,一般不需要开额外的debug参数。如果你需要调试扩展比如swoole你需要设置扩展的debug参数,可以参考扩展的./configure文件说明。

gdb使用

这里简单介绍一下gdb的基本使用,更详细的使用方法可以自行google。

启动gdb

捕获进程

gdb -p {pid}

run 方法启动

gdb php
run test3.php

通过core 文件

gdb -c core.8451

断点

break n :在第n行处设置断点(可以带上代码路径和代码名称)

//注意:这里只能断点c代码,php文件不行的,var.c:201在php-7.1.33是var_dump的入口
break var.c:201

b fn1 if a>b:条件断点设置

break func(break缩写为b):在函数func()的入口处设置断点

//大部分php的方法在c层面的方法名都是zif_ + php方法名。 例如 var_dump 在c的方法名叫zif_var_dump
break zif_var_dump

delete 断点号n:删除第n个断点

disable 断点号n:暂停第n个断点

enable 断点号n:开启第n个断点

clear 行号n:清除第n行的断点

info b (info breakpoints) :显示当前程序的断点设置情况

delete breakpoints:清除所有断点

其他

  • list (简写 l) ,其作用就是列出程序的源代码,默认每次显示10行。

  • list 行号:将显示当前文件以“行号”为中心的前后10行代码,

  • print a:将显示 a 的值

  • continue (简写c ):继续执行,到下一个断点处(或运行结束)。设置断点后需要按这个。

  • next (简写n):当前函数,下一行

  • step (简写s):跳入函数内部

  • where/bt :当前运行的堆栈列表;

php gdb 小工具

这里就是本文的重点了php为gdb提供了一组小工具,在源代码目录下的.gdbinit文件中,它可以帮助我们更好的gdb php源代码。

准备

为了更好的演示,我这里准备一个php文件。

<?php
const A = &#39;test const&#39;;
const B = &#39;test const B&#39;;
class B {
        public $a = &#39;test&#39;;
        public function funB() {
                var_dump(&#39;test funB&#39;);
        }
}
class C extends B {
        public function funC() {
                var_dump(&#39;test funC&#39;);
        }
}
$a = &#39;test&#39;;
$b = [&#39;a1&#39; => 1, &#39;a2&#39; => 2];
$c = new B();
$d = [A, B];
$e = new C();
$f = $b;
var_dump($a, $b, $c, $d, $e, $f);
get_object_vars($e);

启动gdb,设置2个断点。

gdb php //注意这里要用debug版本的
(gdb) break var.c:211
Breakpoint 1 at 0x76e717: file /data/env/runtime/php-7.1.33-src/ext/standard/var.c, line 211.
(gdb) break zend_object_handlers.c:492
Breakpoint 2 at 0x86ce9d: file /data/env/runtime/php-7.1.33-src/Zend/zend_object_handlers.c, line 492.
(gdb) r test4.php

然后载入小工具

source /data/env/runtime/php-7.1.33-src/.gdbinit

使用

zbacktrace 显示当前的php调用栈

(gdb) zbacktrace
[0x7ffff1614200] var_dump("test", array(2)[0x7ffff1614260], object[0x7ffff1614270], array(2)[0x7ffff1614280], object[0x7ffff1614290], array(2)[0x7ffff16142a0]) [internal function]
[0x7ffff1614030] (main) /root/test4.php:26

dump_bt 查看当前调用栈 和 zbacktrace 类似

(gdb) dump_bt executor_globals.current_execute_data
[0x7ffff1614200] var_dump("test", array(2)[0x7ffff1614260], object[0x7ffff1614270], array(2)[0x7ffff1614280], object[0x7ffff1614290], array(2)[0x7ffff16142a0]) [internal function]
[0x7ffff1614030] (main) /root/test4.php:26

printzv 输出zend value 的情况

(gdb) printzv &args[0]
[0x7ffff1614250] (refcount=0) string: test

print_global_vars 输出全局变量

(gdb) print_global_vars
Hash(13)[0x11bf0d0]: {
  [0] _GET => [0x7ffff1657100] (refcount=2) array:
  [1] _POST => [0x7ffff1657120] (refcount=2) array:
  [2] _COOKIE => [0x7ffff1657140] (refcount=2) array:
  [3] _FILES => [0x7ffff1657160] (refcount=2) array:
  [4] argv => [0x7ffff1657180] (refcount=2) array:
  [5] argc => [0x7ffff16571a0] long: 1
  [6] _SERVER => [0x7ffff16571c0] (refcount=2) array:
  [7] a => [0x7ffff16571e0] indirect: [0x7ffff1613080] (refcount=0) string: test
  [8] b => [0x7ffff1657200] indirect: [0x7ffff1613090] (refcount=5) array:
  [9] c => [0x7ffff1657220] indirect: [0x7ffff16130a0] (refcount=2) object(B) #2
  [10] d => [0x7ffff1657240] indirect: [0x7ffff16130b0] (refcount=2) array:
  [11] e => [0x7ffff1657260] indirect: [0x7ffff16130c0] (refcount=2) object(C) #3
  [12] f => [0x7ffff1657280] indirect: [0x7ffff16130d0] (refcount=5) array:

print_const_table 输出定义的常量

(gdb) print_const_table executor_globals.zend_constants
[0x14e8380] {
  Hash(2340)[0x14e8380]: {
    [0] E_ERROR => [0x14fd660] long: 1
    [1] E_RECOVERABLE_ERROR => [0x14fe8a0] long: 4096
    [2] E_WARNING => [0x14fe900] long: 2
    [3] E_PARSE => [0x14fe960] long: 4
    [4] E_NOTICE => [0x14fe9c0] long: 8
    [5] E_STRICT => [0x14fea20] long: 2048
    [6] E_DEPRECATED => [0x14fea80] long: 8192
    [7] E_CORE_ERROR => [0x14feae0] long: 16
    [8] E_CORE_WARNING => [0x14feb40] long: 32
    [9] E_COMPILE_ERROR => [0x14feba0] long: 64
    [10] E_COMPILE_WARNING => [0x14fec10] long: 128
    [11] E_USER_ERROR => [0x14fec70] long: 256
    [12] E_USER_WARNING => [0x14fecd0] long: 512
    [13] E_USER_NOTICE => [0x14fed30] long: 1024
    [14] E_USER_DEPRECATED => [0x14feda0] long: 16384
    [15] E_ALL => [0x14fee00] long: 32767
    [16] DEBUG_BACKTRACE_PROVIDE_OBJECT => [0x14fee70] long: 1
    [17] DEBUG_BACKTRACE_IGNORE_ARGS => [0x14feee0] long: 2
    [18] true => [0x14fef70] bool: true
    [19] false => [0x14ff000] bool: false
    [20] ZEND_THREAD_SAFE => [0x14ff070] bool: false
    [21] ZEND_DEBUG_BUILD => [0x14ff0e0] bool: true
    [22] null => [0x14ff170] NULL
    [23] PHP_VERSION => [0x1500380] (refcount=1) string: 7.1.33
 ......

print_zstr 输出zend string

(gdb) print_zstr args[0]
string(4) "test"
(gdb) print_zstr args[0] 2
string(4) "te..."
(gdb) print_zstr args[0] 4
string(4) "test"

print_cvs 打印已编译的变量及其值 它需要传入一个 zend_execute_data 类型的值。可以先bt看一下调用栈。

(gdb) bt //这里看到 #2 层这里是 zend_vm_execute 的执行入口,这里有zend_execute_data 类型的值。
#0  zif_var_dump (execute_data=0x7ffff1614120, return_value=0x7fffffffa9b0) at /data/env/runtime/php-7.1.33-src/ext/standard/var.c:209
#1  0x0000000000ab08d4 in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER () at /data/env/runtime/php-7.1.33-src/Zend/zend_vm_execute.h:628
#2  0x0000000000ab01c3 in execute_ex (ex=0x7ffff1614030) at /data/env/runtime/php-7.1.33-src/Zend/zend_vm_execute.h:429
#3  0x0000000000ab02d5 in zend_execute (op_array=0x7ffff1672d00, return_value=0x0) at /data/env/runtime/php-7.1.33-src/Zend/zend_vm_execute.h:474
#4  0x0000000000a510f9 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /data/env/runtime/php-7.1.33-src/Zend/zend.c:1482
#5  0x00000000009c02f4 in php_execute_script (primary_file=0x7fffffffdf30) at /data/env/runtime/php-7.1.33-src/main/main.c:2577
#6  0x0000000000b31387 in do_cli (argc=2, argv=0x14e7f30) at /data/env/runtime/php-7.1.33-src/sapi/cli/php_cli.c:993
#7  0x0000000000b32346 in main (argc=2, argv=0x14e7f30) at /data/env/runtime/php-7.1.33-src/sapi/cli/php_cli.c:1381
(gdb) f 2 //跳到#2 这一层
#2  0x0000000000ab01c3 in execute_ex (ex=0x7ffff1614030) at /data/env/runtime/php-7.1.33-src/Zend/zend_vm_execute.h:429
429                     ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
(gdb) print_cvs ex //输出
Compiled variables count: 6
[0] &#39;a&#39;
[0x7ffff1614080] (refcount=0) string: test
[1] &#39;b&#39;
[0x7ffff1614090] (refcount=5) array:     Hash(2)[0x7ffff170e300]: {
      [0] a1 => [0x7ffff1793e20] long: 1
      [1] a2 => [0x7ffff1793e40] long: 2
}
[2] &#39;c&#39;
[0x7ffff16140a0] (refcount=2) object(B) #2
Properties     Hash(1)[0x7ffff170e480]: {
      [0] a => [0x7ffff1793f60] indirect: [0x7ffff170e388] (refcount=4) string: test
}
[3] &#39;d&#39;
[0x7ffff16140b0] (refcount=2) array:     Packed(2)[0x7ffff170e3c0]: {
      [0] 0 => [0x7ffff1793688] (refcount=1) string: test const
      [1] 1 => [0x7ffff17936a8] (refcount=1) string: test const B
}
[4] &#39;e&#39;
[0x7ffff16140c0] (refcount=2) object(C) #3
Properties     Hash(1)[0x7ffff170e4e0]: {
      [0] a => [0x7ffff17940a0] indirect: [0x7ffff170e448] (refcount=4) string: test
}
[5] &#39;f&#39;
[0x7ffff16140d0] (refcount=5) array:     Hash(2)[0x7ffff170e300]: {
      [0] a1 => [0x7ffff1793e20] long: 1
      [1] a2 => [0x7ffff1793e40] long: 2
}

print_ht 输出HashTable, HashTable是php底层一个重要的数据结构是php array的实现方式,你可以理解为是C层面的php array,在php源码中也大量使用HashTable存储各类k v结构或数组结构。

(gdb) print_ht args[1].value
Hash(2)[0x7ffff170e300]: {
  [0] a1 => [0x7ffff1793e20] long: 1
  [1] a2 => [0x7ffff1793e40] long: 2
}
(gdb) print_ht args[3].value
Packed(2)[0x7ffff170e3c0]: {
  [0] 0 => [0x7ffff1793688] (refcount=1) string: test const
  [1] 1 => [0x7ffff17936a8] (refcount=1) string: test const B
}

print_htptr 和 print_ht 类似,它输出的是zval的地址不是zval的值

(gdb) print_htptr args[1].value
Hash(2)[0x7ffff170e300]: {
  [0] a1 => 0x7ffff1793e20
  [1] a2 => 0x7ffff1793e40
}

print_htstr 和 print_ht 类似,只是HashTable中存的不是zval 而是c char,但是这种情况在源码中好像很少见了,大部分存字符串的情况会直接用zend string,我找了一圈在  php_cli_server_mime_type_ctor 有一处使用

(gdb) print_htstr &server->extension_mime_types
Hash(2)[0x11b9228]: {
  [0] ez => application/andrew-inset
  [1] aw => application/applixware
}

print_ft 和 print_ht 类似,只是HashTable中存的是  zend_function 的地址

(gdb) print_ft &args[2].value.obj.ce.function_table
Hash(1)[0x7ffff1783210]: {
  [0] funb => "funB"
}

print_inh 输出class相关信息

(gdb) print_inh &args[4].value.obj.ce
class C extends B {
 class B {
 }
}

print_pi 输出对象中属性相关信息,它需要传入一个zend_property_info类型的地址,在zend_object_handlers.c:492中有使用,php中可以用get_object_vars($e) 触发。

(gdb) c
Continuing.
Breakpoint 2, zend_check_property_access (zobj=0x7ffff170e420, prop_info_name=0x7ffff173c2c0) at /data/env/runtime/php-7.1.33-src/Zend/zend_object_handlers.c:492
492             zend_string_release(member);
(gdb) print_pi property_info
[0x7ffff17833c8] {
    offset = 0x28
    ce = [0x7ffff17831d0] B
    flags = 0x100 (ZEND_ACC_PUBLIC)
    name  = string(1) "a"
    default value: [0x7ffff17690c0] (refcount=4) string: test
}

推荐学习:《PHP视频教程

以上是深入了解PHP:用gdb调试源码的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:腾讯技术工程微信公众号。如有侵权,请联系admin@php.cn删除
您什么时候使用特质与PHP中的抽象类或接口?您什么时候使用特质与PHP中的抽象类或接口?Apr 10, 2025 am 09:39 AM

在PHP中,trait适用于需要方法复用但不适合使用继承的情况。1)trait允许在类中复用方法,避免多重继承复杂性。2)使用trait时需注意方法冲突,可通过insteadof和as关键字解决。3)应避免过度使用trait,保持其单一职责,以优化性能和提高代码可维护性。

什么是依赖性注入容器(DIC),为什么在PHP中使用一个?什么是依赖性注入容器(DIC),为什么在PHP中使用一个?Apr 10, 2025 am 09:38 AM

依赖注入容器(DIC)是一种管理和提供对象依赖关系的工具,用于PHP项目中。DIC的主要好处包括:1.解耦,使组件独立,代码易维护和测试;2.灵活性,易替换或修改依赖关系;3.可测试性,方便注入mock对象进行单元测试。

与常规PHP阵列相比,解释SPL SplfixedArray及其性能特征。与常规PHP阵列相比,解释SPL SplfixedArray及其性能特征。Apr 10, 2025 am 09:37 AM

SplFixedArray在PHP中是一种固定大小的数组,适用于需要高性能和低内存使用量的场景。1)它在创建时需指定大小,避免动态调整带来的开销。2)基于C语言数组,直接操作内存,访问速度快。3)适合大规模数据处理和内存敏感环境,但需谨慎使用,因其大小固定。

PHP如何安全地上载文件?PHP如何安全地上载文件?Apr 10, 2025 am 09:37 AM

PHP通过$\_FILES变量处理文件上传,确保安全性的方法包括:1.检查上传错误,2.验证文件类型和大小,3.防止文件覆盖,4.移动文件到永久存储位置。

什么是无效的合并操作员(??)和无效分配运算符(?? =)?什么是无效的合并操作员(??)和无效分配运算符(?? =)?Apr 10, 2025 am 09:33 AM

JavaScript中处理空值可以使用NullCoalescingOperator(??)和NullCoalescingAssignmentOperator(??=)。1.??返回第一个非null或非undefined的操作数。2.??=将变量赋值为右操作数的值,但前提是该变量为null或undefined。这些操作符简化了代码逻辑,提高了可读性和性能。

什么是内容安全策略(CSP)标头,为什么重要?什么是内容安全策略(CSP)标头,为什么重要?Apr 09, 2025 am 12:10 AM

CSP重要因为它能防范XSS攻击和限制资源加载,提升网站安全性。1.CSP是HTTP响应头的一部分,通过严格策略限制恶意行为。2.基本用法是只允许从同源加载资源。3.高级用法可设置更细粒度的策略,如允许特定域名加载脚本和样式。4.使用Content-Security-Policy-Report-Only头部可调试和优化CSP策略。

什么是HTTP请求方法(获取,发布,放置,删除等),何时应该使用?什么是HTTP请求方法(获取,发布,放置,删除等),何时应该使用?Apr 09, 2025 am 12:09 AM

HTTP请求方法包括GET、POST、PUT和DELETE,分别用于获取、提交、更新和删除资源。1.GET方法用于获取资源,适用于读取操作。2.POST方法用于提交数据,常用于创建新资源。3.PUT方法用于更新资源,适用于完整更新。4.DELETE方法用于删除资源,适用于删除操作。

什么是HTTP,为什么对Web应用程序至关重要?什么是HTTP,为什么对Web应用程序至关重要?Apr 09, 2025 am 12:08 AM

HTTPS是一种在HTTP基础上增加安全层的协议,主要通过加密数据保护用户隐私和数据安全。其工作原理包括TLS握手、证书验证和加密通信。实现HTTPS时需注意证书管理、性能影响和混合内容问题。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。