使用GDB调试PHP代码,解决PHP代码死循环问题


Posted in PHP onMarch 02, 2015

最近在帮同事解决Swoole Server问题时,发现有1个worker进程一直处于R的状态,而且CPU耗时非常高。初步断定是PHP代码中发生死循环。

下面通过一段代码展示如何解决PHP死循环问题。

#dead_loop.php

$array = array();

for($i = 0; $i < 10000; $i++)

{

    $array[] = $i;

}

include __DIR__."/include.php";

#include.php

while(1)

{

    usleep(10);

    $keys = array_flip($array);

    $index = array_search(rand(1500, 9999), $array);

    $str = str_repeat('A', $index);

    $strb = test($index, $str);

}

function test($index, $str)

{

    return str_replace('A', 'B', $str);

}

通过ps aux得到进程ID和状态如下,使用gdb -p 进程ptrace跟踪,通过bt命令得到调用栈

htf 3834 2.6 0.2 166676 22060 pts/12 R+ 10:50 0:12 php dead_loop.php

gdb -p 3834

(gdb) bt

#0 0x00000000008cc03f in zend_mm_check_ptr (heap=0x1eaa2c0, ptr=0x2584910, silent=1, __zend_filename=0xee3d40 "/home/htf/workspace/php-5.4.27/Zend/zend_variables.c",

__zend_lineno=182, __zend_orig_filename=0xee1888 "/home/htf/workspace/php-5.4.27/Zend/zend_execute_API.c", __zend_orig_lineno=437)

at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:1485

#1 0x00000000008cd643 in _zend_mm_free_int (heap=0x1eaa2c0, p=0x2584910, __zend_filename=0xee3d40 "/home/htf/workspace/php-5.4.27/Zend/zend_variables.c", __zend_lineno=182,

__zend_orig_filename=0xee1888 "/home/htf/workspace/php-5.4.27/Zend/zend_execute_API.c", __zend_orig_lineno=437) at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:2064

#2 0x00000000008cebf7 in _efree (ptr=0x2584910, __zend_filename=0xee3d40 "/home/htf/workspace/php-5.4.27/Zend/zend_variables.c", __zend_lineno=182,

__zend_orig_filename=0xee1888 "/home/htf/workspace/php-5.4.27/Zend/zend_execute_API.c", __zend_orig_lineno=437) at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:2436

#3 0x00000000008eda0a in _zval_ptr_dtor (zval_ptr=0x25849a0, __zend_filename=0xee3d40 "/home/htf/workspace/php-5.4.27/Zend/zend_variables.c", __zend_lineno=182)

at /home/htf/workspace/php-5.4.27/Zend/zend_execute_API.c:437

#4 0x00000000008fe687 in _zval_ptr_dtor_wrapper (zval_ptr=0x25849a0) at /home/htf/workspace/php-5.4.27/Zend/zend_variables.c:182

#5 0x000000000091259f in zend_hash_destroy (ht=0x7f7263f6e380) at /home/htf/workspace/php-5.4.27/Zend/zend_hash.c:560

#6 0x00000000008fe2c5 in _zval_dtor_func (zvalue=0x7f726426fe50, __zend_filename=0xeea290 "/home/htf/workspace/php-5.4.27/Zend/zend_execute.c", __zend_lineno=901)

at /home/htf/workspace/php-5.4.27/Zend/zend_variables.c:45

#7 0x0000000000936656 in _zval_dtor (zvalue=0x7f726426fe50, __zend_filename=0xeea290 "/home/htf/workspace/php-5.4.27/Zend/zend_execute.c", __zend_lineno=901)

at /home/htf/workspace/php-5.4.27/Zend/zend_variables.h:35

#8 0x0000000000939747 in zend_assign_to_variable (variable_ptr_ptr=0x7f7263f8e738, value=0x7f726426f6a8) at /home/htf/workspace/php-5.4.27/Zend/zend_execute.c:901

#9 0x0000000000997ee5 in ZEND_ASSIGN_SPEC_CV_VAR_HANDLER (execute_data=0x7f726d04b2a8) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:33168

#10 0x000000000093b5fd in execute (op_array=0x21d58b0) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:410

#11 0x0000000000901692 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/htf/workspace/php-5.4.27/Zend/zend.c:1315

#12 0x000000000087926a in php_execute_script (primary_file=0x7ffffe0038d0) at /home/htf/workspace/php-5.4.27/main/main.c:2502

#13 0x00000000009a32e3 in do_cli (argc=2, argv=0x7ffffe004d18) at /home/htf/workspace/php-5.4.27/sapi/cli/php_cli.c:989

#14 0x00000000009a4491 in main (argc=2, argv=0x7ffffe004d18) at /home/htf/workspace/php-5.4.27/sapi/cli/php_cli.c:1365

执行gdb后,死循环的进程会变成T的状态,表示正在Trace。这个是独占的,所以不能再使用strace/gdb或者其他ptrace工具对此进程进行调试。另外此进程会中断执行。gdb输入c后,程序继续向下运行。然后再次按下ctrl + c中断程序。 通过bt命令查看进程的调用栈。

(gdb) bt

#0 _zend_mm_alloc_int (heap=0x1eaa2c0, size=72, __zend_filename=0xe43410 "/home/htf/workspace/php-5.4.27/ext/standard/array.c", __zend_lineno=2719,

__zend_orig_filename=0xee5a38 "/home/htf/workspace/php-5.4.27/Zend/zend_hash.c", __zend_orig_lineno=412) at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:1895

#1 0x00000000008ceb86 in _emalloc (size=72, __zend_filename=0xe43410 "/home/htf/workspace/php-5.4.27/ext/standard/array.c", __zend_lineno=2719,

__zend_orig_filename=0xee5a38 "/home/htf/workspace/php-5.4.27/Zend/zend_hash.c", __zend_orig_lineno=412) at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:2425

#2 0x0000000000911d85 in _zend_hash_index_update_or_next_insert (ht=0x2257a10, h=3972, pData=0x7ffffe0012b0, nDataSize=8, pDest=0x0, flag=1,

__zend_filename=0xe43410 "/home/htf/workspace/php-5.4.27/ext/standard/array.c", __zend_lineno=2719) at /home/htf/workspace/php-5.4.27/Zend/zend_hash.c:412

#3 0x00000000007767e1 in zif_array_flip (ht=1, return_value=0x7f726424ea68, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1)

at /home/htf/workspace/php-5.4.27/ext/standard/array.c:2719

#4 0x000000000093c03e in zend_do_fcall_common_helper_SPEC (execute_data=0x7f726d04b2a8) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:643

#5 0x00000000009400e6 in ZEND_DO_FCALL_SPEC_CONST_HANDLER (execute_data=0x7f726d04b2a8) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:2233

#6 0x000000000093b5fd in execute (op_array=0x21d58b0) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:410

两次的BT信息不一样,这是因为程序在不同的位置中断。看到execute (oparray=0x21d58b0) 这一行,这里就是PHP执行oparray的入口了。gdb下输入f 6,(通过调用栈编号可得)。

(gdb) f 6

#6 0x000000000093b5fd in execute (op_array=0x21d58b0) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:410

410 if ((ret = OPLINE->handler(execute_data TSRMLS_CC)) > 0) {

(gdb) p *op_array

$2 = {type = 2 '\002', function_name = 0x7f726d086540 "test", scope = 0x0, fn_flags = 134217728, prototype = 0x0, num_args = 2, required_num_args = 2, arg_info = 0x7f726d086bd8,

refcount = 0x7f726d0870f0, opcodes = 0x7f726424d600, last = 8, vars = 0x7f726424e890, last_var = 2, T = 1, brk_cont_array = 0x0, last_brk_cont = 0, try_catch_array = 0x0,

last_try_catch = 0, static_variables = 0x0, this_var = 4294967295, filename = 0x7f726424ba38 "/home/htf/wwwroot/include.php", line_start = 12, line_end = 15, doc_comment = 0x0,

doc_comment_len = 0, early_binding = 4294967295, literals = 0x7f726424eae0, last_literal = 4, run_time_cache = 0x7f726450bfb0, last_cache_slot = 1, reserved = {0x0, 0x0, 0x0, 0x0}}

这里的filename就能看到op_array是哪个PHP文件的。然后输入f 0进入当前位置。

(gdb) p **executor_globals.opline_ptr

$4 = {handler = 0x93ff9c , op1 = {constant = 1680133296, var = 1680133296, num = 1680133296, hash = 140129283132592, opline_num = 1680133296,

jmp_addr = 0x7f726424ccb0, zv = 0x7f726424ccb0, literal = 0x7f726424ccb0, ptr = 0x7f726424ccb0}, op2 = {constant = 0, var = 0, num = 0, hash = 0, opline_num = 0, jmp_addr = 0x0,

zv = 0x0, literal = 0x0, ptr = 0x0}, result = {constant = 32, var = 32, num = 32, hash = 32, opline_num = 32, jmp_addr = 0x20, zv = 0x20, literal = 0x20, ptr = 0x20},

extended_value = 1, lineno = 5, opcode = 60 '

这里的lineno表示OPCODE所在的代码行数,可以到对应文件里去看下是哪行代码。使用GDB可以查看到更多的信息,这里就不再一一介绍了,有兴趣各位可以自行尝试。

zbacktrace的使用

zend官方提供了一个gdb的脚本,对指令进行了封装,可以直接看到php函数的调用关系。在php源代码包的根目录中有一个.gdbinit。使用

source your_php_src_path/.gdbinit

zbacktrace

可以直接看到PHP函数的调用堆栈。

以上就是本文的全部内容了,希望大家能够喜欢。

PHP 相关文章推荐
PHP实现Socket服务器的代码
Apr 03 PHP
PHP MemCached高级缓存配置图文教程
Aug 05 PHP
PHP加密扩展库Mcrypt安装和实例
Nov 10 PHP
PHP提示Warning:phpinfo() has been disabled函数禁用的解决方法
Dec 17 PHP
初识laravel5
Mar 02 PHP
php在数据库抽象层简单使用PDO的方法
Nov 03 PHP
PHP中读取文件的几个方法总结(推荐)
Jun 03 PHP
JSON字符串传到后台PHP处理问题的解决方法
Jun 05 PHP
详解Yii2.0 rules验证规则集合
Mar 21 PHP
Laravel 5使用Laravel Excel实现Excel/CSV文件导入导出的功能详解
Oct 11 PHP
Laravel框架源码解析之反射的使用详解
May 14 PHP
通过实例解析PHP数据类型转换方法
Jul 11 PHP
Laravel5中contracts详解
Mar 02 #PHP
php打印一个边长为N的实心和空心菱型的方法
Mar 02 #PHP
初识laravel5
Mar 02 #PHP
避免Smarty与CSS语法冲突的方法
Mar 02 #PHP
实现PHP+Mysql无限分类的方法汇总
Mar 02 #PHP
Java和PHP在Web开发方面对比分析
Mar 01 #PHP
php中return的用法实例分析
Feb 28 #PHP
You might like
php反射应用示例
2014/02/25 PHP
详解PHP函数 strip_tags 处理字符串缺陷bug
2017/06/11 PHP
Laravel5.1框架路由分组用法实例分析
2020/01/04 PHP
深入浅析安装PhpStorm并激活的步骤详解
2020/09/17 PHP
json数据处理技巧(字段带空格、增加字段、排序等等)
2013/06/14 Javascript
JavaScript 上万关键字瞬间匹配实现代码
2013/07/07 Javascript
在表单提交前进行验证的几种方式整理
2013/07/31 Javascript
基于jquery的禁用右键、文本选择功能、复制按键的实现代码
2013/08/27 Javascript
JS:window.onload的使用介绍
2013/11/13 Javascript
Javascript实现滚动图片新闻的实例代码
2013/11/27 Javascript
javascript中attachEvent用法实例分析
2015/05/14 Javascript
javascript实现密码验证
2015/11/10 Javascript
Jquery zTree 树控件异步加载操作
2016/02/25 Javascript
node.js cookie-parser之parser.js
2016/06/06 Javascript
JavaScript实现向select下拉框中添加和删除元素的方法
2017/03/07 Javascript
使用Vue开发一个实时性时间转换指令
2018/01/17 Javascript
vue实现购物车案例
2020/05/30 Javascript
vue组件开发之tab切换组件使用详解
2020/08/21 Javascript
vant 解决tab切换插件标题样式自定义的问题
2020/11/13 Javascript
[02:11]DOTA2上海特级锦标赛主赛事第二日RECAP
2016/03/04 DOTA
Python装饰器(decorator)定义与用法详解
2018/02/09 Python
python实现微信自动回复功能
2018/04/11 Python
Tensorflow中的placeholder和feed_dict的使用
2018/07/09 Python
使用python打印十行杨辉三角过程详解
2019/07/10 Python
python快速编写单行注释多行注释的方法
2019/07/31 Python
python之yield和Generator深入解析
2019/09/18 Python
Django之form组件自动校验数据实现
2020/01/14 Python
结束运行python的方法
2020/06/16 Python
python lambda的使用详解
2021/02/26 Python
可以随进度显示不同颜色的css3进度条分享
2014/04/11 HTML / CSS
css3 条纹化和透明化表格Firefox下测试成功
2014/04/15 HTML / CSS
销售主管岗位职责范本
2014/02/14 职场文书
连锁超市项目计划书
2014/09/15 职场文书
村主任群众路线个人对照检查材料
2014/09/26 职场文书
网吧管理制度范本
2015/08/05 职场文书
CSS实现切角+边框+投影+内容背景色渐变效果
2021/11/01 HTML / CSS