PHP 内核分析经验谈:工具篇
来源:互联网 发布:淘宝网限制登录申诉 编辑:程序博客网 时间:2024/06/05 09:40
http://joshuais.me/php-nei-he-fen-xi-jing-yan-tan-gong-ju-pian/?utm_source=tuicool&utm_medium=referral
近,我在分析 PHP 内核的过程中,使用到一些工具,总结了这些工具的使用方法,分享给大家。
VLD
VLD(Vulcan Logic Dumper)是 PHP 的一个扩展。它以钩子的方式嵌入到 Zend 引擎中,收集并打印 PHP 脚本编译时期产生所有的 OPCODE。使用它,我们可以很方便地查看 PHP 源码产生的 OPCODE。
安装 VLD 扩展
通过 Github 直接安装 VLD 扩展。
git clone https://github.com/derickr/vld.gitcd vldphpize./configuremake && make install
使用 VLD 查看 OPCODE
通过一个简单的样例我们来下如何使用 VLD。首先,我们进入 ~/test/php
目录,创建一个简单的 PHP 脚本,保存为 simple.php
。
<?php$a = 1;$b = $a + 1;echo $b;
在命令行里输入以下命令:
php -dvld.active=1 ~/test/php/simple.php
可以看到 VLD 扩展将产生的 OPCODE 打印到了终端上:
Finding entry pointsBranch analysis from position: 0Jump found. Position 1 = -2filename: /Users/joshua/test/php/simple.phpfunction name: (null)number of ops: 5compiled vars: !0 = $a, !1 = $bline #* E I O op fetch ext return operands------------------------------------------------------------------------------------- 3 0 E > ASSIGN !0, 1 4 1 ADD ~3 !0, 1 2 ASSIGN !1, ~3 6 3 ECHO !1 4 > RETURN 1branch: # 0; line: 3- 6; sop: 0; eop: 4; out1: -2path #1: 0,
我们可以看到这些信息:
compiled vars
表示编译时期生成所有变量。filename
当前文件名称。function name
当前所在的方法名称。number of ops
当前方法所有 OPCODE 总数。opcode line
区域是当前方法内所有 OPCODE 列表。我们依次来看看每列的含义:line
表示对应源码的行号;op
表示对应 OPCODE;return
表示返回的变量;operands
是操作数列表(一般的 OPCODE 包含1 ~ 2个操作数)。
-dvld.active=1
是 VLD 的基础参数,表示激活 VLD 模式。VLD 还支持其他参数,参数详情可以参考 VLD扩展使用指南 这篇文章,我在此引用其部分内容,方便查阅:
-dvld.active
是否在执行 PHP 时激活 VLD 挂钩,默认为0,表示禁用。可以使用-dvld.active=1
启用。-dvld.skip_prepend
是否跳过 php.ini 配置文件中auto_prepend_file
指定的文件,默认为0,即不跳过包含的文件,显示这些包含的文件中的代码所生成的中间代码。此参数生效有一个前提条件:-dvld.execute=0
。-dvld.skip_append
是否跳过 php.ini 配置文件中auto_append_file
指定的文件,默认为0,即不跳过包含的文件,显示这些包含的文件中的代码所生成的中间代码。此参数生效有一个前提条件:-dvld.execute=0
。-dvld.execute
是否执行这段 PHP 脚本,默认值为1,表示执行。可以使用-dvld.execute=0
,表示只显示中间代码,不执行生成的中间代码。-dvld.format
是否以自定义的格式显示,默认为0,表示否。可以使用-dvld.format=1
,表示以自己定义的格式显示。这里自定义的格式输出是以-dvld.col_sep
指定的参数间隔。-dvld.col_sep
在-dvld.format
参数启用时此函数才会有效,默认为 “\t”。-dvld.verbosity
是否显示更详细的信息,默认为1,其值可以为0 ~ 3其实比0小的也可以,只是效果和0一样,比如0.1之类,但是负数除外,负数和效果和3的效果一样 比3大的值也是可以的,只是效果和3一样。-dvld.save_dir
指定文件输出的路径,默认路径为 /tmp。-dvld.save_paths
控制是否输出文件,默认为0,表示不输出文件。-dvld.dump_paths
控制输出的内容,现在只有0和1两种情况,默认为1,输出内容。
GDB
强大的 GDB 调试工具相信不需要我过多介绍吧。接下来,主要说一说如何使用 GDB 调试 PHP 源码。
开启 PHP 的调试模式
建议重新安装一个纯净的 PHP。通常我会选择直接下载 Github 上的源码。
~> git clone http://git.php.net/repository/php-src.git~> cd php-src
由于是从 Git 仓库中直接下载的文件,需要执行 buildconf
命令,该命令会生成编译所需的 configure 文件。
~/php-src> ./buildconf
然后就是标准的安装的流程,这里我们打开了调试模式,并且将其他的所有扩展都禁止安装。
~/php-src> ./configure --disable-all --enable-debug --prefix=/Users/joshua/Tools/bin/php-dev~/php-src> make && make install
可以看到,prefix
参数我们设置为 /Users/joshua/Tools/bin/php-dev
,这样,这个纯净的 PHP 就被安装到这个目录下。接下来,我们介绍的内容,都基于这个目录下编译的 PHP。
使用 GBD 调试代码
我们还是使用上一节创建的 simple.php
文件,我们进入 GDB 调试模式:
~/php-src> cd ~/test/php~/php> sudo gdb --args /Users/joshua/Tools/bin/php-dev/bin/php ./simple.phpGNU gdb (GDB) 7.12Copyright (C) 2016 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-apple-darwin15.6.0".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from /Users/joshua/Tools/bin/php-dev/bin/php...done.
这里有一点需要注意,我使用的环境是 MacOS,需要使用管理员权限才能成功运行 GDB。接下来将演示如何调试 PHP。
首先,我们先执行 run 命令,运行我们需要调试的程序,由于我们没有设置断点,程序会直接运行完成后退出。
(gdb) runStarting program: /Users/joshua/Tools/bin/php-dev/bin/php ./simple.php2[Inferior 1 (process 6924) exited normally]
然后,我们在 PHP 的入口函数中设置断点。
(gdb) break mainBreakpoint 1 at 0x1003d9aa0: file sapi/cli/php_cli.c, line 1199.
当我们再次运行程序时,我们会发现,程序在我们设置断点的地方中断了。
(gdb) runStarting program: /Users/joshua/Tools/bin/php-dev/bin/php ./test.phpBreakpoint 1, main (argc=2, argv=0x7fff5fbffb38) at sapi/cli/php_cli.c:11991199int exit_status = SUCCESS;
这时,我们可以通过 next 指令,单步执行代码:
(gdb) next1204char *ini_entries = NULL;(gdb) next1205int ini_entries_len = 0;(gdb) next1206int ini_ignore = 0;(gdb) next1207sapi_module_struct *sapi_module = &cli_sapi_module;
需要结束调试,可以执行 quit 命令离开 GDB。
(gdb) quitA debugging session is active.Inferior 1 [process 7101] will be killed.
GBD 常用命令
我总结了一些 GDB 的常用命令,以及命令的简写形式,方便大家使用时查阅:
r
run
运行指定的程序(在执行 gdb 脚本时指定的程序)b
break
** 在某断代码上增加断点c
continue
运行到下一个断点p
变量名/表达式 查看变量的值/执行表达式n
next
执行下一条语句s
step
执行下一条语句(可以进入到方法内部)finish
fin
跳出当前方法clear
清除下一个断点delete
清除所有断点info locals
列出所有当前上下文变量
总结
工欲善其事必先利其器,使用合适的工具可以让学习事半功倍。当然,工具即是方法,方法终究只是辅助,没有正确的思路作为指导,再牛逼的工具也发挥不了它应该有的作用。学习的道路还很漫长,有新的思路,从而衍生出的新工具和方法,我会在本文里继续补充,也欢迎大家介绍新的思路和工具给我。
- PHP 内核分析经验谈:工具篇
- 关于Linux系统内核源代码分析经验谈
- 关于Linux内核源代码分析经验谈
- 关于Linux内核源代码分析经验谈
- Linux内核源代码分析工具
- Linux内核源代码分析工具
- 分析内核问题的工具
- PHP性能分析工具
- PHP分析工具安装
- PHP内核分析(一)
- PHP内核分析(二)
- php内核分析-opcode
- php内核分析1 sapi_module_struct
- php面试经验谈
- PHP内核研究: PHP源码目录分析
- Linux内核编程实战经验谈
- Linux内核编程实战经验谈
- Linux内核编程实战经验谈
- 经典的站外seo方法和做法?
- 【转】后勤常用查询报告
- 数组
- zookeeper适用场景:配置文件同步
- Spring4+Springmvc+quartz实现多线程动态定时调度
- PHP 内核分析经验谈:工具篇
- Java算法之希尔排序
- Lambda使用
- windows下搭建基于nginx的rtmp服务器
- 想成为前端开发工程师,不能再错过这次机会了
- HDU 4279Number (打表找规律)
- Spring Cloud分布式微服务云架构图
- 想成为前端开发工程师,不能再错过这次机会了
- [arc080f]Prime Flip