在Rust #[no_std]环境中如何获取panic消息?
在#[no_std]环境中捕获panic原因及处理编译器生成的panic payload
编译器生成的panic payload类型
对于算术溢出、数组越界这类Rust内置调试检查触发的panic,编译器默认会生成**&'static str类型的payload**,而非String——这是因为#[no_std]环境默认不包含alloc crate,String依赖堆分配无法直接使用。你之前尝试用downcast_ref::<String>()自然拿不到数据,换成downcast_ref::<&str>()就能捕获这类内置panic的原因。
实现能捕获原因的自定义panic handler
下面是适配#[no_std]的panic handler示例,同时获取文件、行号和panic原因:
#![no_std] #![no_main] #![panic_handler = "custom_panic"] use core::panic::PanicInfo; #[panic_handler] fn custom_panic(info: &PanicInfo) -> ! { // 获取panic发生的位置信息 let location = info.location().unwrap(); let file = location.file(); let line = location.line(); // 获取panic原因 let msg = match info.payload().downcast_ref::<&str>() { Some(s) => *s, None => "Unknown panic reason", }; // 替换成你嵌入式环境的输出逻辑(比如串口打印) // 示例:uart_println!("Panic at {}:{} - {}", file, line, msg); loop {} // 或调用硬件中止逻辑 } // 示例:触发算术溢出panic(debug模式默认启用溢出检查) #[no_mangle] fn main() { let x: u8 = 255; let _y = x + 1; // 触发算术溢出panic }
自定义可打印的panic payload(可选)
如果需要自定义panic信息格式,可以定义轻量结构体(无需堆分配),实现core::fmt::Display或core::fmt::Debug,再在panic!宏中传入:
use core::fmt; #[derive(Debug)] struct CustomPanicPayload { module: &'static str, code: u32, } impl fmt::Display for CustomPanicPayload { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Module {} error code: {}", self.module, self.code) } } // 在代码中触发自定义panic panic!(CustomPanicPayload { module: "UART", code: 0x01 }); // 在panic handler中捕获 let msg_buffer = [0u8; 64]; // 静态缓冲区,无需堆分配 let mut writer = core::fmt::Write::new(&mut msg_buffer[..]); match info.payload().downcast_ref::<CustomPanicPayload>() { Some(payload) => let _ = write!(writer, "{}", payload), None => { let default_msg = info.payload().downcast_ref::<&str>().unwrap_or("Unknown panic"); let _ = write!(writer, "{}", default_msg); } }; // 输出msg_buffer中的内容到硬件
如果你的#[no_std]环境未启用alloc,全程用静态缓冲区配合core::fmt即可避免堆分配。
内容的提问来源于stack exchange,提问作者QuinnF
相关产品推荐
相关产品推荐

