gprof编译必须加-pg且禁用-o2以上优化,因高阶优化会内联或删除函数调用导致call graph为空;-pg需同时出现在编译和链接阶段;程序正常退出才生成gmon.out;解析时须指定原始可执行文件路径。

gprof 编译时必须加 -pg 且不能开 -O2 以上优化
gprof 依赖编译器在函数入口/出口插入计数桩(instrumentation),而高阶优化(如 -O2、-O3)会内联函数、删除看似无用的调用,导致 main 调用链断裂、__mcount 桩丢失,最终生成的 gmon.out 里只有 flat profile 且所有调用关系(call graph)为空。实测中 -O1 多数情况下可保留调用栈,但稳妥起见建议用 -O0 -pg 编译调试版。
- 正确命令:
g++ -O0 -pg -o myapp main.cpp utils.cpp - 错误命令:
g++ -O2 -pg myapp.cpp→ call graph 几乎全空 -
-pg必须同时出现在编译和链接阶段,只加在链接时无效
运行程序后检查是否生成了 gmon.out
程序退出(包括 exit()、return 或收到 SIGTERM)时,glibc 会把采样数据写入当前工作目录的 gmon.out。如果没生成,常见原因有:
- 程序被
kill -9强杀(SIGKILL 不可捕获,无法触发写入) - 程序 fork 后子进程未正常退出,父进程也未 wait —— 只有终末进程会写
gmon.out - 当前目录无写权限,或磁盘满;可提前用
strace -e trace=openat,write ./myapp 2>&1 | grep gmon确认写入行为 - 多线程程序中,仅主线程退出时写一次;若用
pthread_exit且主线程先退,可能丢数据
用 gprof 解析时要指定可执行文件路径
gprof 默认读取当前目录下的 gmon.out,但它需要符号表才能把地址映射成函数名,所以必须显式传入原始可执行文件(不是 .o 或 .so):
gprof ./myapp gmon.out > profile.txt
常见误操作:
-
gprof ./myapp(没给gmon.out)→ 报错gmon.out: No such file or directory -
gprof ./myapp stripped_binary→ 符号全失,只显示地址,无法识别函数 - 跨目录运行:程序在
/tmp下运行生成/tmp/gmon.out,但你在项目根目录执行gprof ./myapp→ 找不到数据
看懂 flat profile 里的 self 和 children 时间含义
flat profile 第一列是函数总耗时占比,但关键在第二列(self)和第三列(children):
-
self:该函数自身代码执行时间(不含它调用的其他函数) -
children:该函数直接调用的所有函数的self时间之和 - 真正反映“这个函数是否热点”的是
self;若children高但self很低,说明瓶颈不在它,而在它下游(比如std::sort占比高,你该看谁在频繁调它) - 注意:gprof 使用采样(默认每 10ms 中断一次),对短于 1ms 的函数可能完全漏报;高频小函数请改用
perf record -g
call graph 里箭头方向是“被调用”关系(A calls B → A → B),和多数调用图相反,容易看反。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











