8 个版本 (4 个破坏性更新)

0.5.0 2023 年 8 月 21 日
0.4.0 2023 年 4 月 7 日
0.3.0 2022 年 6 月 28 日
0.2.0 2022 年 5 月 3 日
0.1.1 2022 年 2 月 24 日

#6 in #capturing

每月 24 次下载

MIT/Apache

51KB
767 代码行

Stackdump Capture

crates.io Documentation

此 Crate 为编译平台定义了堆栈转储捕获函数。

另请参阅 主仓库的 README 文件

在 build.rs 文件中自动执行平台检测。这仅有助于捕获堆栈和寄存器。如果您想捕获堆栈或任何静态数据,则必须自行完成。

Crate 是基于 stackdump-core Crate 构建的。要获取堆栈跟踪,请将捕获的数据输入到 stackdump-trace Crate。

Cortex m

Cortex m 捕获有两种变体

  • 无 FPU
    • 仅捕获并返回核心寄存器
  • 带 FPU
    • 同时捕获并返回 FPU 寄存器

如果编译目标支持,则自动捕获 FPU 寄存器。这会改变捕获返回类型。

示例

带 FPU

use stackdump_capture::core::memory_region::ArrayMemoryRegion;
use stackdump_core::register_data::ArrayRegisterData;

let mut stack_capture = ArrayMemoryRegion::default();
let mut core_registers = ArrayRegisterData::default();
let mut fpu_registers = ArrayRegisterData::default();


stackdump_capture::cortex_m::capture(&mut stack_capture, &mut core_registers, &mut fpu_registers);

用于崩溃时(以 cortex m 作为目标示例)

当发生崩溃时,您可能想要进行堆栈转储,以便在重启后将其发送到服务器。

为此,您仍然需要自己进行一些设置。因为堆栈捕获可能相当大,您必须将其引用传递给捕获函数。寄存器直接返回。

我们需要能够在重启时持久化所有数据。如果您有文件系统或类似的东西,您可以将它写入那里。但大多数嵌入式系统都有一些 SRAM,因此我们可以将其保留在未初始化的内存中。

use core::mem::MaybeUninit;
use stackdump_core::memory_region::ArrayMemoryRegion;
use stackdump_core::register_data::ArrayRegisterData;

#[link_section = ".uninit"]
static mut STACK_CAPTURE: MaybeUninit<ArrayMemoryRegion<4096>> = MaybeUninit::uninit();
#[link_section = ".uninit"]
static mut CORE_REGISTERS_CAPTURE: MaybeUninit<ArrayRegisterData<16, u32>> = MaybeUninit::uninit();
#[link_section = ".uninit"]
static mut FPU_REGISTERS_CAPTURE: MaybeUninit<ArrayRegisterData<32, u32>> = MaybeUninit::uninit();

我们还需要能够在启动时检测到是否已捕获堆栈转储。最佳方法是有一个未初始化的整数,它可以具有特定的值以指示已制作转储。

use core::mem::MaybeUninit;

#[link_section = ".uninit"]
static mut CAPTURE_INDICATOR: MaybeUninit<u32> = MaybeUninit::uninit();
const CAPTURE_INDICATOR_TRUE: u32 = 0xC0DED1ED; // Code died

fn is_capture_made() -> bool {
    unsafe {
        CAPTURE_INDICATOR.assume_init() == CAPTURE_INDICATOR_TRUE
    }
}

fn reset_capture_made() {
    unsafe {
        CAPTURE_INDICATOR.write(0);
    }
}


fn set_capture_made() {
    unsafe {
        CAPTURE_INDICATOR.write(CAPTURE_INDICATOR_TRUE);
    }
}

现在我们可以在例如 panic 中捕获所有内容。

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    unsafe {
        stackdump_capture::cortex_m::capture(
            &mut *STACK_CAPTURE.as_mut_ptr(),
            &mut *CORE_REGISTERS_CAPTURE.as_mut_ptr(),
            &mut *FPU_REGISTERS_CAPTURE.as_mut_ptr(),
        );
        // If you want to capture the heap or the static data, then do that here too yourself
    }

    set_capture_made();
    cortex_m::peripheral::SCB::sys_reset()
}

然后在我们的主函数中,我们可以检查是否存在堆栈转储并将其发送到服务器。实际上传输数据是用户的责任,但内存区域和寄存器数据有一个迭代函数,您可以遍历字节。

fn main() {
    let server = (); // User defined

    if is_capture_made() {
        reset_capture_made();
        
        for byte in unsafe { STACK_CAPTURE.assume_init_ref().iter() } {
            server.send(byte);
        }
        for byte in unsafe { CORE_REGISTERS_CAPTURE.assume_init_ref().iter() } {
            server.send(byte);
        }
        for byte in unsafe { FPU_REGISTERS_CAPTURE.assume_init_ref().iter() } {
            server.send(byte);
        }
    }
}

依赖关系

~3MB
~62K SLoC