Debugging PHP segfault backtraces with `gdb`
2015-12-10 11:09
796 查看
http://www.robertames.com/blog.cgi/entries/debugging-php-segfault-backtraces-with-gdb.html
Hello, and welcome. I am going to assume you have gone through the normal PHP documentation about how to get core files, load them into
command.
In my particular case at work, I ran into a PHP segfault in in the
This was clear from the multiple backtraces we had captured, consistently
Looking at the PHP doc’s there were a few more items to try.
Great… still
Obviously there are other “execute” items on the stack, but how to get to them, and what to do with them once you’re there?
With some help from the wonderful
You already know
Through some googling, I learned a little about the
from the PHP web page always printed out the same data.
Then I learned about the
From a “C” perspective, the function question is always
That looks promising, but I have no idea what I’m looking at or how to figure out what is hidden away in this core dump. At this point my IRC buddy on
Let’s go back to the original PHP
of a sudden makes the following line very important:
…and helps to understand a bit the original
OK, so what is
Now what else can we do with
Ok, so now we’re getting somewhere. But guess what. There is an even simpler way of doing the above.
…but how do I know that I can access
So now we’re really getting somewhere.
Wow. Now that is pretty productive. I started with a PHP script that randomly segfaulted and dumped core. From the core file I can generate a “C” backtrace. Walking through the “execute” statements I can get at basically the “PHP” backtrace. And furthermore
I can walk a bit more “forward” into “C-land” and get info from the C module itself (
Messing with GDB was really quite rewarding and just so long as I post this guide to my blog, I won’t forget it next time I need to go debugging PHP segfaults.
Hello, and welcome. I am going to assume you have gone through the normal PHP documentation about how to get core files, load them into
gdband run the
bt(backtrace)
command.
In my particular case at work, I ran into a PHP segfault in in the
oci_executefunction. It’s a C function / module for querying the oracle databases which evidently has some sort of crash bug in certain circumstances.
This was clear from the multiple backtraces we had captured, consistently
OCIStmtExecuteexposed in PHP via
oci_execute().
(gdb) bt #0 0x00000000 in ?? () #1 0xf6f83cd6 in ttcdrv () from /lib/libclntsh.so.10.1 #2 0xf6e25461 in nioqwa () from /lib/libclntsh.so.10.1 #3 0xf6c97032 in upirtrc () from /lib/libclntsh.so.10.1 #4 0xf6c2dce9 in kpurcsc () from /lib/libclntsh.so.10.1 #5 0xf6be9fba in kpuexecv8 () from /lib/libclntsh.so.10.1 #6 0xf6bec360 in kpuexec () from /lib/libclntsh.so.10.1 #7 0xf6c60b3a in OCIStmtExecute () from /lib/libclntsh.so.10.1 #8 0xf7609199 in php_oci_statement_execute (statement=0xf1446534, mode=138243868) at /...442 #9 0xf760fdc5 in zif_oci_execute (ht=1, return_value=0xf1325ab4, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1) at /...1302 #10 0x081ab5c1 in zend_do_fcall_common_helper_SPEC (execute_data=0xffff60d0) at /...200 #11 0x081aad69 in execute (op_array=0xf61a00b8) at /...92 #12 0x081aafb5 in zend_do_fcall_common_helper_SPEC (execute_data=0xffff6500) at /...234 #13 0x081aad69 in execute (op_array=0xf7b9378c) at /...92 #14 0x081aafb5 in zend_do_fcall_common_helper_SPEC (execute_data=0xffff6650) at /...234 #15 0x081aad69 in execute (op_array=0xf7ba6a9c) at /...92 #16 0x081aafb5 in zend_do_fcall_common_helper_SPEC (execute_data=0xffff6cd0) at /...234 #17 0x081aad69 in execute (op_array=0xf5c2db80) at /...92 #18 0x081aafb5 in zend_do_fcall_common_helper_SPEC (execute_data=0xffff7000) at /...234 #19 0x081aad69 in execute (op_array=0xf5b69434) at /...92 #20 0x081aafb5 in zend_do_fcall_common_helper_SPEC (execute_data=0xffff99c0) at /...234 #21 0x081aad69 in execute (op_array=0xf5ad5684) at /...92 #22 0x081aafb5 in zend_do_fcall_common_helper_SPEC (execute_data=0xffff9d20) at /...234 #23 0x081aad69 in execute (op_array=0xf5ad46fc) at /...92 #24 0x081aafb5 in zend_do_fcall_common_helper_SPEC (execute_data=0xffffb090) at /...234 #25 0x081aad69 in execute (op_array=0xf7b72f1c) at /...92 #26 0x08191311 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /...1135 #27 0x08157b1a in php_execute_script (primary_file=0xffffd640) at /...2064 #28 0x0820e6b4 in main (argc=18, argv=0xffffd724) at /...1176
Looking at the PHP doc’s there were a few more items to try.
(gdb) print (char *)(executor_globals.function_state_ptr->function)->common.function_name $1 = 0x218bac "oci_execute" (gdb) print (char *)executor_globals.active_op_array->function_name $2 = 0xb7beaa5c "getData" (gdb) print (char *)executor_globals.active_op_array->filename $3 = 0xb7b4e4c4 "DatabaseConnection.php"
Great… still
oci_execute, and yep, it’s called via the database wrapper. But what SQL is being executed? What is the function that is calling the generic
getData()function?
Obviously there are other “execute” items on the stack, but how to get to them, and what to do with them once you’re there?
With some help from the wonderful
#gdbambassador “xdje” on
irc.freenode.net, I was able to get a crash course on using
gdbin a slightly more advanced way for debugging PHP core files.
You already know
btwhich will print you out the backtrace. After that I started with
helpbut I didn’t know how to describe what I was looking for so that was basically a dead end.
Through some googling, I learned a little about the
frame #,
up, and
downcommands which will move your context through the backtrace, but I couldn’t understand why even when I moved through the stack the
print ...commands
from the PHP web page always printed out the same data.
Then I learned about the
info ...subsets. Ok, now we are getting somewhere. In the example above, I knew there was important stuff located in frames 13, 15, 17, etc. which I hoped contained the PHP function names that were responsible for each
execute...line.
From a “C” perspective, the function question is always
execute()but I needed to know what was going through PHP’s tiny little brain.
help info,
info localslooks promising.
(gdb) bt .... (gdb) frame 13 #13 0x081aad69 in execute (op_array=0xb7bead08) at zend_vm_execute.h:92 92 in zend_vm_execute.h (gdb) info frame Stack level 13, frame at 0xbff17080: eip = 0x81aad69 in execute (zend_vm_execute.h:92); saved eip 0x1cbad4 called by frame at 0xbff170d0, caller of frame at 0xbff16e10 source language c. Arglist at 0xbff17078, args: op_array=0xb7bead08 Locals at 0xbff17078, Previous frame's sp is 0xbff17080 Saved registers: ebx at 0xbff17070, ebp at 0xbff17078, esi at 0xbff16e00, edi at 0xbff17074, eip at 0xbff1707c (gdb) info locals execute_data = {opline = 0xb7b55e88, function_state = {function_symbol_table = 0xb7150838, function = 0xa270c48, reserved = {0x8188816, 0xbff170c0, 0x0, 0xbff17068}}, fbc = 0x0, op_array = 0xb7bead08, object = 0x0, Ts = 0xbff16e50, CVs = 0xbff16e20, original_in_execution = 1 '\001', symbol_table = 0xb714f98c, prev_execute_data = 0xbff175e0, old_error_reporting = 0x0}
That looks promising, but I have no idea what I’m looking at or how to figure out what is hidden away in this core dump. At this point my IRC buddy on
xdjejumped in to save the day.
Let’s go back to the original PHP
of a sudden makes the following line very important:
(gdb) info frame Stack level 13, frame at 0xbff17080: eip = 0x81aad69 in execute (zend_vm_execute.h:92); saved eip 0x1cbad4 called by frame at 0xbff170d0, caller of frame at 0xbff16e10 source language c. Arglist at 0xbff17078, args: op_array=0xb7bead08 Locals at 0xbff17078, Previous frame's sp is 0xbff17080 Saved registers: ebx at 0xbff17070, ebp at 0xbff17078, esi at 0xbff16e00, edi at 0xbff17074, eip at 0xbff1707c
…and helps to understand a bit the original
(gdb) print (char *)executor_globals.active_op_array->function_name ^-- GDB command ^-- dereference / type ^-- variable ^-- variable ^-- variable
OK, so what is
executor_globals? Turns out (don’t ask how just yet) that it is basically a global variable. So you go daisy-chaining down the line of function calls and data values and finally you can get to
function_name. Dandy.
Now what else can we do with
(gdb) info frame Stack level 13, frame at 0xbff17080: eip = 0x81aad69 in execute (zend_vm_execute.h:92); saved eip 0x1cbad4 called by frame at 0xbff170d0, caller of frame at 0xbff16e10 ...this is important source language c. ...aha, here is another op_array Arglist at 0xbff17078, args: op_array=0xb7bead08 Locals at 0xbff17078, Previous frame's sp is 0xbff17080 Saved registers: ebx at 0xbff17070, ebp at 0xbff17078, esi at 0xbff16e00, edi at 0xbff17074, eip at 0xbff1707c ...hrm, not what I expected (gdb) print 0xb7bead08->function_name Attempt to extract a component of a value that is not a structure pointer. ...maybe it needs that char * thing? (gdb) print (char *)0xb7bead08->function_name Attempt to extract a component of a value that is not a structure pointer. ...oh, there are operator precedence rules at play here (gdb) print ((char *)0xb7bead08)->function_name Attempt to extract a component of a value that is not a structure pointer. ...here is where IRC buddy saved the day (gdb) info args op_array = (zend_op_array *) 0xb7bead08 ...aha! you have to treat that memory address as a *type* and then you can get data off of it (gdb) print ((zend_op_array *)0xb7bead08)->function_name $11 = 0xb7beaa5c "getData"
Ok, so now we’re getting somewhere. But guess what. There is an even simpler way of doing the above.
...d'oh! you can reference it just like a variable, nothing fancy required (gdb) print op_array->function_name $11 = 0xb7beaa5c "getData"
…but how do I know that I can access
function_nameoff of that
op_arraything? More help from my IRC buddy exposes the
ptypecommand:
...aha! this is like dir(...) in python (gdb) ptype op_array type = struct _zend_op_array { zend_uchar type; char *function_name; zend_class_entry *scope; zend_uint fn_flags; union _zend_function *prototype; zend_uint num_args; zend_uint required_num_args; ... } *
So now we’re really getting somewhere.
...backtrace (gdb) bt ... ...jump to where i want info (gdb) frame 13 ...hrm, see the op_array variable (gdb) info args ...there's a function_name field, looks useful (gdb) ptype op_array ...this is what i'm looking for (gdb) print op_array->function_name $13 = 0xb7beaa5c "getData" ...go to the next function call up the chain (gdb) frame 15 ...print its name too (gdb) print op_array->function_name $14 = 0xb7f6d248 "getMissingDates" ...i wonder if I can get the SQL for this as well (gdb) frame 8 #8 0x00210199 in php_oci_statement_execute (statement=0xb17ccfbc, mode=171218828) at oci8_statement.c:442 442 oci8_statement.c: No such file or directory. in oci8_statement.c (gdb) info args statement = (php_oci_statement *) 0xb17ccfbc mode = 171218828 (gdb) ptype statement type = struct { int id; int parent_stmtid; php_oci_connection *connection; sword errcode; OCIError *err; OCIStmt *stmt; char *last_query; long int last_query_len; HashTable *columns; HashTable *binds; HashTable *defines; int ncolumns; unsigned int executed : 1; unsigned int has_data : 1; ub2 stmttype; } * ...hrm, last_query looks useful? (gdb) print statement->last_query print statement->last_query $15 = 0xb1546574 "select ... from date_table where ..." (gdb) print "bingo" You can't do that without a process to debug.
Wow. Now that is pretty productive. I started with a PHP script that randomly segfaulted and dumped core. From the core file I can generate a “C” backtrace. Walking through the “execute” statements I can get at basically the “PHP” backtrace. And furthermore
I can walk a bit more “forward” into “C-land” and get info from the C module itself (
statement->last_query).
Messing with GDB was really quite rewarding and just so long as I post this guide to my blog, I won’t forget it next time I need to go debugging PHP segfaults.
相关文章推荐
- php数组函数-array_diff()
- 新服配置 ftp 下载文件
- php 使用date及strtotime等日期时间函数的注意
- php数组函数-array_combine()
- php封装的连接Mysql类及用法分析
- php composer
- php数组函数-array_chunk()
- PHP多维数组遍历方法(2种实现方法)
- php-fpm – 启动参数及重要配置详解
- php数组函数-array_change_key_case()
- 优雅的使用 PhpStorm 来开发 Laravel 项目(翻译中)
- DedeCMS安全:FTP设置dedecms目录权限
- php数组函数-array()
- thinkphp中的配置与读取C方法详解
- [Windows Server 2008] 查看PHP详细错误信息
- 破解PHPStrom 10 and Pycharm
- PHP-Java-Bridge使用笔记,2014年9月最新版
- PHP-Java-Bridge的使用(平安银行支付功能专版)
- thinkphp3.2 =>0625-9_文件上传
- php使用mkdir创建多级目录入门例子