Как сбросить backtrace PHP в режиме реального времени script в lldb?

Я играю с LLDB (отладчик), и я сделал следующий эксперимент.

  • Запустите PHP script как:

    php -r "sleep(1000);"
    

    или

    php -r "function r(){sleep(1000);}r();"
    
  • На другой консоли я вызывал непосредственно zif_debug_backtrace() из lldb:

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

Выше работал, однако процесс остановился со следующим предупреждением:

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

Я не совсем уверен, почему script должен был остановиться и что мне нужно сделать для достижения прозрачности (без влияния на script)?

P.S. То же самое происходит при вызове zif_debug_print_backtrace(), а при вызове custom_backtrace() отображается: Backtrace null function called. Я использую xdebug, если это что-то изменит.

Возможно, мне нужно вызвать другую функцию, например zend_fetch_debug_backtrace (см. image dump symtab)? Или используйте правильные аргументы, если да, то какой?

Меня интересуют только решения lldb/gdb, чтобы распечатать обратную трассировку.


Аналогичный подход работает в Ruby, например:

  • Выполнить: ruby -e 'sleep 1000'.
  • В другом терминале: echo 'call (void)rb_backtrace()' | lldb -p $(pgrep -nf ruby).

Ответ 1

Вы не можете вызывать внутренние функции, подобные этим, внутренние функции ожидают такие вещи, как фрейм, возвращаемое значение и т.д.... не делайте этого.

Существует .gdbinit, распространяемый с php, в нем есть функция с именем zbacktrace, вы можете перенести это на lldb.

Еще одна вещь, которую вы могли бы сделать, что, скорее всего, проще, - это просто вызвать функцию API, которая генерирует трассировку, но правильно ее назовите.

Вот он для 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 

И для 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)

Поскольку вы спросили, LLDB для 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)

Ответ 2

Я немного поиграл с этим и узнал, как он работает:

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

Для вашего примера выше это ничего не печатает, потому что нет обратного трассировки. Но когда вы запускаете script следующим образом:

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

Затем команда lldb выведет процесс PHP:

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

Voila. К сожалению, это также приводит к сбою script.

Он работает с gdb, хотя:

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

при отсоединении script. (тестируется на Debian с PHP 5.6.14 (DEBUG))