perf top不能显示业务代码行号,仅实时显示cpu占用最高的函数名;要显示如main.c:42等行号,必须同时满足编译带-g、未strip符号、perf可访问符号表三个条件,否则需改用perf record+火焰图或async-profiler。

perf top 不能显示业务代码行号,只能实时看到占用 CPU 周期最多的函数名(含内核函数和用户态函数),是否带行号取决于符号信息是否可用、是否启用调试信息、以及采样上下文。 它是快速扫描“谁在吃 CPU”的第一工具,不是源码级定位器。
perf top 实时看函数级热点
运行 sudo perf top -F 99 -ag 即可启动实时视图:
- -F 99:设为每秒采样 99 次,平衡精度与开销;过高易扰动系统,过低可能漏掉短时热点
- -a:采集全系统事件(含内核线程、中断处理等),便于发现内核侧瓶颈(如 softirq、ksoftirqd)
-
-g:开启调用图,能显示函数调用链(例如
sys_write → vfs_write → ext4_file_write_iter),但需内核开启 CONFIG_FRAME_POINTER 或使用 DWARF
界面中按 ↑/↓ 移动,Enter 可下钻到选中函数的调用者/被调用者;按 q 退出。重点关注 “Overhead” 列占比高的项——它代表该函数(及其子调用)占用了多少采样周期。
能否看到业务代码行?关键看三件事
perf top 默认只显示函数名(如 json_parse、malloc),要出现 MyService.java:127 或 main.c:42 这类行号,必须同时满足:
- 目标程序编译时带调试信息:
gcc -g或javac -g;Java 类还需未被 ProGuard 等 strip - 运行时未 strip 符号:C/C++/Go 二进制不能执行
strip;Java 需确保 classpath 中的 JAR 含调试信息 - perf 能访问符号表:容器中需挂载
/proc/sys/kernel/perf_event_paranoid允许用户态采样(建议临时设为-1)
不满足任一条件,你只会看到 [unknown]、__libc_start_main 或地址(如 0x000055e... ),此时应改用 perf record + 火焰图 方式补足行号信息。
区分内核函数 vs 业务代码的识别技巧
在 perf top 输出中快速判断来源:
-
内核函数:名称以
do_、sys_、ext4_、tcp_、ksoftirqd、swapper开头;或出现在[kernel.kallsyms]、[vdso]、[kmod]模块列 -
用户态业务函数:名称为你自己的函数名(如
handle_request)、第三方库函数(如memcpy@plt、json_tokener_parse),模块列为你的二进制名(如./myserver)或libc-2.31.so -
Java 特殊情况:perf top 通常只显示 JIT 编译后的函数名(如
java_lang_String::hashCode),几乎不显示 .java 行号;此时应切换至async-profiler获取真实源码位置
比 perf top 更准的替代方案(需要行号时)
当 perf top 显示高开销但看不到具体行号,或想确认是否为瞬时尖峰时,推荐立即切到录制模式:
- 对 C/C++/Go:运行
sudo perf record -g -F 99 -p $PID -- sleep 20,再用perf script | stackcollapse-perf.pl | flamegraph.pl > cpu.svg生成火焰图 - 对 Java:跳过 perf,直接用
./profiler.sh -e cpu -d 20 -f profile.svg $PID(async-profiler),它专为 JVM 设计,支持行号且无需修改启动参数 - 若怀疑是内核路径(如磁盘 IO 导致软中断飙升),加
-e irq:softirq_entry或-e syscalls:sys_enter_write限定事件,减少噪声











