可实现毫秒级快照式采集,对高并发Worker影响极小;单条命令自动获取全线程堆栈快照,不暂停、不修改寄存器,适用于卡顿、协程堆积等紧急排查。

直接 attach 正在运行的 Worker 进程做非阻塞堆栈分析,核心在于「不中断服务、不改变线程调度、只读取状态」。GDB 本身默认会暂停目标进程,但通过合理组合命令与参数,可实现毫秒级快照式采集,对高并发 Worker(如 Swoole、Nginx 或自研 C/C++ 服务)影响极小。
快速获取全线程堆栈快照(推荐一线运维用)
适用于紧急排查卡顿、协程堆积、RSS 异常增长等场景,全程无交互、不暂停、不修改寄存器:
-
单条命令一键采集:
gdb -p $(pgrep -f "php worker" | head -n1) -ex "thread apply all bt full" -ex "quit" 2>/dev/null | grep -E "^\#|^\$[0-9]" -
说明:自动找一个匹配的 Worker PID,执行全线程回溯(
bt full包含参数和局部变量),立即退出;2>/dev/null屏蔽 GDB 启动提示,grep提取有效帧信息 - 优势:整个过程通常
定位可疑线程并检查其上下文(开发/深度排查用)
当发现某线程长时间处于特定函数(如 poll、epoll_wait、curl_easy_perform)时,需进入该线程查看变量与调用链:
-
步骤分解:
•
gdb -p $PID进入交互模式
•info threads列出所有 LWP(轻量级进程)及状态(running / sleeping / blocked)
•thread <lwp_id></lwp_id>切换到目标线程(例如thread 423826)
•bt查看当前线程完整调用栈
•frame 2跳转到第 3 层栈帧(如进入业务回调函数)
•print $rdi或print (char*)buf检查关键参数或缓冲区地址(需符号表支持) -
注意:若 Worker 使用了协程(如 Swoole 的 ucontext 或 boost::context),
bt显示的是底层调度栈而非 PHP 协程栈,此时需配合info registers查rbp/rsp推断协程栈基址,或改用php-meminfo+dump memory分析
避免干扰业务的调试技巧
GDB attach 默认冻结整个进程,但可通过以下方式最小化影响:
-
禁用调度锁:进入 GDB 后立即执行
set scheduler-locking off,确保其他线程继续运行,仅当前调试线程受控 -
跳过信号中断:添加
-ex "handle SIGUSR1 nostop noprint pass"等,防止业务自定义信号被 GDB 拦截 -
不设断点、不单步、不修改内存:仅用
bt、info proc mappings、info registers等只读命令——这些操作由ptrace(PTRACE_GETREGS)和ptrace(PTRACE_PEEKTEXT)完成,内核开销极低 -
验证是否真“非阻塞”:执行前后对比
cat /proc/$PID/stat | awk '{print $30}'(utime)和$31(stime),若增长值 ≈ 命令耗时,则未造成额外 CPU 阻塞
配合其他工具交叉验证
单靠 GDB 堆栈有时难以判断内存或资源泄漏,建议联动使用:
-
gdb -p $PID -ex "info proc mappings" -ex "quit" | grep rwx—— 查看可执行内存页数量是否异常增多(可能有 JIT 或动态代码注入) -
lsof -p $PID | wc -l与cat /proc/$PID/status | grep Threads对比,若句柄数远超线程数,大概率存在 fd 泄漏(如 stream_socket_client 未 close) -
cat /proc/$PID/status | grep -E "VmRSS|VmSize|Threads"结合多次采样,确认 RSS 增长是否与某次bt中高频出现的函数调用次数正相关


















