使用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 相关文章推荐
一次编写,随处运行
Oct 09 PHP
vBulletin HACK----关于排版的两个HACK
Oct 09 PHP
落伍首发 php+mysql 采用ajax技术的 省 市 地 3级联动无刷新菜单 源码
Dec 16 PHP
可定制的PHP缩略图生成程式(需要GD库支持)
Mar 06 PHP
cmd下运行php脚本
Nov 25 PHP
PHP目录函数实现创建、读取目录教程实例
Jan 13 PHP
在PHP中设置、使用、删除Cookie的解决方法
May 06 PHP
关于PHP的curl开启问题探讨
Apr 08 PHP
discuz目录文件资料汇总
Dec 30 PHP
PHP中Session可能会引起并发问题
Jun 26 PHP
Yii实现Command任务处理的方法详解
Jul 14 PHP
PHP使用gearman进行异步的邮件或短信发送操作详解
Feb 27 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代码
2007/03/03 PHP
微信支付开发教程(一)微信支付URL配置
2014/05/28 PHP
smarty中常用方法实例总结
2015/08/07 PHP
php的扩展写法总结
2019/05/14 PHP
TNC vs RR BO3 第一场 2.14
2021/03/10 DOTA
jquery validate.js表单验证的基本用法入门
2010/05/13 Javascript
手把手教你自己写一个js表单验证框架的方法
2010/09/14 Javascript
javascript 系统文件夹文件操作及参数介绍
2013/01/08 Javascript
JavaScript获取onclick、onchange等事件值的代码
2013/07/22 Javascript
JS根据变量保存方法名并执行方法示例
2014/04/04 Javascript
JavaScript事件委托用法分析
2015/01/24 Javascript
JS简单实现动画弹出层效果
2015/05/05 Javascript
jquery插件validation实现验证身份证号等
2015/06/04 Javascript
JavaScript+html5 canvas绘制缤纷多彩的三角形效果完整实例
2016/01/26 Javascript
Jquery插件仿百度搜索关键字自动匹配功能
2016/05/11 Javascript
学习Angularjs分页指令
2016/07/01 Javascript
js addDqmForPP给标签内属性值加上双引号的函数
2016/12/24 Javascript
解决vue-router在同一个路由下切换,取不到变化的路由参数问题
2018/09/01 Javascript
Three.js实现简单3D房间布局
2018/12/30 Javascript
JSX在render函数中的应用详解
2019/09/04 Javascript
ant-design-vue 快速避坑指南(推荐)
2020/01/21 Javascript
学习python分支结构
2019/05/17 Python
PyQt5组件读取参数的实例
2019/06/25 Python
Python 多个图同时在不同窗口显示的实现方法
2019/07/07 Python
关于PyTorch 自动求导机制详解
2019/08/18 Python
Sephora丝芙兰菲律宾官方网站:购买化妆品和护肤品
2017/04/05 全球购物
介绍一下游标
2012/01/10 面试题
怎样自定义一个异常类
2016/09/27 面试题
应届生求职信写作技巧
2013/10/24 职场文书
给儿子的表扬信
2014/01/15 职场文书
2014年图书管理员工作总结
2014/12/01 职场文书
会议邀请函
2015/01/30 职场文书
2015年幼儿教师个人工作总结
2015/05/20 职场文书
家长意见书
2015/06/04 职场文书
HTML中的表单元素介绍
2022/02/28 HTML / CSS
MySQL创建管理子分区
2022/04/13 MySQL