网易首页 > 网易号 > 正文 申请入驻

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

0
分享至

作者:bobyzhang,腾讯 IEG 运营开发工程师

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文件。


const A = 'test const';
const B = 'test const B';
class B {
public $a = 'test';
public function funB() {
var_dump('test funB');

class C extends B {
public function funC() {
var_dump('test funC');
}
}

$a = 'test';
$b = ['a1' => 1, 'a2' => 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] 'a'
[0x7ffff1614080] (refcount=0) string: test
[1] 'b'
[0x7ffff1614090] (refcount=5) array: Hash(2)[0x7ffff170e300]: {
[0] a1 => [0x7ffff1793e20] long: 1
[1] a2 => [0x7ffff1793e40] long: 2
}

[2] 'c'
[0x7ffff16140a0] (refcount=2) object(B) #2
Properties Hash(1)[0x7ffff170e480]: {
[0] a => [0x7ffff1793f60] indirect: [0x7ffff170e388] (refcount=4) string: test

[3] 'd'
[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] 'e'
[0x7ffff16140c0] (refcount=2) object(C) #3
Properties Hash(1)[0x7ffff170e4e0]: {
[0] a => [0x7ffff17940a0] indirect: [0x7ffff170e448] (refcount=4) string: test

[5] 'f'
[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
}

视频号最新视频

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
17国入和平委员会,4国反悔!普京10亿没花出去,特朗普态度变了

17国入和平委员会,4国反悔!普京10亿没花出去,特朗普态度变了

娱乐圈的笔娱君
2026-01-23 00:21:00
官场奇案:汕头大火烧死中纪委调查员,是天灾还是人祸?

官场奇案:汕头大火烧死中纪委调查员,是天灾还是人祸?

午夜故事会
2024-03-28 11:35:01
-9℃!要下雨了!江苏天气最新预测

-9℃!要下雨了!江苏天气最新预测

微淮安
2026-01-23 19:20:33
天助C罗:1-2,本泽马破门难救主,C罗争冠劲敌2连败,落后胜利队10分

天助C罗:1-2,本泽马破门难救主,C罗争冠劲敌2连败,落后胜利队10分

侧身凌空斩
2026-01-23 03:57:15
江苏昆山一工厂春节提前离岗或延迟到岗的均按自动离职处理

江苏昆山一工厂春节提前离岗或延迟到岗的均按自动离职处理

捣蛋窝
2026-01-23 08:44:18
他们为何急于注销携程?

他们为何急于注销携程?

智识漂流
2025-12-25 20:09:36
爆笑经典糗事冷笑话,那天我在机场碰上一个上海女人,正在和一个北京女人抬杠说!

爆笑经典糗事冷笑话,那天我在机场碰上一个上海女人,正在和一个北京女人抬杠说!

天天明星
2026-01-23 14:15:21
楼市的五大新城时代,彻底结束

楼市的五大新城时代,彻底结束

环线房产咨询
2026-01-23 19:04:20
10万亿度需求也不买!中国摊牌,输电专线全叫停,国产电价教做人

10万亿度需求也不买!中国摊牌,输电专线全叫停,国产电价教做人

大鱼简科
2026-01-23 19:59:24
美军8艘战舰携812枚战斧导弹压境,伊朗:支持美以者必遭打击

美军8艘战舰携812枚战斧导弹压境,伊朗:支持美以者必遭打击

老马拉车莫少装
2026-01-24 00:08:32
斯诺克最新战报!中国德比一边倒:雷佩凡夺赛点,张安达2局0分!

斯诺克最新战报!中国德比一边倒:雷佩凡夺赛点,张安达2局0分!

刘姚尧的文字城堡
2026-01-23 19:33:16
好消息!武汉明确:计划再扩招8000人!

好消息!武汉明确:计划再扩招8000人!

蔡甸在线
2026-01-23 12:07:07
高调炫富只是冰山一角啊!

高调炫富只是冰山一角啊!

BenSir本色说
2026-01-21 22:03:50
人社部部长表态!2026养老金到底怎么涨?企退和事退有无差异?

人社部部长表态!2026养老金到底怎么涨?企退和事退有无差异?

丁丁鲤史纪
2026-01-23 11:10:35
火箭122 - 128负费城,谁是逆转“罪魁”?数据说话!

火箭122 - 128负费城,谁是逆转“罪魁”?数据说话!

魔血獄苼
2026-01-24 00:58:12
流亡一年后,阿萨德已成“透明人”,连约普京吃顿饭都做不到

流亡一年后,阿萨德已成“透明人”,连约普京吃顿饭都做不到

朔方瞭望
2026-01-06 11:11:51
美航母已战前静默!关键时刻,运-20飞抵伊朗紧急交付红旗-9B?

美航母已战前静默!关键时刻,运-20飞抵伊朗紧急交付红旗-9B?

红岸卫士
2026-01-21 22:24:37
2月财气冲天,3生肖迎贵人旺财运,多吉多利

2月财气冲天,3生肖迎贵人旺财运,多吉多利

人閒情事
2026-01-20 12:58:44
我38岁被裁员,公司补偿了我75万,手续办完后,财务总监追了出来

我38岁被裁员,公司补偿了我75万,手续办完后,财务总监追了出来

五元讲堂
2025-12-23 11:21:19
广东这家餐饮店5500高薪诚聘35岁以下服务员,时薪不到10块!

广东这家餐饮店5500高薪诚聘35岁以下服务员,时薪不到10块!

捣蛋窝
2026-01-18 00:12:55
2026-01-24 02:56:49
腾讯技术工程
腾讯技术工程
不止于技术
1362文章数 600关注度
往期回顾 全部

科技要闻

TikTok守住了算法"灵魂" 更握紧了"钱袋子"

头条要闻

疑在达沃斯受挫 79岁的特朗普转发超80条帖子发泄怒气

头条要闻

疑在达沃斯受挫 79岁的特朗普转发超80条帖子发泄怒气

体育要闻

杜兰特鏖战44分钟累瘫 轰36+7却致命失误

娱乐要闻

演员孙涛澄清闫学晶言论 落泪维护妻子

财经要闻

2026年,消费没有新故事?

汽车要闻

主打家庭大六座 奕境首款SUV将北京车展亮相

态度原创

时尚
房产
亲子
艺术
旅游

今日热点:车银优代言广告被隐藏;《巅峰对决》主演担任米兰冬奥会火炬手……

房产要闻

正式官宣!三亚又一所名校要来了!

亲子要闻

“一个桃就拐走了!”宝妈在水果店频频拒绝女儿,评论区太真实!

艺术要闻

人像摄影背后的真相,模特并不是全部!

旅游要闻

北京世园“天宫灯会”正式开幕,持续至3月8日

无障碍浏览 进入关怀版