gdb 是定位 swoole worker 卡死、rss 暴涨、协程不退出的终极手段,用于抓现场、看栈、查 c 层阻塞点;它不测性能,但能揭示“php 代码未卡而进程却 hang”的根因。

直接上结论:GDB 不是用来“测性能”的,而是当 Swoole Worker 进程卡死、RSS 暴涨、协程不退出时,用来抓现场、看栈、定位 C 层阻塞点的终极手段。它不能替代 xdebug_start_profiling() 或 perf,但能告诉你「为什么 PHP 代码没卡,进程却 hang 住了」。
gdb -p $(pgrep -f "php worker") 为什么总 attach 失败?
常见错误现象:ptrace: Operation not permitted 或 Permission denied,尤其在容器或 systemd 环境下。
- 根本原因是内核安全策略限制了非 root 进程对其他进程的 ptrace 能力,不是权限没加 sudo
- 检查
/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] - systemd 服务要加
SecureBits=keep-caps和CapabilityBoundingSet=CAP_SYS_PTRACE,否则sudo gdb -p也无效
bt 和 zbacktrace 输出一堆 ??,怎么读?
这是符号缺失的典型表现 —— GDB 找不到 PHP 或 Swoole 的调试符号,导致调用栈显示为 ?? 或地址乱码。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- PHP 必须用源码编译安装,并带
--enable-debug和--without-opcache(Opcache 会干扰栈帧) - Swoole 扩展需用
phpize && ./configure --enable-debug重新编译,不能只装 pecl 版本 - 确认
gdb能加载.gdbinit:启动后执行source /path/to/php-src/.gdbinit,再输zbacktrace才有 PHP 函数名 - 若仍显示
swServer_master_onAccept这类 C 函数但无参数,说明符号表存在但局部变量被优化掉了 —— 编译 PHP 时务必加-O0,禁用所有优化
如何用 info proc mappings 快速判断内存泄漏源头?
这个命令不看代码,只看进程实际占用了哪些可写可执行内存页,是识别扩展层泄漏(如未释放的 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 未 free - 对比两次采样:先记下当前
rwx段数量和大小,等 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 代码。










