使用xhprof测试slim框架的性能时,发现php抛了Segment Fault错误,试着使用gdb查看是什么导致的。

生成coredump文件

要生成php的coredump文件需要对系统进行一些配置,具体查看:Generating core-dump for php5-fpm

使用gdb调试coredump文件

执行gdb命令,因为coredump是php-fpm进程生成的,还需要指定php-fpm的路径,要不查看不了具体的代码执行信息。

$ gdb /usr/local/php5/sbin/php-fpm -c /tmp/core-php-fpm.1567 

gdb调试命令

bt

bt命令打印程序执行的堆栈信息

(gdb) bt
#0  _phpi_pop (ht=<value optimized out>, return_value=0x7faf756ace10, return_value_ptr=0x0, this_ptr=<value optimized out>, 
    return_value_used=<value optimized out>, off_the_end=0) at /home/apps/php-5.6.10/ext/standard/array.c:1879
#1  0x00007faf7d8f3317 in hp_execute_internal (execute_data=0x7faf8991db40, fci=0x0, ret=1) at /home/apps/xhprof-master/extension/xhprof.c:1730
#2  0x0000000000888cc5 in zend_do_fcall_common_helper_SPEC (execute_data=<value optimized out>) at /home/apps/php-5.6.10/Zend/zend_vm_execute.h:560
#3  0x00000000008787d0 in execute_ex (execute_data=0x7faf8991db40) at /home/apps/php-5.6.10/Zend/zend_vm_execute.h:363
#4  0x00007faf7d8f2ca9 in hp_execute_ex (execute_data=0x7faf8991db40) at /home/apps/xhprof-master/extension/xhprof.c:1675

p

p命令用于打印变量信息

(gdb) p *executor_globals->argument_stack
$2 = {top = 0x7faf8991dc38, end = 0x7faf8993d848, prev = 0x0}

f

切换当前的栈,一般来说,程序停止时,最顶层的栈就是当前栈

# 切换到1层级的栈
(gdb) f 1
#1  0x00007f701d60c317 in hp_execute_internal (execute_data=0x7f702940a858, fci=0x0, ret=1) at /home/apps/xhprof-master/extension/xhprof.c:1730
1730	      ((zend_internal_function *) execute_data->function_state.function)->handler(

# 查看当前栈信息
(gdb) info f
Stack level 1, frame at 0x7fff515e2090:
 rip = 0x7f701d60c317 in hp_execute_internal (/home/apps/xhprof-master/extension/xhprof.c:1730); saved rip 0x888a15
 called by frame at 0x7fff515e2150, caller of frame at 0x7fff515e2040
 source language c.
 Arglist at 0x7fff515e2038, args: execute_data=0x7f702940a858, fci=0x0, ret=1
 Locals at 0x7fff515e2038, Previous frame's sp is 0x7fff515e2090
 Saved registers:
  rbx at 0x7fff515e2058, rbp at 0x7fff515e2060, r12 at 0x7fff515e2068, r13 at 0x7fff515e2070, r14 at 0x7fff515e2078, r15 at 0x7fff515e2080,
  rip at 0x7fff515e2088

info

查看信息

# 打印局部变量信息
info locals
# 打印全局和局部变量信息
info variables 
# 打印参数信息
info args
# 打印当前堆栈信息
info f

php脚本调试命令

使用php脚本调试命令,需要引入php源码中的脚本文件.gdbinit。

(gdb) source /home/apps/php-5.6.10/.gdbinit 

zbacktrace

zbacktrace可以打印php的堆栈信息,不同于bt只打印c的堆栈信息。

(gdb) zbacktrace
[0x7faf8991db40] array_shift(array(2)[0x7faf756acc88]) /var/www/html/php-framework-benchmark/slim-2.6/vendor/slim/slim/Slim/Slim.php:441 
[0x7faf8991da18] Slim\Slim->mapRoute(array(2)[0x7faf897a0590]) /var/www/html/php-framework-benchmark/slim-2.6/vendor/slim/slim/Slim/Slim.php:473 
[0x7faf8991d910] Slim\Slim->get("/hello/index", object[0x7faf897a0510]) /var/www/html/php-framework-benchmark/slim-2.6/index.php:18 

print_cvs打印当前执行命令下的所有php变量

printzv

打印zval变量信息,可指定zval的指针地址,如zbacktrace返回的函数参数地址

(gdb) printzv 0x7faf756acc88
[0x7faf756acc88] (refcount=2,is_ref) array(2): {
    0 => [0x7faf8979ee40] (refcount=4) string(12): "/hello/index"
    1 => [0x7faf897a0510] (refcount=3) objectYou can't do that without a process to debug.

print_ht是用打印zval变量信息

print_htptr是用打印zval指针指向的变量信息

print_htstr用于打印string构成的Hashtable变量信息

print_ft用于打印function table (HashTable)

zmemcheck

zmemcheck用于显示当前的内在分配状态信息

php全局对象executor_globals

php源码用有个用EG宏表示的全局对象executor_globals,它包含了php所有相关的执行信息,如当前执行的op_array,传递参数和函数名等信息。源码中的EG(v)实际是调用executor_globals->v。executor_globals包含的变量有很多,可以用命令:

p executor_globals

打印出来查看。

下面是比较重要的几个变量:

argument_stack       : 传递给当前调用函数的参数堆栈,可以查看参数信息
current_execute_data : 当前执行的命令信息,如当前执行函数信息,op_array,op_code等
prev_execute_data    :前一执行命令信息,相当于调用函数的上级函数
active_op_array      : 当前正在执行的op_array
exception            : 当前包含的异常信息

查看示例:

## 查看当前执行命令信息
(gdb) p *executor_globals->current_execute_data
$8 = {opline = 0x7faf76376fd0, function_state = {function = 0x1123720, arguments = 0x7faf8991dc30}, op_array = 0x7faf897bf9f8, object = 0x0, 
  symbol_table = 0x0, prev_execute_data = 0x7faf8991da18, old_error_reporting = 0x0, nested = 0 '\000', original_return_value = 0x0, 
  current_scope = 0x7faf8991da28, current_called_scope = 0x0, current_this = 0x0, fast_ret = 0x0, delayed_exception = 0x0, 
  call_slots = 0x7faf8991dc08, call = 0x7faf8991dc08}
  
## 查看前一执行命令信息,相当于上级调用函数
(gdb) p *executor_globals->current_execute_data->prev_execute_data
$19 = {opline = 0x7faf763781e8, function_state = {function = 0x7faf897bf9f8, arguments = 0x7faf8991dad8}, op_array = 0x7faf897bf020, 
  object = 0x7faf8978adf0, symbol_table = 0x0, prev_execute_data = 0x7faf8991d910, old_error_reporting = 0x0, nested = 0 '\000', 
  original_return_value = 0x0, current_scope = 0x7faf897c2d48, current_called_scope = 0x7faf897c2d48, current_this = 0x7faf8978adf0, 
  fast_ret = 0x7faf897c2d48, delayed_exception = 0x0, call_slots = 0x7faf8991dab0, call = 0x7faf8991dab0}

扩展阅读

如何调试PHP的Core之获取基本信息
TIPI: 深入理解PHP内核