gprof 编译必须加 -pg 且避免 -O2 以上优化,否则因函数内联导致统计失真;需 -O0 -pg 编译、链接也带 -pg,运行后生成 gmon.out 并与可执行文件同目录使用。
-pg 且不能用 -O2 以上优化gprof 依赖编译器在函数入口/出口插入计数桩(instrumentation),而高阶优化(如 -O2、-O3)会内联函数、删除看似无用的调用,导致 gprof 统计失真甚至完全漏掉热点。实测中,-O1 通常可接受,但最稳妥的是 -O0 -pg。
g++ -O0 -pg -o myapp main.cpp utils.cpp —— 正确:关闭优化,启用桩点g++ -O2 -pg -o myapp main.cpp —— 危险:gprof 报告中大量函数调用次数为 0,% time 分布异常-pg,否则动态链接库中的函数不会被采样gmon.out,必须在同一目录下执行 gprof
程序退出(非 crash 或 exit(0) 之外的强制终止)后,会在**当前工作目录**生成 gmon.out。这个文件是二进制格式,不可编辑,且与可执行文件强绑定——换路径、重命名或重新编译都会让 gprof 解析失败。
./myapp gprof myapp gmon.out > profile.txt
gprof ./build/myapp gmon.out —— 若 myapp 不在当前目录,gprof 找不到符号表,报错 not in a.out format
gmon.out;子进程需单独处理(gprof 默认不支持多进程聚合)flat profile 和 call graph 的关键字段flat profile 告诉你「哪个函数耗时最多」,call graph 揭示「谁调用了谁、调用频次和传播开销」。二者结合才能准确定位瓶颈。
% time:该函数自身执行时间占总采样时间的百分比(不含子调用)self seconds:函数纯开销,可用于横向对比不同函数calls(在 call graph 中):实际调用次数,注意区分 self(本函数直接调用)和 children(子函数调用)main 占比过高却无明细:说明热点在 main 内联循环或未分离逻辑,应拆出独立函数再分析std::function 和信号处理gprof 对现代 C++ 构建方式支持有限,不是所有函数都能被准确追踪。
立即学习“C++免费学习笔记(深入)”;
.a)若未用 -pg 编译,其内部函数不会出现在 flat profile 中,只显示为“未知调用者”std::vector::push_back )可能被折叠或符号名过长,gprof 显示为 ??? 或截断名,需配合 c++filt 解码:echo "_ZSt4copyIPiS0_EET0_T_S2_S1_" | c++filt
std::function 回调、lambda 捕获、信号处理函数(signal() 注册的 handler)因跳转非标准,通常无法被 gprof 捕获perf 或 valgrind --tool=callgrind
gmon.out 是否存在、是否和可执行文件匹配、是否所有 .o 都用 -pg 编译——这些细节比调参更关键。