axbacktrace
路径:
components/axbacktrace类型:库 crate 分层:组件层 / 调试与回溯基础件 版本:0.1.2文档依据:Cargo.toml、src/lib.rs、src/dwarf.rs
axbacktrace 为 ArceOS 体系提供栈回溯能力。它负责从当前上下文或 trap 上下文收集栈帧,并在启用 dwarf 时利用调试段做符号化输出。它属于调试叶子基础件:既不是 panic 处理器,也不是异常分发框架,更不是通用 DWARF 装载服务。
架构设计
设计定位
axbacktrace 的职责非常聚焦:
- 向下,它依赖架构相关的帧指针读取和可选的 DWARF 调试段。
- 向上,它向
ax-runtime的 panic 路径、ax-alloc的 tracking 路径、ax-cpu的 trap context 提供统一的Backtrace对象。 - 横向,它把“捕获栈帧”和“解析符号”拆成两个层次:不开
dwarf仍可存在Backtrace,但会退化为不可解析或禁用状态。
所以 axbacktrace 解决的是“如何得到并展示回溯”,而不是“何时触发 panic”“如何恢复异常”。
模块结构
src/lib.rs:回溯入口、栈展开逻辑、Backtrace状态机与架构相关帧指针读取。src/dwarf.rs:dwarffeature 下的符号化支持,负责从链接器暴露的.debug_*段初始化addr2line::Context。
1.3 关键对象
Frame:一帧的原始表示,包含fp与ip。Backtrace:对外暴露的回溯对象。Inner:Backtrace内部状态,区分Unsupported、Disabled与Captured(Vec<Frame>)。FrameIter:只在dwarf下可用的符号化帧迭代器。IP_RANGE/FP_RANGE:初始化后保存合法代码区和合法帧指针区间。MAX_DEPTH:限制展开深度,默认32。
1.4 栈展开主线
常规捕获路径如下:
源码里有几个重要细节:
Frame::OFFSET会因架构不同而变化,x86_64/aarch64为0,其他架构为1。unwind_stack()必须先看到FP_RANGE,否则不会 panic,而是记日志并返回空向量。- 为避免坏栈或损坏的帧指针无限追链,代码同时检查最大深度和“单步跳跃是否超过 8 MiB”。
1.5 trap 回溯与 DWARF 解析
capture_trap(fp, ip, ra):给 trap/异常上下文使用。ax-cpu在x86_64、aarch64、riscv、loongarch64的 trap context 中都直接调用它。dwarf::init():通过__start_debug_*/__stop_debug_*链接符号抓取 DWARF 段,并建立addr2line::Context。frames():只在dwarf打开时可用,返回的是“原始帧 + 解析后符号帧”的组合迭代器。
如果不开 dwarf,Backtrace::capture() 仍然可以返回 Backtrace,但其内部状态会是 Disabled,不会进行符号化。
核心功能
功能概览
- 初始化可回溯的指令区/帧区。
- 从当前执行流捕获栈回溯。
- 从 trap 上下文捕获栈回溯。
- 在开启
dwarf时把原始栈帧解析成函数名和源码位置。
使用场景
init(ip_range, fp_range):由ax-runtime/src/lib.rs在启动期调用。Backtrace::capture():被ax-runtime/src/lang_items.rs的 panic 输出路径和ax-alloc/src/tracking.rs的分配跟踪路径直接调用。Backtrace::capture_trap():被components/axcpu/src/*/context.rs的 trap context 直接调用。frames():适合更精细的调试输出场景,但必须依赖dwarffeature。
边界说明
axbacktrace不负责决定何时打印回溯;调用时机在ax-runtime、ax-cpu或其他上层模块。- 它依赖帧指针链可用。若编译配置或架构路径不保留帧链,结果会受影响。
- 它不会替你解决符号段装载问题;
dwarf相关段必须真实存在于最终镜像里。
依赖关系
直接依赖
spin:保存一次初始化的地址范围与 DWARF 上下文。log:初始化失败或未初始化时输出错误信息。addr2line、gimli、paste:只在dwarf下启用。
主要消费者
ax-runtime:panic 和启动期初始化。ax-alloc:分配跟踪记录回溯。ax-cpu:trap 上下文回溯入口。starry-kernel:通过共享基础栈复用回溯能力。
开发指南
接入方式
[dependencies]
axbacktrace = { workspace = true, features = ["dwarf"] }
如果只需要保持接口存在而不做符号化,可不打开 dwarf。
Host 离线符号化(Issue #146)
Target 可通过 Backtrace::capture().kind(kind) 输出机器可解析的 raw 块(BACKTRACE_BEGIN / BT / BACKTRACE_END),在 host 上用 cargo xtask backtrace symbolize 对日志与 ELF 做符号化。用法与后续「测试结束后自动 symbolize」计划见 Backtrace Host 符号化(PR #635 / #646)。
注意事项
- 修改架构相关帧指针读取逻辑时,必须同步检查
capture()和capture_trap()两条路径。 - 若调整
Frame::OFFSET或adjust_ip()语义,需要一起核对addr2line解析结果是否仍正确。 - 若扩展
dwarf::init()支持的段集合,应确认链接脚本确实暴露了相应__start_*/__stop_*符号。 - 不要把 panic、异常恢复或日志策略直接塞进
axbacktrace;这层只负责回溯对象本身。
4.3 开发建议
- 新增架构支持时,先确认该架构 trap context 能提供
fp/ip/ra的合理来源。 - 需要更长回溯时优先调整
set_max_depth(),不要直接去掉边界检查。 - 如果只做 memtrack,不必强制依赖
frames();原始Backtrace已足够做归档和打印。
测试
测试覆盖
axbacktrace 本体没有独立单元测试,当前验证主要依赖真实运行路径:
ax-runtime的 panic 输出;ax-cpu的 trap backtrace;ax-alloctracking 对Backtrace::capture()的调用。
单元测试
Frame::read()与adjust_ip()的边界行为。MAX_DEPTH限制和 8 MiB 防护。capture_trap()在首帧ip不落在IP_RANGE时回退到ra的分支。
集成测试
- 打开
dwarf后 panic 输出能解析出函数名和源码位置。 - trap 路径下四种已支持架构都能产出合理回溯。
- tracking 模式下大量分配不会因回溯记录导致递归崩溃。
覆盖率
- 对
axbacktrace,系统级可用性比局部行覆盖率更重要。 - 涉及新架构或
dwarf初始化逻辑的改动,都应至少覆盖一条 panic 路径和一条 trap 路径。