You are browsing a read-only backup copy of Wikitech. The live site can be found at wikitech.wikimedia.org

GDB with PHP

From Wikitech-static
Jump to navigation Jump to search

Some handy things to know about using GDB to debug PHP.


Basics

  • Compile PHP with export CFLAGS=-ggdb3 .
  • The usual reason to use GDB to debug PHP is because PHP is segfaulting. Run PHP under GDB until it segfaults, then run "bt" to show the C backtrace. That gives you an idea of the problem location in the C code. Sometimes it's useful to also know what the problem was in the PHP code. You can use the phpbt macro given below.
  • If the backtrace is corrupted, then give logging a go instead to identify the PHP code. For instance, you can use my magic segfault tracker.

Backtraces by calling functions

(gdb) call zif_debug_print_backtrace(0,(zval*)0,(zval**)0, (zval*)0, 0)
#0  Language->getLanguageName() called at [/usr/local/apache/common-local/php-1.5/includes/Parser.php:1660]
#1  Parser->replaceInternalLinks() called at [/usr/local/apache/common-local/php-1.5/includes/Parser.php:965]
#2  Parser->internalParse() called at [/usr/local/apache/common-local/php-1.5/includes/Parser.php:299]
#3  Parser->parse() called at [/home/wikipedia/common/php-1.5/maintenance/eval.php(48) : eval()'d code:1]
#4  eval() called at [/home/wikipedia/common/php-1.5/maintenance/eval.php:48]

This only works if you don't have any output buffer. You can call any function that doesn't touch the return value this way. However most functions do try to write to the return value, so for that you need a target zval for the second and third parameters.

set $zp = emalloc(sizeof(zval))
set $zpp = emalloc(sizeof(zval*))
set *(zval**)$zpp = $zp
call zif_ob_end_clean(0, $zp, $zpp, (zval*)0, 0)
call zif_debug_print_backtrace(0,(zval*)0,(zval**)0, (zval*)0, 0)

OMG 1337 haxor! Anyone would think C was an interpreted language.

But what about when running under apache? How do we get the output to go somewhere we can see it, instead of going back to apache?

(gdb) set output_globals.php_body_write = php_default_output_func

That would send all output to stderr, skipping the output buffer. But apache redirects stderr -- lsof says:

httpd   25969 apache    0u   CHR              136,0                   2 /dev/pts/0
httpd   25969 apache    1u   CHR              136,0                   2 /dev/pts/0
httpd   25969 apache    2w   CHR                1,3                2366 /dev/null

Never fear, a few syscalls and all will be well:

call close(2)
call dup2(1,2)
set output_globals.php_body_write = php_default_output_func
call zif_debug_print_backtrace(0,(zval*)0,(zval**)0, (zval*)0, 0)

Backtraces by inspecting data

The following user-defined GDB commands are also available with

source ~tstarling/php.gdb


Function names

Switch to a frame with an execute_data local variable

define phpbt
  set $ed=execute_data
  while $ed
    print ((zend_execute_data *)$ed)->function_state.function->common.function_name
    set $ed = ((zend_execute_data *)$ed)->prev_execute_data
  end
end

Example:

(gdb) phpbt
$1 = 0x2aaaaf33360f "preg_split"
$2 = 0x2aaab018fc48 "extractTagsAndParams"
$3 = 0x2aaab0194d40 "strip"
$4 = 0x2aaab0182780 "parse"
$5 = 0x2aaab0449740 "addWikiTextTitle"
$6 = 0x2aaab0448530 "addWikiText"
$7 = 0x2aaab06d2800 "showEditForm"
$8 = 0x2aaab06a0698 "edit"
$9 = 0x2aaab06a02b8 "submit"
$10 = 0x2aaab04203e0 "performAction"
$11 = 0x2aaab040a470 "initialize"
$12 = 0x0

Symbol table variable names

define hashkeys
  set $p = (HashTable*)$arg0->pListHead
  while $p
    output (Bucket*)$p
    echo \t
    x/s (char*)(((Bucket*)$p)->arKey)
    set $p = ((Bucket*)$p)->pListNext
  end
end

Example:

(gdb) hashkeys execute_data->symbol_table
(struct bucket *) 0xc40b28      0xc40b68:        "t"
(struct bucket *) 0xc40bd8      0xc40c18:        "fname"
(struct bucket *) 0xc40ff8      0xc41038:        "td"
(struct bucket *) 0xc40de8      0xc40e28:        "ltd"
(struct bucket *) 0x17b48f8     0x17b4938:       "tr"
(struct bucket *) 0x17b2da8     0x17b2de8:       "ltr"
(struct bucket *) 0x1528d18     0x1528d58:       "has_opened_tr"
(struct bucket *) 0x1528ad8     0x1528b18:       "indent_level"
(struct bucket *) 0x17b4fe8     0x17b5028:       "x"
(struct bucket *) 0x17b3298     0x17b32d8:       "k"
(struct bucket *) 0x10623f8     0x1062438:       "fc"
(struct bucket *) 0xc32c28      0xc32c68:        "matches"
(struct bucket *) 0xc34768      0xc347a8:        "attributes"


Symbol table data

define bucketdata
print **(zval**)(((struct bucket *) $arg0)->pData)
end

Example following on from previous:

(gdb) bucketdata 0xc40bd8
$175 = {value = {lval = 12802728, dval = 6.3253880778498107e-317, str = {
      val = 0xc35aa8 "Parser::doTableStuff", len = 20}, ht = 0xc35aa8, obj = {handle = 12802728,
      handlers = 0x14}}, refcount = 1, type = 6 '\006', is_ref = 0 '\0'}