begin/end探针无上下文,故ustack/kstack为空;interval探针也因无进程上下文导致栈不可靠;profile:hz:99基于内核中断,可稳定采样栈。

为什么 BEGIN / END 探针里调用 ustack 或 kstack 总是空?
因为 BEGIN 和 END 是纯用户态探针,不关联任何内核上下文或进程上下文,ustack 和 kstack 依赖当前运行线程的寄存器/栈指针状态,而这两个探针触发时根本没“当前线程”可言——它们只是 bpftrace 启动/退出时的钩子。
常见错误现象:ustack 返回 (empty)、kstack 报错 invalid stack id,或者输出全是 0x0 地址。
- 正确做法:只在进程上下文相关的探针里用栈函数,比如
uprobe、usdt、tracepoint:syscalls:sys_enter_*、kprobe -
interval:s:1这类定时探针也不带上下文,同样不能直接调ustack;如果真要周期性采样用户栈,得配合pid过滤 +uprobe注入点,或改用profile:hz:99 - 想看启动/退出时的全局状态?用
printf打印环境变量、pid、comm等静态信息更靠谱
interval:s:1 触发时,kstack 为什么有时解析不出符号?
interval 探针基于 perf event 的 PERF_COUNT_SW_BPF_OUTPUT 或 perf_event_open 定时采样,但内核栈符号解析依赖 /proc/kallsyms 和 vmlinux 路径配置。bpftrace 默认只查系统默认路径,很多发行版(如 Ubuntu/Debian)把 vmlinux 放在 /usr/lib/debug/boot/vmlinux-$(uname -r),而 bpftrace 不自动找。
- 检查是否加载了符号:运行
bpftrace -v -e 'kprobe:do_sys_open { kstack; }',看输出是否有函数名;若全是地址,说明符号未加载 - 手动指定 vmlinux:加
-v --vmlinux /path/to/vmlinux,注意路径必须存在且有读权限 -
/proc/kallsyms需要 root 权限读取,非 root 用户下kstack会退化为地址列表;sudo bpftrace是硬性要求 - 某些云环境(如 AWS Nitro)或容器中可能禁用
kallsyms,此时kstack必然失效,只能靠ksym查单个地址
ustack 解析失败的三个典型场景和对应 fix
ustack 失败不是“没符号”那么简单,它卡在用户栈帧遍历、DWARF 信息读取、ELF 加载路径等多个环节。
- 目标进程没开 debuginfo:Ubuntu/Debian 需装
libc6-dbg,CentOS/RHEL 装glibc-debuginfo;否则ustack只能走到 libc 入口就停住 - 进程是 stripped 的二进制:
readelf -S ./a.out | grep debug看有没有.debug_*段;没有就只能靠sym查 PLT 符号,或重编译加-g - Java/Go 等运行时栈不兼容 libdw:bpftrace 的
ustack依赖 libdw 解析 DWARF,而 Go 1.20+ 默认关 DWARF,Java 需 JVM 参数-XX:+PreserveFramePointer+-XX:+UnlockDiagnosticVMOptions,否则栈帧链断裂
profile:hz:99 比 interval 更适合栈采样?为什么
是的。profile:hz:99 基于 perf 的 PERF_TYPE_HARDWARE 事件(如 PERF_COUNT_HW_CPU_CYCLES),由内核在中断上下文中触发,天然携带当前 CPU 上正在运行的 task_struct,因此 ustack/kstack 能稳定获取上下文。
-
interval:s:1是用户态定时器驱动,执行时可能在任意进程上下文(甚至无进程),栈不可靠 -
profile:hz:99每秒约 99 次中断采样,频率可控,且支持pid、comm、cpu等过滤,更适合性能画像 - 注意:profile 采样开销略高,高频 profile(如 hz:1000)可能影响被测程序,尤其在低配机器上;99 是经验平衡值
- 示例:
bpftrace -e 'profile:hz:99 /pid == 1234/ { @[ustack] = count(); }'—— 这才是安全的用户栈聚合方式
真正麻烦的是混合栈:当 ustack 遇到内核态切换(比如系统调用返回路径),帧之间 ABI 不一致,libdw 容易误判栈顶。这时候别硬刚符号,先用 ustack(10) 限制深度,再结合 printf 打印 retval 和 arg0 辅助定位。










