加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

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

发布时间:2020-12-13 02:26:26 所属栏目:PHP教程 来源:网络整理
导读:《使用GDB调试PHP代码,解决PHP代码死循环问题》要点: 本文介绍了使用GDB调试PHP代码,解决PHP代码死循环问题,希望对您有用。如果有疑问,可以联系我们。 最近在帮同事办理Swoole Server问题时,发现有1个worker进程一直处于R的状态,而且CPU耗时非常高.初步

《使用GDB调试PHP代码,解决PHP代码死循环问题》要点:
本文介绍了使用GDB调试PHP代码,解决PHP代码死循环问题,希望对您有用。如果有疑问,可以联系我们。

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

下面通过一段代码展示如何办理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命令得到调用栈PHP教程

代码如下:

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_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_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_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_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命令查看进程的调用栈.PHP教程

代码如下:

(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_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,(通过调用栈编号可得).PHP教程

代码如下:

(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 '02',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}}

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

代码如下:

(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可以查看到更多的信息,这里就不再一一介绍了,有兴趣各位可以自行尝试.PHP教程

zbacktrace的使用PHP教程

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

代码如下:

source your_php_src_path/.gdbinit
zbacktrace

可以直接看到PHP函数的调用堆栈.PHP教程

以上便是本文的全部内容了,希望大家能够喜欢.PHP教程

欢迎参与《使用GDB调试PHP代码,解决PHP代码死循环问题》讨论,分享您的想法,编程之家 52php.cn为您提供专业教程。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读