GDB 是定位 Swoole Worker 卡死、RSS 暴涨、协程不退出的终极手段,用于抓现场、看栈、查 C 层阻塞点;它不测性能,但能揭示“PHP 代码未卡而进程却 hang”的根因。
直接上结论:GDB 不是用来“测性能”的,而是当 Swoole Worker 进程卡死、RSS 暴涨、协程不退出时,用来抓现场、看栈、定位 C 层阻塞点的终极手段。它不能替代 xdebug_start_profiling() 或 perf,但能告诉你「为什么 PHP 代码没卡,进程却 hang 住了」。
常见错误现象:ptrace: Operation not permitted 或 Permission denied,尤其在容器或 systemd 环境下。
/proc/sys/kernel/yama/ptrace_scope:值为 1(默认)会拒绝非子进程 attach;临时放开用 echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
--cap-add=SYS_PTRACE 启动,Docker Compose 则加 cap_add: [SYS_PTRACE]
SecureBits=keep-caps 和 CapabilityBoundingSet=CAP_SYS_PTRACE,否则 sudo gdb -p 也无效这是符号缺失的典型表现 —— GDB 找不到 PHP 或 Swoole 的调试符号,导致调用栈显示为 ?? 或地址乱码。
--enable-debug 和 --without-opcache(Opcache 会干扰栈帧)phpize && ./configure --enable-debug 重新编译,不能只装 pecl 版本gdb 能加载 .gdbinit:启动后执行 source /path/to/php-src/.gdbinit,再输 zbacktrace 才有 PHP 函数名swServer_master_onAccept 这类 C 函数但无参数,说明符号表存在但局部变量被优化掉了 —— 编译 PHP 时务必加 -O0,禁用所有优化这个命令不看代码,只看进程实际占用了哪些可写可执行内存页,是识别扩展层泄漏(如未释放的 swString、zval)最直接的方式。
gdb -p $(pgrep -f "php worker") -ex "info proc mappings" -ex "quit"
rwx 权限段:grep rwx,正常 PHP 进程只有 1–2 段(如 JIT 缓存、libpthread);若出现 5+ 段且地址连续增长,大概率是 Swoole 扩展反复 malloc 未 freerwx 段数量和大小,等 RSS 上升 50MB 后再跑一次,新增的段起始地址就是泄漏发生位置dump memory 提取该段:dump memory /tmp/leak.bin 0x7fabc0000000 0x7fabc0100000,再交给 php-meminfo 或 pstack 分析内容真正难的不是命令怎么敲,而是看到 bt 里满屏 swReactorEpoll_wait 时,得意识到问题不在 PHP 层 —— 那说明事件循环卡死了,要立刻查是否有人在协程里调用了同步阻塞函数(比如 sleep()、file_get_contents()、未 hook 的 curl_exec()),而不是继续翻 PHP 代码。