funclatency:一个函数到底跑多久(延迟分布)
更新时间:2026-09-19。perf 擅长「谁在占 CPU(on-CPU)」,但回答不了「某个具体函数每次调用的耗时分布」。bcc 套件里的
funclatency专门干这个:挂到任意内核或用户态函数,统计其调用延迟并打直方图。自定义一行版本见 bpftrace。
一、基本用法
bash
# 内核函数延迟(默认微秒),持续采样
funclatency vfs_read
# 单位换成毫秒/纳秒
funclatency -m do_nanosleep # -m 毫秒
funclatency -u vfs_read # -u 微秒(默认)
funclatency -n tcp_sendmsg # -n 纳秒
# 每 5 秒输出一轮(间隔刷新)
funclatency -i 5 vfs_read
# 只看某进程
funclatency -p 1234 vfs_read
# 只采 10 次调用后退出(脚本化)
funclatency -r 10 do_sys_open
# 带时间戳、展示总调用数
funclatency -T -i 10 vfs_read输出是 2 的幂次延迟直方图 + 平均/总次数:
bash
nsecs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 120 |************** |
4 -> 7 : 980 |****************************************|
8 -> 15 : 42 |** |
16 -> 31 : 3 | |
avg = 6 us, total: 1145 samples直方图右侧直接给出长尾形状:大多数调用 4~7us,但有没有 16 -> 31 甚至更右的小尾巴,一目了然(尾延迟思想见 P99)。
二、用户态函数(uprobe)
bash
# 追踪用户态程序里的函数(需要符号、未内联)
funclatency -p $(pidof app) -u _ZN6search3queryEv
# 也可直接给二进制路径,工具自动挂 uprobe
funclatency /usr/bin/app:handle_request这让你在不改代码、不重启的前提下,直接拿到线上某个业务函数的延迟分布,对「这个接口偶尔慢」类问题特别有效。被内联/优化掉的函数挂不到,需要带符号的构建或适当优化级别。
三、典型场景
- 接口偶发慢:对处理函数跑 funclatency,确认慢是均匀偏慢还是长尾,长尾对应 IO/锁/调度等待。
- 系统调用层定位:
vfs_read/do_sys_open/fget慢 → 文件系统/FD;tcp_sendmsg/tcp_recvmsg→ 网络栈。 - 锁与调度:函数慢但不在 CPU 上跑时,配合 off-CPU 分析,区分「算得慢」还是「等得久」。
- 优化前后对比:固定负载各跑一轮,对比直方图整体是否左移、长尾桶是否消失,比单点平均更可靠。
四、与 perf / bpftrace 的分工
| 工具 | 回答 | 数据 |
|---|---|---|
| perf record | 时间花在哪些函数(on-CPU 火焰图) | 采样 |
| perf stat | cache-miss/IPC 等硬件计数 | PMU |
| funclatency | 某个函数每次调用的延迟分布 | kretprobe/uprobe 精确计时 |
| bpftrace | 任意自定义问题(含延迟) | 灵活 eBPF 脚本 |
| runqlat | 线程等运行队列的延迟 | 调度器 |
funclatency 是「点」——你已经怀疑某个函数,用它精确验证;不知道热点在哪时先用 perf 找。
坑
- 追的函数被内联或 strip 掉符号,uprobe 挂不上;用带符号构建。
- 单位看错(默认微秒,纳秒用 -n),直方图数量级误判。
- 在高频函数上长时间挂探针有额外开销,定位到后及时停止。
- 只看平均不看最右几个桶,漏掉关键长尾。
- 函数慢包含 off-CPU 等待时间,别误当成纯计算开销。
总结
funclatency 通过 kretprobe/uprobe 精确测量指定内核或用户态函数的每次调用耗时并输出指数桶直方图,用 -u/-m/-n 选单位、-i 定刷新间隔、-p 限定进程、-r 限定采样数,是验证「某函数是否有长尾延迟」的点查利器;它不做全局热点发现(那是 perf record 的职责),而是在怀疑某个函数时直接给出延迟分布和长尾桶,自定义场景再用 bpftrace 扩展。