使用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 相关文章推荐
基于OpenCV的PHP图像人脸识别技术
Oct 11 PHP
PHP 多维数组排序(usort,uasort)
Jun 30 PHP
php 数组的指针操作实现代码
Feb 08 PHP
php函数的常用方法及注意之处小结
Jul 10 PHP
PHP删除非空目录的函数代码小结
Feb 28 PHP
php实现监听事件
Nov 06 PHP
php函数指定默认值方法的小例子
Dec 04 PHP
学习php设计模式 php实现模板方法模式
Dec 08 PHP
php正则去除网页中所有的html,js,css,注释的实现方法
Nov 03 PHP
php中输出json对象的值(实现方法)
Mar 07 PHP
PHP排序二叉树基本功能实现方法示例
May 26 PHP
laravel 配置路由 api和web定义的路由的区别详解
Sep 03 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获取某个目录大小的代码
2008/09/10 PHP
PHP学习笔记 IIS7下安装配置php环境
2012/10/29 PHP
php5.5新数组函数array_column使用
2013/07/08 PHP
php合并数组并保留键值的实现方法
2018/03/12 PHP
PHP中PCRE正则解析代码详解
2019/04/26 PHP
关于Laravel-admin的基础用法总结和自定义model详解
2019/10/08 PHP
Javascript UrlDecode函数代码
2010/01/09 Javascript
通过下拉框的值来确定输入框是否可以为空的代码
2011/10/18 Javascript
浅析jQuery中使用$所引发的问题
2016/05/29 Javascript
深入理解JavaScript中的块级作用域、私有变量与模块模式
2016/10/31 Javascript
js实现简单的手风琴效果
2017/02/27 Javascript
js事件委托和事件代理案例分享
2017/07/25 Javascript
使用Ajax和Jquery配合数据库实现下拉框的二级联动的示例
2018/01/25 jQuery
layui表格checkbox选择全选样式及功能的实例
2018/03/07 Javascript
详解node.js创建一个web服务器(Server)的详细步骤
2021/01/15 Javascript
[10:42]Team Liquid Vs Newbee
2018/06/07 DOTA
python基础教程之序列详解
2014/08/29 Python
python中global与nonlocal比较
2014/11/21 Python
python编程开发之日期操作实例分析
2015/11/13 Python
Python使用Paramiko模块编写脚本进行远程服务器操作
2016/05/05 Python
python实现折半查找和归并排序算法
2017/04/14 Python
详解从Django Rest Framework响应中删除空字段
2019/01/11 Python
Python3实现从排序数组中删除重复项算法分析
2019/04/03 Python
简单了解python的内存管理机制
2019/07/08 Python
VSCode中自动为Python文件添加头部注释
2019/11/14 Python
Python对称的二叉树多种思路实现方法
2020/02/28 Python
PyTorch实现重写/改写Dataset并载入Dataloader
2020/07/14 Python
遮罩层 + Iframe实现界面自动显示的示例代码
2020/04/26 HTML / CSS
Muziker英国:中欧最大的音乐家商店
2020/02/05 全球购物
WEB控件可以激发服务端事件,请谈谈服务端事件是怎么发生并解释其原理?自动传回是什么?为什么要使用自动传回?
2012/02/21 面试题
最新党员的自我评价分享
2013/11/04 职场文书
财务管理个人自荐书范文
2013/11/24 职场文书
数学高效课堂实施方案
2014/03/29 职场文书
2014年群众路线党员自我评议
2014/09/24 职场文书
大学生如何逃脱“毕业季创业队即散伙”魔咒?
2019/08/19 职场文书
python playwrigh框架入门安装使用
2022/07/23 Python