callgrind 默认不显示源码行,根本原因是其输出文件仅记录地址偏移,需依赖调试符号(-g)和禁用优化(-o0)配合 qcachegrind 或 callgrind_annotate 解析 dwarf 信息才能还原行号。

Callgrind 为什么看不到源码行?
Callgrind 默认不显示源码行,根本原因是它只记录函数调用和指令计数,不主动关联调试符号。即使程序带 -g 编译,Callgrind 输出的 callgrind.out.xxx 文件里也只存地址偏移,没有行号映射——这得靠后续工具(如 qcachegrind 或 callgrind_annotate)解析时读取 ELF 中的 DWARF 信息才能还原。
必须加 -g 且禁用优化
没有调试符号,qcachegrind 打开 callgrind.out 后只会显示 ???:??? 或函数名+地址,完全看不到 .c/.cpp 行号。关键点:
-
gcc -g -O0是硬性要求;-O1及以上会导致内联、跳转重排,行号映射错乱甚至丢失 - 不要用系统自带的 nginx/httpd 二进制,它们几乎都不带
-g;必须自己从源码编译,比如 Nginx 要加--with-debug - 如果用
cmake,确认CMAKE_BUILD_TYPE是Debug,且未被覆盖为RelWithDebInfo(后者有时会悄悄启用-O2)
qcachegrind 打不开或不显示行号
常见原因不是 Callgrind 本身问题,而是前端工具没读到符号:
- 运行
qcachegrind callgrind.out.12345前,先执行file ./your_program,确认输出含with debug_info字样 - 若用
callgrind_annotate callgrind.out.12345,它只输出函数级统计,不显示行号;要行号必须用图形界面qcachegrind - Linux 下如果提示
Error: can not open cache simulation output file,大概率是权限问题:确保callgrind.out.xxx所在目录可写,且文件非只读(chmod 644 callgrind.out.*) - macOS 用户注意:
brew install qcachegrind安装的是命令行版,需额外装kcachegrind(GUI),或用brew install --cask kcachegrind
Callgrind 启动参数影响源码定位
这些选项直接决定输出是否包含足够上下文:
-
--dump-instr=yes:开启指令级采样(默认关),配合qcachegrind才能点进汇编/源码行;不加这个,只能看到函数粒度 -
--collect-jumps=yes:收集跳转信息,对分析分支预测有用,但和源码行无关,可不加 -
--instr-atstart=no:延迟注入,适合长进程;但首次callgrind_control -i on后才开始记录,此时已过初始化阶段,可能漏掉关键行号 - 避免
--trace-children=yes除非真要追踪 fork 出的子进程;它会让输出膨胀,且子进程若没-g,对应部分就无行号
真正卡住人的地方,往往不是 Callgrind 没跑起来,而是编译时漏了 -g、或者 qcachegrind 加载时找不到调试段——这两个环节缺一不可,中间断掉任何一环,源码行就永远出不来。











