How to dump PHP backtrace on live running script in lldb?
Asked Answered
W

2

12

I'm playing around with LLDB (debugger) and I did the following experiment.

  1. Run PHP script as:

    php -r "sleep(1000);"
    

    or:

    php -r "function r(){sleep(1000);}r();"
    
  2. On another console, I've called directly zif_debug_backtrace() from lldb:

    echo 'call (void)zif_debug_backtrace()' | lldb -p $(pgrep -fn php)
    

Above worked, however the process stopped with the following warning:

Warning: sleep() expects at most 2 parameters, 1606408648 given in Command line code on line 1
Call Stack:
    0.0016     235152   1. {main}() Command line code:0
    0.0021     235248   2. sleep(1000) Command line code:1

I'm not quite sure why the script had to stop and what I need to do to achieve transparency (without affecting the script)?

P.S. The same happening when calling zif_debug_print_backtrace() and when calling custom_backtrace() it is showing: Backtrace null function called. I'm using xdebug if that change anything.

Maybe I need to call a different function like zend_fetch_debug_backtrace (see: image dump symtab)? Or use the right arguments, if so, which one?

I'm only interested in lldb/gdb solutions in order to print the backtrace.


Similar approach works in Ruby, e.g.:

  1. Run: ruby -e 'sleep 1000'.
  2. In another terminal: echo 'call (void)rb_backtrace()' | lldb -p $(pgrep -nf ruby).
Worthen answered 5/8, 2015 at 9:53 Comment(0)
M
10

You can't call internal functions like that, internal functions expect things like frame, return value and so on ... don't' do it.

There is a .gdbinit distributed with php, in it there is a function named zbacktrace, you could port that to lldb.

Another thing you could do, which is likely easier, is just call the API function that generates a trace, but call it properly.

Here it is for GDB (PHP7):

define ztrace
    set $var = malloc(sizeof(zval))
    call zend_fetch_debug_backtrace($var, 0, 0, 0)
    call php_var_dump($var, 0)
    call _zval_ptr_dtor($var, 0, 0)
    call free($var)
end

document ztrace
    show a debug backtrace
end 

And for LLDB (PHP7):

(lldb) expr zval $var;
(lldb) expr zend_fetch_debug_backtrace(&$var, 0, 0, 0)
(lldb) expr php_var_dump(&$var, 0)
(lldb) expr _zval_ptr_dtor(&$var, 0, 0)

Since you asked, LLDB for PHP5.6 (no-zts):

(lldb) expr zval *$zp = (zval*) malloc(sizeof(zval))
(lldb) expr zend_fetch_debug_backtrace($zp, 0, 0, 0)
(lldb) expr php_var_dump(&$zp, 0)
(lldb) expr _zval_ptr_dtor(&$zp, 0, 0)
(lldb) expr free($zp)
Munday answered 11/10, 2015 at 16:31 Comment(0)
S
1

I played a bit around with this and found out how it works:

echo 'call (void)zif_debug_print_backtrace(0)' | lldb -p $(pgrep -fn php)

For your example of above this will not print anything because there is no backtrace. But when you run a script like this:

php -r "function r(){sleep(1000);}r();"

Then the lldb command will cause the PHP process to output:

#0  r() called at [Command line code:1]

Voila. Unfortunately this also crashes the script.

It does work with gdb, though:

echo 'call zif_debug_print_backtrace(0,0,0,0,0)' | gdb -p $(pgrep -fn php)

when detaching the script continues to run. (tested on Debian with PHP 5.6.14 (DEBUG))

Semicolon answered 9/10, 2015 at 16:1 Comment(7)
Thanks for the response. I've tested and it still kills the original process, so it basically works the same way. Any chance that it won't kill the original process?Worthen
I don't think this can be avoided. PHP is single threaded, so as soon as you want to execute a command externally, you need to stop the script.Semicolon
It works in Ruby: ruby -e 'sleep 1000', and echo 'call (void)rb_backtrace()' | lldb -p $(pgrep -nf ruby).Worthen
Already echo detach | lldb -p $(pgrep -fn php) kills the process.Semicolon
Reported lldb bug in here. My gdb doesn't work on OSX (wrong Mach), I'll check in VM soon.Worthen
Can you explain why we need these zeros?Worthen
tbh it is because of the message "Too few arguments in function call." if I use less arguments. It's also the same number of arguments as here: wikitech.wikimedia.org/wiki/GDB_with_PHPSemicolon

© 2022 - 2024 — McMap. All rights reserved.