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 次下载
51KB
767 代码行
Stackdump Capture
此 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