callgrind 可通过 callgrind_start_instrumentation 和 callgrind_stop_instrumentation 宏精准控制采集区间,需包含 头文件、编译加 -g,且仅在 valgrind --tool=callgrind 下生效。

Callgrind 如何只分析指定代码段
Callgrind 默认会全程采集整个程序运行过程的指令和调用信息,但实际调试中你往往只关心某段逻辑(比如一个函数、一次循环或初始化阶段)。它本身不支持“自动跳过前 N 行”,但可通过运行时控制开关实现精准采集。
用 CALLGRIND_START_INSTRUMENTATION 和 CALLGRIND_STOP_INSTRUMENTATION
这是最直接、最可控的方式:在源码中插入宏,让 Callgrind 仅在你标记的区间内记录数据。前提是编译时加 -g 且链接了 Valgrind 的 client request 支持(默认已包含)。
-
#include <valgrind></valgrind>必须显式包含头文件 - 宏只在运行于
valgrind --tool=callgrind下才生效;普通执行时被定义为空,无开销 - 必须成对使用,嵌套不推荐——Callgrind 不维护计数器栈,重复
START会重置状态 - 示例写法:
#include <valgrind>
int main() {
// ... 初始化代码,不采集
CALLGRIND_START_INSTRUMENTATION;
hot_function(); // 这里开始计数
for (int i = 0; i
<h3>避免常见误操作</h3>
<p>很多人试图靠 <code>--instr-atstart=no</code> 配合手动 start,却忘了这个选项只是禁用初始采集,不代表能“中途开启”——它仍需配合 client request 宏才能真正生效。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2741" title="Valgrind 3.23.0"><img
src="https://img.php.cn/upload/manual/001/431/639/6a995defc2965341.png" alt="Valgrind 3.23.0" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2741" title="Valgrind 3.23.0" class="overflowclass">Valgrind 3.23.0</a>
<p class="overflowclass">Valgrind 3.23.0 官方历史源码发布包,适合旧项目兼容、复现历史内存检测结果、排查版本差异和在受支持 Unix-like 系统上自行构建。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2741" title="Valgrind 3.23.0" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<ul>
<li>
<code>--instr-atstart=no</code> 必须与 <code>CALLGRIND_START_INSTRUMENTATION</code> 同时使用,否则全程静默,输出文件为空</li>
<li>漏掉 <code>#include <valgrind></valgrind></code> 会导致编译失败或宏未定义,尤其在 CMake 项目中容易忽略 include 目录</li>
<li>多线程下,这些宏是 per-thread 的,主线程启停不影响子线程——若需子线程也受控,得在对应线程函数内单独插宏</li>
<li>不要在 signal handler 或极短生命周期的栈帧里调用这些宏,可能因上下文切换导致行为异常</li>
</ul>
<h3>输出文件里如何确认只采集了目标段</h3>
<p>生成的 <code>callgrind.out.*</code> 文件体积明显变小,且 <code>callgrind_annotate</code> 输出中,非目标区域的函数调用次数应为 0 或极低(如 <code>main</code> 入口部分只有几条指令计数)。</p>
<ul>
<li>用 <code>callgrind_annotate --inclusive=yes callgrind.out.PID</code> 查看是否含大量 <code>???</code> ——如果有,说明符号缺失,不是采集范围问题,而是编译没加 <code>-g</code>
</li>
<li>如果目标函数完全没出现在报告里,优先检查宏是否被预处理器跳过(比如被 <code>#ifdef DEBUG</code> 包住但编译时未定义)</li>
<li>
<code>kcachegrind</code> 中观察“Call Graph”视图,入口应从你 <code>START</code> 后的第一个函数开始,前面不应有深度调用链</li>
</ul>
<p>真正难的不是插那两行宏,而是判断“哪段才算关键路径”——有时你以为的热点,其实是 IO 等待或锁竞争,Callgrind 记的是指令数,不是 wall-clock 时间。需要结合 <code>--collect-jumps=yes</code> 或 <code>perf</code> 交叉验证。</p></valgrind>










