1#![cfg_attr(target_arch = "riscv32", doc = "\n")]
6#![cfg_attr(
7 target_arch = "riscv32",
8 doc = "\nPlease note that you **need** to force frame pointers (i.e. `\"-C\", \"force-frame-pointers\",` in your `.cargo/config.toml`).\n"
9)]
10#![cfg_attr(
11 target_arch = "riscv32",
12 doc = "Otherwise the panic handler will emit a stack dump which needs tooling to decode it.\n\n"
13)]
14#![cfg_attr(target_arch = "riscv32", doc = "\n")]
15#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
21#![doc = ""]
28#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_backtrace_config_table.md"))]
29#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
30#![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))]
31#![no_std]
32
33#[macro_use]
34extern crate esp_metadata_generated;
35
36#[cfg(feature = "defmt")]
37use defmt as _;
38#[cfg(feature = "println")]
39use esp_println as _;
40
41const MAX_BACKTRACE_ADDRESSES: usize =
42 esp_config::esp_config_int!(usize, "ESP_BACKTRACE_CONFIG_BACKTRACE_FRAMES");
43
44#[derive(Clone)]
45pub struct Backtrace(pub(crate) heapless::Vec<BacktraceFrame, MAX_BACKTRACE_ADDRESSES>);
46
47impl Backtrace {
48 #[inline]
50 pub fn capture() -> Self {
51 arch::backtrace()
52 }
53
54 #[inline]
56 pub fn frames(&self) -> &[BacktraceFrame] {
57 &self.0
58 }
59}
60
61#[derive(Clone)]
62pub struct BacktraceFrame {
63 pub(crate) pc: usize,
64}
65
66impl BacktraceFrame {
67 pub fn program_counter(&self) -> usize {
68 self.pc - crate::arch::RA_OFFSET
69 }
70}
71
72#[cfg(feature = "panic-handler")]
73const RESET: &str = "\u{001B}[0m";
74#[cfg(feature = "panic-handler")]
75const RED: &str = "\u{001B}[31m";
76
77#[cfg(all(feature = "panic-handler", feature = "defmt"))]
78macro_rules! println {
79 ($($arg:tt)*) => {
80 defmt::error!($($arg)*);
81 };
82}
83
84#[cfg(all(feature = "panic-handler", feature = "defmt", stack_dump))]
85pub(crate) use println;
86
87#[cfg(all(feature = "panic-handler", feature = "println"))]
88macro_rules! println {
89 ($($arg:tt)*) => {
90 esp_println::println!($($arg)*);
91 };
92}
93
94#[cfg(feature = "panic-handler")]
95fn set_color_code(_code: &str) {
96 #[cfg(all(feature = "colors", feature = "println"))]
97 {
98 println!("{}", _code);
99 }
100}
101
102#[cfg_attr(target_arch = "riscv32", path = "riscv.rs")]
103#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")]
104pub(crate) mod arch;
105
106#[cfg(feature = "panic-handler")]
107#[panic_handler]
108fn panic_handler(info: &core::panic::PanicInfo) -> ! {
109 pre_backtrace();
110
111 set_color_code(RED);
112 println!("");
113 println!("====================== PANIC ======================");
114
115 println!("{}", info);
116 set_color_code(RESET);
117
118 cfg_if::cfg_if! {
119 if #[cfg(not(stack_dump))]
120 {
121 println!("");
122 println!("Backtrace:");
123 println!("");
124
125 let backtrace = Backtrace::capture();
126 #[cfg(target_arch = "riscv32")]
127 if backtrace.frames().is_empty() {
128 println!(
129 "No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)"
130 );
131 }
132 for frame in backtrace.frames() {
133 println!("0x{:x}", frame.program_counter());
134 }
135 } else {
136 arch::dump_stack();
137 }
138 }
139
140 abort()
141}
142
143fn is_valid_ram_address(address: u32) -> bool {
147 memory_range!("DRAM").contains(&address)
148}
149
150#[cfg(feature = "halt-cores")]
151fn halt() {
152 #[cfg(any(feature = "esp32", feature = "esp32s3"))]
153 {
154 #[cfg(feature = "esp32")]
155 mod registers {
156 pub(crate) const OPTIONS0: u32 = 0x3ff48000;
157 pub(crate) const SW_CPU_STALL: u32 = 0x3ff480ac;
158 }
159
160 #[cfg(feature = "esp32s3")]
161 mod registers {
162 pub(crate) const OPTIONS0: u32 = 0x60008000;
163 pub(crate) const SW_CPU_STALL: u32 = 0x600080bc;
164 }
165
166 let sw_cpu_stall = registers::SW_CPU_STALL as *mut u32;
167
168 unsafe {
169 let options0 = registers::OPTIONS0 as *mut u32;
175
176 options0.write_volatile(options0.read_volatile() & !(0b1111) | 0b1010);
177
178 sw_cpu_stall.write_volatile(
179 sw_cpu_stall.read_volatile() & !(0b111111 << 20) & !(0b111111 << 26)
180 | (0x21 << 20)
181 | (0x21 << 26),
182 );
183 }
184 }
185}
186
187#[cfg(feature = "panic-handler")]
188fn pre_backtrace() {
189 #[cfg(feature = "custom-pre-backtrace")]
190 {
191 unsafe extern "Rust" {
192 fn custom_pre_backtrace();
193 }
194 unsafe { custom_pre_backtrace() }
195 }
196}
197
198#[cfg(feature = "panic-handler")]
199fn abort() -> ! {
200 println!("");
201 println!("");
202 println!("");
203
204 cfg_if::cfg_if! {
205 if #[cfg(feature = "semihosting")] {
206 arch::interrupt_free(|| {
207 semihosting::process::abort();
208 });
209 } else if #[cfg(feature = "halt-cores")] {
210 halt();
211 } else if #[cfg(feature = "custom-halt")] {
212 unsafe extern "Rust" {
214 fn custom_halt() -> !;
215 }
216 unsafe { custom_halt() }
217 }
218 }
219
220 #[allow(unreachable_code)]
221 arch::interrupt_free(|| {
222 #[allow(clippy::empty_loop)]
223 loop {}
224 })
225}