perf record -g 默认使用 frame pointer 模式,但现代内核(5.10+)普遍关闭 config_frame_pointer,导致内核态调用栈截断、大量显示[unknown];arm64 更默认不支持 fp,必须显式指定 --call-graph dwarf 才能完整还原内核函数调用链。

perf record -g 采集内核态调用栈必须加 --call-graph dwarf
默认 perf record -g 在 x86_64 上用 frame pointer 模式,但现代内核(5.10+)普遍开启 CONFIG_FRAME_POINTER=n,导致内核函数调用栈截断、火焰图顶部大量显示 [unknown]。ARM64 更是默认不支持 fp 模式。
解决办法是强制使用 DWARF 解析:
-
sudo perf record -a -g --call-graph dwarf,8192 -F 99—— 全系统采样,8KB 栈深度,99Hz 频率 -
sudo perf record -p <pid> -g --call-graph dwarf,4096</pid>—— 只抓指定进程,栈深设为 4KB 防止溢出 - 注意:DWARF 模式开销略高,但能完整还原内核函数调用链,比如从
sys_read→ksys_read→__vfs_read→ext4_file_read_iter
perf report 显示内核符号需确保 /proc/kallsyms 可读且有调试信息
perf report 默认无法解析内核函数名,会显示成 0xffffffffXXXXXXX 地址,因为缺少符号映射。
检查并修复步骤:
- 确认内核符号表可读:
cat /proc/kallsyms | head -n3,若报Permission denied,执行echo 0 | sudo tee /proc/sys/kernel/kptr_restrict - 安装对应内核的 debuginfo 包(Ubuntu/Debian):
sudo apt install linux-image-$(uname -r)-dbgsym;CentOS/RHEL:debuginfo-install kernel-$(uname -r) - 验证是否生效:
perf report --symbols | grep ext4,应能看到具体函数名而非地址
区分用户态 vs 内核态热点:用 perf script + grep 过滤关键路径
原始 perf script 输出混杂用户态和内核态调用,直接看 report 容易忽略内核侧瓶颈。例如磁盘延迟高,真正耗时可能在 blk_mq_dispatch_rq_list 或 scsi_queue_rq,而非上层应用代码。
ApiPost是一个支持团队协作,支持模拟POST、GET、PUT等常见请求,并可直接生成文档的API调试、管理工具,ApiPost是后台接口开发者或前端、接口测试人员的工作必备工具。快速生成、一键导出API文档。感兴趣的朋友快来下载吧。软件说明ApiPost官方版是一款十分出色的接口调试与文档生成工具,ApiPost官方版界面美观大方,功能强劲实用,支持团队协作,支持模拟POST、GET、PUT等常见请求,是后台接口开发者或前端、接口测试人员的工作必备工具。软件特色更方便支持接口调试的同时快速生成、一键
快速定位内核函数热点的方法:
- 提取纯内核栈:
perf script | awk -F';' '$1 ~ /^\[kernel\]/ {print $0}' | sort | uniq -c | sort -nr | head -10 - 聚焦 I/O 相关内核函数:
perf script | grep -E "(ext4|bio|blk|scsi|nvme)" | head -20 - 统计某内核函数被调用次数:
perf script | cut -d';' -f1-3 | grep "ext4_writepages" | wc -l
火焰图中内核函数名显示为 [k]xxx 是正常现象,但 [unknown] 就有问题
生成火焰图后,内核函数统一以 [k]xxx 前缀标识(如 [k]do_syscall_64),这是预期行为;但若大量出现 [unknown] 或 0xffffffffXXXX,说明符号解析失败或调用栈未捕获。
常见原因与对策:
- DWARF 未启用或栈深不足 → 改用
--call-graph dwarf,8192并确认内核编译含CONFIG_DEBUG_INFO_DWARF4=y - 容器环境未挂载
/proc/kallsyms或权限受限 → 进入容器 namespace 后运行sudo perf record,或用 host PID 模式采集 - 内核模块无调试符号 → 单独安装对应模块的
-dbg或-debuginfo包,或重新编译模块时加-g
内核函数调用链的完整性,比用户态更依赖底层配置一致性;一个 [unknown] 往往意味着你漏掉了某个符号环节,而不是数据本身没采到。










