如何使用 gdb 在 php 脚本中设置断点

How to set breakpoints in a php script using gdb

我正在尝试使用 GDB 通过控制台调试 php 脚本,但我无法设置断点。这就是我所做的。 我创建了一个包含以下内容的脚本:

<?php
echo "1";
echo "1";
echo "1";
echo "1";
echo "1";
echo "1";
echo "1";

这是我调试它的诱惑

# gdb php -d CANCELLAMI.php 
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...

warning: /root/CANCELLAMI.php is not a directory.
Reading symbols from /usr/bin/php...(no debugging symbols found)...done.

现在我在 gdb 中,我在第 2 行放置了一个断点。

(gdb) break CANCELLAMI.php:2
No symbol table is loaded.  Use the "file" command.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (CANCELLAMI.php:2) pending.

但是如果我 运行 CANCELLAMI 脚本

(gdb) run CANCELLAMI.php 
Starting program: /usr/bin/php CANCELLAMI.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
11111111[Inferior 1 (process 30216) exited normally]

整个脚本被执行。

简短回答:如果您想调试 PHP 脚本,请使用 xdebug


目前,gdb 只能真正调试编译语言。它有很多关于可执行文件格式、调试信息格式、如何展开堆栈帧以及诸如此类的低级内容的知识。它没有的是一种将这些东西与解释器中的更高级别构造相关联的方法。

现在,如果您对解释器有足够的了解,就可以通过这种方式调试脚本。您可以单步执行解释器并了解它在做什么。我以前做过这个——这是可行的,但并不完全令人愉快。这已经够难了,只有当你试图在解释器中找到由某个特定脚本触发的错误时,它才真正值得做。

偶尔会出现 gdb 可以调试脚本的想法。这是一个好主意,但需要相当大的工作量。据我所知,目前没有人在研究它。

您可以相对轻松地从 PHP 触发断点:

  1. configure --enable-debug编译PHP。
  2. 找一个方便的 PHP 脚本不调用的函数。比如,hebrevc(或 quotemetaspl_autoload_extensions——要有创意,PHP 有很多功能)。在要触发的位置添加调用。
  3. 接下来,启动 gdb 并在 zif_hebrevc 上设置断点。如果你阅读 PHP 来源,你会发现
    1. PHP_FUNCTION(hebrevc)
    2. #define PHP_FUNCTION ZEND_FUNCTION
    3. define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
    4. #define ZEND_FN(name) zif_##name
    5. #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)

所有这些导致 zif_hebrevc(或 zif_quotemetazif_spl_autoload_extensions)。

您可能需要 source /path/to/the/php/source/checkout/.gdbinit 这也是可选的,但没有它很难使用 PHP。此文件为您提供 zbacktraceprintzvprint_ht 等命令。此 .gdbinit 的最佳文档是 source.

最后,http://lxr.php.net/ 是你浏览 PHP 来源的朋友。

如果您想了解类型转换的工作原理,您可以在 execute_ex() 函数处设置断点。它在操作后顺序执行操作。以下 gdb 场次:

(gdb) n
54056  HYBRID_SWITCH() {
(gdb)
54383  ZEND_INIT_FCALL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
(gdb)
54384  HYBRID_BREAK();
(gdb)
54524  ZEND_SEND_VAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
(gdb)
54525  HYBRID_BREAK();
(gdb)
54243  ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
(gdb)
54244  HYBRID_BREAK();
(gdb)
54415  ZEND_ECHO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);

对应以下脚本:

<?php sleep(1); echo 0.1 + 0.2;

诀窍是适时介入。