Linux平台下如何不阻塞地跟踪运行中用户进程的实时函数调用
Linux 非阻塞式用户态进程函数调用跟踪方案
1. eBPF 方案(首选,低开销、全量捕获支持)
eBPF 是内核原生提供的 tracing 框架,无需依赖 ptrace,不会将目标进程置为暂停状态,常规使用场景下对目标进程的性能开销低于 5%,生产环境可直接使用。
- 针对特定函数跟踪:使用内核
uprobe(用户态函数探针)+uretprobe(用户态函数返回探针),可以捕获指定函数的每次入栈、出栈操作,支持同时获取调用栈、参数、返回值等信息。
示例(基于 bpftrace 工具,跟踪指定 PID 进程的 libc malloc 调用):bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /pid == 1234/ { printf("PID %d call malloc, size: %d\n", pid, arg0); }' - 针对全函数调用栈抓取:配合
ustack参数可以直接输出完整的用户态调用栈,也可以使用 bcc 工具集的现成工具funccount、functrace直接实现进程全函数调用跟踪。
2. PMU 采样方案(无侵入、低开销,适合热点排查)
利用 CPU 硬件性能监控单元的采样能力,通过 Linux 原生 perf 工具实现无阻塞跟踪,完全不会干扰目标进程的运行。
示例(实时采样指定 PID 的用户态调用栈):
# 实时查看调用栈热点 perf top -p 1234 -g -e cycles:u # 记录10秒采样数据,事后分析全量调用栈 perf record -g -p 1234 -e cycles:u -- sleep 10 && perf script
注意:该方案为采样式实现,无法保证100%捕获每一次函数调用,适合排查性能热点、统计调用占比场景,不适合需要精确记录每次入栈出栈的场景。
3. 用户态插桩方案(灵活度高,适合自定义跟踪逻辑)
如果需要自定义跟踪逻辑,不需要内核权限的场景可以选择该方案:
- 可提前重启目标进程时:使用
LD_PRELOAD注入自定义钩子库,在钩子函数中实现入栈、出栈的日志记录,完全运行在用户态,对目标进程无额外阻塞开销。 - 无法重启目标进程时:使用
dyninst工具集对运行中的进程进行动态指令插桩,仅在插入钩子时会短暂暂停进程一次,后续跟踪过程完全无阻塞,开销可控。
注意事项
- 所有跨进程跟踪方案都需要 root 权限才能访问目标进程的符号表、内存信息。
- 若需要精确捕获每一次函数调用,不要使用非同步的
/proc/pid/stack遍历方案,会拿到不一致的栈数据,精度无法满足要求。
内容的提问来源于stack exchange,提问作者ANTHONY
相关产品推荐
相关产品推荐

