常驻程序需用 SIGUSR2 触发 Callgrind 快照生成报告;Callgrind 是唯一支持运行中采样的 Valgrind 工具,通过 --dump-instr=yes 启用并 kill -USR2 发送信号落盘;callgrind_annotate 用于离线分析,callgrind_control 支持运行时控制。

常驻程序无法退出,Valgrind 默认不生成报告
Valgrind 的 Memcheck 和 Callgrind 默认只在目标进程正常退出(收到 SIGTERM 或自然返回)时才写入最终报告。对 systemd 服务、网络守护进程(如 nginx、redis-server)、或手动用 while true 循环维持的常驻程序,直接运行 valgrind --tool=callgrind ./mydaemon 会导致:报告文件始终为空、进程卡住、或等不到输出就中断。
关键不是“能不能跑”,而是“怎么触发报告落地”。必须主动干预生命周期:
- 给程序发
SIGUSR1(部分工具支持)或SIGTERM—— 但得确保程序能优雅响应并退出,否则 Valgrind 不写报告 - 用
--dump-instr=yes+ 定期发送SIGUSR2触发快照(仅 Callgrind 支持) - 改用
--log-file配合--time-stamp=yes,让日志持续追加,再人工截断分析
Callgrind 快照机制是常驻程序性能分析的核心
Callgrind 是唯一原生支持运行中采样的 Valgrind 工具。它不依赖进程退出,靠信号驱动数据落盘:
- 启动时加
--dump-instr=yes --collect-jumps=yes,启用指令级采集和跳转统计 - 运行中执行
kill -USR2 <pid></pid>,Callgrind 会立即把当前累积数据写入callgrind.out.<pid></pid>(或你指定的--callgrind-out-file) - 可多次发送
SIGUSR2,每次生成独立快照,便于对比不同负载阶段(如空闲/高并发/GC 后) - 注意:
SIGUSR2不会终止进程,但会暂停执行约几十毫秒 —— 生产环境慎用高频触发
示例命令:
valgrind --tool=callgrind --dump-instr=yes --collect-jumps=yes --callgrind-out-file=callgrind.%p ./myserver
之后查进程号 pgrep myserver,再执行 kill -USR2 <pid></pid> 即可生成 callgrind.12345。
callgrind_annotate 和 callgrind_control 的分工要分清
生成的 callgrind.out.* 是二进制格式,不能直接读。常用两个配套工具:
-
callgrind_annotate:离线解析,适合看函数级热点。例如callgrind_annotate --auto=yes callgrind.out.12345输出按指令数排序的函数列表 -
callgrind_control:运行时控制,支持-s(暂停采集)、-r(恢复)、-e(导出当前状态)。对常驻程序更实用,比如callgrind_control -e callgrind.out.12345等价于发SIGUSR2 - 别用
callgrind_annotate实时监控 —— 它不支持流式输入,必须等快照写完才能处理
容易被忽略的三个硬限制
Valgrind 对常驻程序做性能分析时,有三个底层约束常被绕过导致结果失真:
- 所有采集都基于模拟 CPU 执行,开销极大(通常慢 10–50 倍),不能代替 perf 或 eBPF 做线上长期观测
- Callgrind 默认只统计用户态,
--collect-systime=yes才能包含系统调用耗时,但会进一步拖慢速度 - 多线程下,
--separate-threads=yes可为每个线程生成独立计数,但默认关闭;若没开,所有线程数据混在一起,无法定位具体线程瓶颈
真正要盯住长周期服务的热点,建议先用 perf record -g -p <pid></pid> 快速抓帧,再用 Valgrind 在复现环境里深挖可疑函数 —— 它不是监控工具,是手术刀。



















