Linux中输出函数调用栈需区分用户态与内核态:用户态可用backtrace(需-g -rdynamic)、gdb/pstack或perf;内核态用dump_stack或ftrace;strace仅跟踪系统调用,非函数栈。

在 Linux 系统中输出详细的函数调用栈轨迹,需区分是调试用户态应用程序还是内核态代码。两者机制不同,工具链和触发方式也完全不同。
用户态程序:用 backtrace + 符号解析
适用于 C/C++ 程序在运行中主动打印当前线程的调用栈,比如在异常分支、超时或断言失败处插入诊断逻辑:
- 编译时加
-g -rdynamic(-rdynamic确保动态符号表导出函数名) - 在目标位置插入如下代码片段:
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
<p>void print_backtrace(void) {
void *buffer[64];
int nptrs = backtrace(buffer, 64);
char **strings = backtrace_symbols(buffer, nptrs);
if (strings != NULL) {
for (int i = 0; i < nptrs; i++) {
printf("%s\n", strings[i]);
}
free(strings);
}
}</p>- 调用
print_backtrace()即可输出带函数名、偏移和地址的完整栈帧 - 若符号缺失(只显示十六进制地址),检查是否漏了
-rdynamic或链接时 strip 过文件
用户态进程:用 gdb 或 pstack 实时抓取
不修改源码,也能对正在运行的进程快速查看其当前调用栈:
Linux系统管理专家,覆盖12大模块:用户权限、SSH、存储、网络、systemd、防火墙、日志监控、备份恢复、TLS证书、Ansible、容器、IaC。提供配置、验证、加固、监控、备份、自动化、故障排查、回滚闭环。关键词:useradd、sudo、sshd_config、chmod、SEL...
-
gdb -p <PID> -ex "bt" -ex "quit":单行命令输出完整栈,适合脚本集成 -
pstack <PID>:轻量级封装,本质等价于上面的 gdb 调用,无需调试符号也能显示部分帧 -
perf record -g -p <PID> && perf report:不仅看栈,还能统计各函数耗时占比,适合性能瓶颈分析
内核态代码:用 dump_stack 或 ftrace 控制粒度
内核中不能用用户态的 backtrace,但有更底层、更可靠的机制:
- 在驱动或子系统代码中插入
dump_stack();:触发即打印当前 CPU 上下文的完整调用栈,含寄存器状态和函数符号(需 CONFIG_KALLSYMS=y) - 想避免侵入式修改?用
ftrace动态开启函数图谱追踪:
echo function_graph > /sys/kernel/tracing/current_tracer echo vfs_write > /sys/kernel/tracing/set_graph_function echo 1 > /sys/kernel/tracing/tracing_on # 触发目标操作(如 dd 写盘) echo 0 > /sys/kernel/tracing/tracing_on cat /sys/kernel/tracing/trace
- 输出含缩进层级、每层执行时间(单位 us)、函数名和参数示意,比纯 stack trace 更具分析价值
- 全程无 printk 锁竞争,不影响实时性,适合生产环境短时诊断
补充:strace 不是调用栈,但常被混淆
strace 跟踪的是系统调用层面的进出(如 open/read/write),不是函数级调用栈。它能告诉你用户程序“向内核要了什么”,但看不到 libc 内部或内核函数之间的跳转。需要栈信息时,不要依赖 strace。

















