Monday, February 27, 2012

How to dump PHP stack from gdb

The case:
  1. Php-based applications running on a linux server, be it under httpd or php-cgi process
  2. One of the applications misbehave, i.e. page never displayed until timeout
  3. Timeout mechanism doesn't work or we're unable to pinpoint the problem from the timeout message written on the logfile
We would like to dump PHP stack traces from a running PHP script.
Required tool: xdebug, gdb.
Using standard xdebug functionality, debugging is only possible when php debugging explicitly initiated (eclipse/phpstorm must be running and the page is requested with debugging key activated). Just in time debugging in xdebug means debug when an error occured, which is not always the case. Remote_autostart means to debug in every time a php page is requested, which is an overkill in my opinion.
Procedure :
  • obtain list of pids from processes we're interested in (ps auxw | grep httpd)
  • guess which pid (or iterate pids)
  • run gdb, attach to the pid (gdb --pid=nn)
  • run this gdb script (i.e source dumpphpstack.txt)
dumpphpstack.txt : (tested on x86_64 architecture, CentOS 6)
set $xstack = ((long**)&xdebug_globals)[2]
set $pcurrent = (long*)$xstack[0]
while $pcurrent
set $xptr = (long*)$pcurrent[0]
set $xptr_s = (char**)$xptr
set $xptr_i = (int*)$xptr
set $filename = $xptr_s[4]
set $funcname = $xptr_s[1]
set $linenum = $xptr_i[10]
printf "%s@%s:%d\n", $funcname, $filename, $linenum
set $pnext = (long*)$pcurrent[2]
set $pcurrent = $pnext
This script may need adjustment because architectural difference or field alignment variations. Now if just someone could teach me how to iterate pids via shell script, I could've create the first PHP sampling profiler..

revised version of the script as follows :