Add the esp-backtrace package to the repository (#1583)

This commit is contained in:
Jesse Braham 2024-05-23 13:52:52 +00:00 committed by GitHub
parent bd4b044748
commit 49ebc23a6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 945 additions and 5 deletions

View File

@ -3,6 +3,7 @@ resolver = "2"
members = ["xtask"]
exclude = [
"esp-alloc",
"esp-backtrace",
"esp-build",
"esp-hal",
"esp-hal-procmacros",

45
esp-backtrace/Cargo.toml Normal file
View File

@ -0,0 +1,45 @@
[package]
name = "esp-backtrace"
version = "0.11.1"
edition = "2021"
description = "Bare-metal backtrace support for ESP32"
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.docs.rs]
default-target = "riscv32imc-unknown-none-elf"
features = ["esp32c3", "panic-handler", "exception-handler", "println", "esp-println/uart"]
[dependencies]
defmt = { version = "0.3.6", optional = true }
esp-println = { version = "0.9.1", optional = true, default-features = false, path = "../esp-println" }
semihosting = { version = "0.1.7", optional = true }
[build-dependencies]
esp-build = { version = "0.1.0", path = "../esp-build" }
[features]
default = ["colors"]
# You must enable exactly one of the below features to support the correct chip:
esp32 = ["esp-println?/esp32", "semihosting?/openocd-semihosting"]
esp32c2 = ["esp-println?/esp32c2"]
esp32c3 = ["esp-println?/esp32c3"]
esp32c6 = ["esp-println?/esp32c6"]
esp32h2 = ["esp-println?/esp32h2"]
esp32p4 = ["esp-println?/esp32p4"]
esp32s2 = ["esp-println?/esp32s2", "semihosting?/openocd-semihosting"]
esp32s3 = ["esp-println?/esp32s3", "semihosting?/openocd-semihosting"]
# Use esp-println
println = ["dep:esp-println"]
# Use defmt
defmt = ["dep:defmt"]
# You may optionally enable one or more of the below features to provide
# additional functionality:
colors = []
exception-handler = []
halt-cores = []
panic-handler = []

52
esp-backtrace/README.md Normal file
View File

@ -0,0 +1,52 @@
# esp-backtrace - backtrace for ESP32 bare-metal
Supports the ESP32, ESP32-C2/C3/C6, ESP32-H2, ESP32-P4, and ESP32-S2/S3. Optional exception and panic handlers are included, both of which can be enabled via their respective features.
Please note that when targeting a RISC-V device, you **need** to force frame pointers (i.e. `"-C", "force-frame-pointers",` in your `.cargo/config.toml`); this is **not** required for Xtensa.
You can get an array of backtrace addresses (currently limited to 10) via `arch::backtrace()` if
you want to create a backtrace yourself (i.e. not using the panic or exception handler).
When using the panic and/or exception handler make sure to include `use esp_backtrace as _;`.
## Features
| Feature | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------ |
| esp32 | Target ESP32 |
| esp32c2 | Target ESP32-C2 |
| esp32c3 | Target ESP32-C3 |
| esp32c6 | Target ESP32-C6 |
| esp32h2 | Target ESP32-H2 |
| esp32p4 | Target ESP32-P4 |
| esp32s2 | Target ESP32-S2 |
| esp32s3 | Target ESP32-S3 |
| panic-handler | Include a panic handler, will add `esp-println` as a dependency |
| exception-handler | Include an exception handler, will add `esp-println` as a dependency |
| println | Use `esp-println` to print messages |
| defmt | Use `defmt` logging to print messages\* (check [example](https://github.com/playfulFence/backtrace-defmt-example)) |
| colors | Print messages in red\* |
| halt-cores | Halt both CPUs on ESP32 / ESP32-S3 in case of a panic or exception |
| semihosting | Call `semihosting::process::abort()` on panic. |
\* _only used for panic and exception handlers_
### `defmt` Feature
Please note that `defmt` does _not_ provide MSRV guarantees with releases, and as such we are not able to make any MSRV guarantees when this feature is enabled. For more information refer to the MSRV section of `defmt`'s README:
https://github.com/knurling-rs/defmt?tab=readme-ov-file#msrv
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
any additional terms or conditions.

28
esp-backtrace/build.rs Normal file
View File

@ -0,0 +1,28 @@
use esp_build::assert_unique_used_features;
fn main() {
// Ensure that only a single chip is specified:
assert_unique_used_features!(
"esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4", "esp32s2", "esp32s3"
);
// Ensure that exactly a backend is selected:
assert_unique_used_features!("defmt", "println");
if is_nightly() {
println!("cargo:rustc-cfg=nightly");
}
}
fn is_nightly() -> bool {
let version_output = std::process::Command::new(
std::env::var_os("RUSTC").unwrap_or_else(|| std::ffi::OsString::from("rustc")),
)
.arg("-V")
.output()
.unwrap()
.stdout;
let version_string = String::from_utf8_lossy(&version_output);
version_string.contains("nightly")
}

336
esp-backtrace/src/lib.rs Normal file
View File

@ -0,0 +1,336 @@
#![no_std]
#![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))]
#![allow(rustdoc::bare_urls)]
#![doc = include_str!("../README.md")]
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
#![cfg_attr(nightly, feature(panic_info_message))]
#[cfg(feature = "defmt")]
use defmt as _;
#[cfg(feature = "println")]
use esp_println as _;
const MAX_BACKTRACE_ADDRESSES: usize = 10;
#[cfg(feature = "colors")]
const RESET: &str = "\u{001B}[0m";
#[cfg(feature = "colors")]
const RED: &str = "\u{001B}[31m";
#[cfg(feature = "defmt")]
macro_rules! println {
("") => {
// Do nothing if the string is just a space
};
($($arg:tt)*) => {
defmt::error!($($arg)*);
};
}
#[cfg(all(feature = "println", not(feature = "defmt")))]
macro_rules! println {
($($arg:tt)*) => {
esp_println::println!($($arg)*);
};
}
#[allow(unused, unused_variables)]
fn set_color_code(code: &str) {
#[cfg(feature = "println")]
{
println!("{}", code);
}
}
#[cfg_attr(target_arch = "riscv32", path = "riscv.rs")]
#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")]
pub mod arch;
#[cfg(feature = "panic-handler")]
#[panic_handler]
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
#[cfg(feature = "colors")]
set_color_code(RED);
println!("");
println!("");
if let Some(location) = info.location() {
let (file, line, column) = (location.file(), location.line(), location.column());
println!(
"!! A panic occured in '{}', at line {}, column {}:",
file, line, column
);
} else {
println!("!! A panic occured at an unknown location:");
}
#[cfg(not(nightly))]
{
#[cfg(not(feature = "defmt"))]
println!("{:#?}", info);
#[cfg(feature = "defmt")]
println!("{:#?}", defmt::Display2Format(info));
}
#[cfg(nightly)]
{
if let Some(message) = info.message() {
#[cfg(not(feature = "defmt"))]
println!("{}", message);
#[cfg(feature = "defmt")]
println!("{}", defmt::Display2Format(message));
}
}
println!("");
println!("Backtrace:");
println!("");
let backtrace = crate::arch::backtrace();
#[cfg(target_arch = "riscv32")]
if backtrace.iter().filter(|e| e.is_some()).count() == 0 {
println!("No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)");
}
for e in backtrace {
if let Some(addr) = e {
#[cfg(all(feature = "colors", feature = "println"))]
println!("{}0x{:x}", RED, addr - crate::arch::RA_OFFSET);
#[cfg(not(all(feature = "colors", feature = "println")))]
println!("0x{:x}", addr - crate::arch::RA_OFFSET);
}
}
#[cfg(feature = "colors")]
set_color_code(RESET);
#[cfg(feature = "semihosting")]
semihosting::process::abort();
halt();
}
#[cfg(all(feature = "exception-handler", target_arch = "xtensa"))]
#[no_mangle]
#[link_section = ".rwtext"]
unsafe fn __user_exception(cause: arch::ExceptionCause, context: arch::Context) {
#[cfg(feature = "colors")]
set_color_code(RED);
// Unfortunately, a different formatter string is used
#[cfg(not(feature = "defmt"))]
esp_println::println!("\n\nException occured '{:?}'", cause);
#[cfg(feature = "defmt")]
defmt::error!("\n\nException occured '{}'", cause);
println!("{:?}", context);
let backtrace = crate::arch::backtrace_internal(context.A1, 0);
for e in backtrace {
if let Some(addr) = e {
println!("0x{:x}", addr);
}
}
println!("");
println!("");
println!("");
#[cfg(feature = "colors")]
set_color_code(RESET);
#[cfg(feature = "semihosting")]
semihosting::process::abort();
halt();
}
#[cfg(all(feature = "exception-handler", target_arch = "riscv32"))]
#[export_name = "ExceptionHandler"]
fn exception_handler(context: &arch::TrapFrame) -> ! {
let mepc = context.pc;
let code = context.mcause & 0xff;
let mtval = context.mtval;
#[cfg(feature = "colors")]
set_color_code(RED);
if code == 14 {
println!("");
println!(
"Stack overflow detected at 0x{:x} called by 0x{:x}",
mepc, context.ra
);
println!("");
} else {
let code = match code {
0 => "Instruction address misaligned",
1 => "Instruction access fault",
2 => "Illegal instruction",
3 => "Breakpoint",
4 => "Load address misaligned",
5 => "Load access fault",
6 => "Store/AMO address misaligned",
7 => "Store/AMO access fault",
8 => "Environment call from U-mode",
9 => "Environment call from S-mode",
10 => "Reserved",
11 => "Environment call from M-mode",
12 => "Instruction page fault",
13 => "Load page fault",
14 => "Reserved",
15 => "Store/AMO page fault",
_ => "UNKNOWN",
};
println!(
"Exception '{}' mepc=0x{:08x}, mtval=0x{:08x}",
code, mepc, mtval
);
#[cfg(not(feature = "defmt"))]
println!("{:x?}", context);
#[cfg(feature = "defmt")]
println!("{:?}", context);
let backtrace = crate::arch::backtrace_internal(context.s0 as u32, 0);
if backtrace.iter().filter(|e| e.is_some()).count() == 0 {
println!("No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)");
}
for e in backtrace {
if let Some(addr) = e {
#[cfg(all(feature = "colors", feature = "println"))]
println!("{}0x{:x}", RED, addr - crate::arch::RA_OFFSET);
#[cfg(not(all(feature = "colors", feature = "println")))]
println!("0x{:x}", addr - crate::arch::RA_OFFSET);
}
}
}
println!("");
println!("");
println!("");
#[cfg(feature = "colors")]
set_color_code(RESET);
#[cfg(feature = "semihosting")]
semihosting::process::abort();
halt();
}
// Ensure that the address is in DRAM and that it is 16-byte aligned.
//
// Based loosely on the `esp_stack_ptr_in_dram` function from
// `components/esp_hw_support/include/esp_memory_utils.h` in ESP-IDF.
//
// Address ranges can be found in `components/soc/$CHIP/include/soc/soc.h` as
// `SOC_DRAM_LOW` and `SOC_DRAM_HIGH`.
fn is_valid_ram_address(address: u32) -> bool {
if (address & 0xF) != 0 {
return false;
}
#[cfg(feature = "esp32")]
if !(0x3FFA_E000..=0x4000_0000).contains(&address) {
return false;
}
#[cfg(feature = "esp32c2")]
if !(0x3FCA_0000..=0x3FCE_0000).contains(&address) {
return false;
}
#[cfg(feature = "esp32c3")]
if !(0x3FC8_0000..=0x3FCE_0000).contains(&address) {
return false;
}
#[cfg(feature = "esp32c6")]
if !(0x4080_0000..=0x4088_0000).contains(&address) {
return false;
}
#[cfg(feature = "esp32h2")]
if !(0x4080_0000..=0x4085_0000).contains(&address) {
return false;
}
#[cfg(feature = "esp32p4")]
if !(0x4FF0_0000..=0x4FFC_0000).contains(&address) {
return false;
}
#[cfg(feature = "esp32s2")]
if !(0x3FFB_0000..=0x4000_0000).contains(&address) {
return false;
}
#[cfg(feature = "esp32s3")]
if !(0x3FC8_8000..=0x3FD0_0000).contains(&address) {
return false;
}
true
}
#[cfg(any(
not(any(feature = "esp32", feature = "esp32p4", feature = "esp32s3")),
not(feature = "halt-cores")
))]
#[allow(unused)]
fn halt() -> ! {
loop {}
}
// TODO: Enable `halt` function for `esp32p4` feature once implemented
#[cfg(all(any(feature = "esp32", feature = "esp32s3"), feature = "halt-cores"))]
#[allow(unused)]
fn halt() -> ! {
#[cfg(feature = "esp32")]
mod registers {
pub(crate) const OPTIONS0: u32 = 0x3ff48000;
pub(crate) const SW_CPU_STALL: u32 = 0x3ff480ac;
}
#[cfg(feature = "esp32p4")]
mod registers {
pub(crate) const SW_CPU_STALL: u32 = 0x50115200;
}
#[cfg(feature = "esp32s3")]
mod registers {
pub(crate) const OPTIONS0: u32 = 0x60008000;
pub(crate) const SW_CPU_STALL: u32 = 0x600080bc;
}
let sw_cpu_stall = registers::SW_CPU_STALL as *mut u32;
#[cfg(feature = "esp32p4")]
unsafe {}
#[cfg(not(feature = "esp32p4"))]
unsafe {
// We need to write the value "0x86" to stall a particular core. The write
// location is split into two separate bit fields named "c0" and "c1", and the
// two fields are located in different registers. Each core has its own pair of
// "c0" and "c1" bit fields.
let options0 = registers::OPTIONS0 as *mut u32;
options0.write_volatile(options0.read_volatile() & !(0b1111) | 0b1010);
sw_cpu_stall.write_volatile(
sw_cpu_stall.read_volatile() & !(0b111111 << 20) & !(0b111111 << 26)
| (0x21 << 20)
| (0x21 << 26),
);
}
loop {}
}

168
esp-backtrace/src/riscv.rs Normal file
View File

@ -0,0 +1,168 @@
use core::arch::asm;
use crate::MAX_BACKTRACE_ADDRESSES;
// subtract 4 from the return address
// the return address is the address following the JALR
// we get better results (especially if the caller was the last function in the
// calling function) if we report the address of the JALR itself
// even if it was a C.JALR we should get good results using RA - 4
pub(super) const RA_OFFSET: usize = 4;
/// Registers saved in trap handler
#[doc(hidden)]
#[allow(missing_docs)]
#[derive(Default, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(C)]
pub(crate) struct TrapFrame {
pub ra: usize,
pub t0: usize,
pub t1: usize,
pub t2: usize,
pub t3: usize,
pub t4: usize,
pub t5: usize,
pub t6: usize,
pub a0: usize,
pub a1: usize,
pub a2: usize,
pub a3: usize,
pub a4: usize,
pub a5: usize,
pub a6: usize,
pub a7: usize,
pub s0: usize,
pub s1: usize,
pub s2: usize,
pub s3: usize,
pub s4: usize,
pub s5: usize,
pub s6: usize,
pub s7: usize,
pub s8: usize,
pub s9: usize,
pub s10: usize,
pub s11: usize,
pub gp: usize,
pub tp: usize,
pub sp: usize,
pub pc: usize,
pub mstatus: usize,
pub mcause: usize,
pub mtval: usize,
}
impl core::fmt::Debug for TrapFrame {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(
fmt,
"TrapFrame
PC=0x{:08x} RA/x1=0x{:08x} SP/x2=0x{:08x} GP/x3=0x{:08x} TP/x4=0x{:08x}
T0/x5=0x{:08x} T1/x6=0x{:08x} T2/x7=0x{:08x} S0/FP/x8=0x{:08x} S1/x9=0x{:08x}
A0/x10=0x{:08x} A1/x11=0x{:08x} A2/x12=0x{:08x} A3/x13=0x{:08x} A4/x14=0x{:08x}
A5/x15=0x{:08x} A6/x16=0x{:08x} A7/x17=0x{:08x} S2/x18=0x{:08x} S3/x19=0x{:08x}
S4/x20=0x{:08x} S5/x21=0x{:08x} S6/x22=0x{:08x} S7/x23=0x{:08x} S8/x24=0x{:08x}
S9/x25=0x{:08x} S10/x26=0x{:08x} S11/x27=0x{:08x} T3/x28=0x{:08x} T4/x29=0x{:08x}
T5/x30=0x{:08x} T6/x31=0x{:08x}
MSTATUS=0x{:08x}
MCAUSE=0x{:08x}
MTVAL=0x{:08x}
",
self.pc,
self.ra,
self.gp,
self.sp,
self.tp,
self.t0,
self.t1,
self.t2,
self.s0,
self.s1,
self.a0,
self.a1,
self.a2,
self.a3,
self.a4,
self.a5,
self.a6,
self.a7,
self.s2,
self.s3,
self.s4,
self.s5,
self.s6,
self.s7,
self.s8,
self.s9,
self.s10,
self.s11,
self.t3,
self.t4,
self.t5,
self.t6,
self.mstatus,
self.mcause,
self.mtval,
)
}
}
/// Get an array of backtrace addresses.
///
/// This needs `force-frame-pointers` enabled.
pub fn backtrace() -> [Option<usize>; MAX_BACKTRACE_ADDRESSES] {
let fp = unsafe {
let mut _tmp: u32;
asm!("mv {0}, x8", out(reg) _tmp);
_tmp
};
backtrace_internal(fp, 2)
}
pub(crate) fn backtrace_internal(
fp: u32,
suppress: i32,
) -> [Option<usize>; MAX_BACKTRACE_ADDRESSES] {
let mut result = [None; 10];
let mut index = 0;
let mut fp = fp;
let mut suppress = suppress;
let mut old_address = 0;
loop {
unsafe {
let address = (fp as *const u32).offset(-1).read_volatile(); // RA/PC
fp = (fp as *const u32).offset(-2).read_volatile(); // next FP
if old_address == address {
break;
}
old_address = address;
if address == 0 {
break;
}
if !crate::is_valid_ram_address(fp) {
break;
}
if suppress == 0 {
result[index] = Some(address as usize);
index += 1;
if index >= MAX_BACKTRACE_ADDRESSES {
break;
}
} else {
suppress -= 1;
}
}
}
result
}

309
esp-backtrace/src/xtensa.rs Normal file
View File

@ -0,0 +1,309 @@
use core::arch::asm;
use crate::MAX_BACKTRACE_ADDRESSES;
// subtract 3 from the return address
// the return address is the address following the callxN
// we get better results (especially if the caller was the last function in the
// calling function) if we report the address of callxN itself
pub(super) const RA_OFFSET: usize = 3;
#[doc(hidden)]
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(C)]
pub enum ExceptionCause {
/// Illegal Instruction
IllegalInstruction = 0,
/// System Call (Syscall Instruction)
Syscall = 1,
/// Instruction Fetch Error
InstrFetchError = 2,
/// Load Store Error
LoadStoreError = 3,
/// Level 1 Interrupt
LevelOneInterrupt = 4,
/// Stack Extension Assist (movsp Instruction) For Alloca
Alloca = 5,
/// Integer Divide By Zero
DivideByZero = 6,
/// Use Of Failed Speculative Access (Not Implemented)
NextPCValueIllegal = 7,
/// Privileged Instruction
PrivilegedInstruction = 8,
/// Unaligned Load Or Store
UnalignedLoadOrStore = 9,
/// Reserved
ExternalRegisterPrivilegeError = 10,
/// Reserved
ExclusiveError = 11,
/// Pif Data Error On Instruction Fetch (Rb-200x And Later)
InstrDataError = 12,
/// Pif Data Error On Load Or Store (Rb-200x And Later)
LoadStoreDataError = 13,
/// Pif Address Error On Instruction Fetch (Rb-200x And Later)
InstrAddrError = 14,
/// Pif Address Error On Load Or Store (Rb-200x And Later)
LoadStoreAddrError = 15,
/// Itlb Miss (No Itlb Entry Matches, Hw Refill Also Missed)
ItlbMiss = 16,
/// Itlb Multihit (Multiple Itlb Entries Match)
ItlbMultiHit = 17,
/// Ring Privilege Violation On Instruction Fetch
InstrRing = 18,
/// Size Restriction On Ifetch (Not Implemented)
Reserved19 = 19,
/// Cache Attribute Does Not Allow Instruction Fetch
InstrProhibited = 20,
/// Reserved
Reserved21 = 21,
/// Reserved
Reserved22 = 22,
/// Reserved
Reserved23 = 23,
/// Dtlb Miss (No Dtlb Entry Matches, Hw Refill Also Missed)
DtlbMiss = 24,
/// Dtlb Multihit (Multiple Dtlb Entries Match)
DtlbMultiHit = 25,
/// Ring Privilege Violation On Load Or Store
LoadStoreRing = 26,
/// Size Restriction On Load/Store (Not Implemented)
Reserved27 = 27,
/// Cache Attribute Does Not Allow Load
LoadProhibited = 28,
/// Cache Attribute Does Not Allow Store
StoreProhibited = 29,
/// Reserved
Reserved30 = 30,
/// Reserved
Reserved31 = 31,
/// Access To Coprocessor 0 When Disabled
Cp0Disabled = 32,
/// Access To Coprocessor 1 When Disabled
Cp1Disabled = 33,
/// Access To Coprocessor 2 When Disabled
Cp2Disabled = 34,
/// Access To Coprocessor 3 When Disabled
Cp3Disabled = 35,
/// Access To Coprocessor 4 When Disabled
Cp4Disabled = 36,
/// Access To Coprocessor 5 When Disabled
Cp5Disabled = 37,
/// Access To Coprocessor 6 When Disabled
Cp6Disabled = 38,
/// Access To Coprocessor 7 When Disabled
Cp7Disabled = 39,
None = 255,
}
#[doc(hidden)]
#[allow(missing_docs, non_snake_case)]
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(C)]
pub struct Context {
pub PC: u32,
pub PS: u32,
pub A0: u32,
pub A1: u32,
pub A2: u32,
pub A3: u32,
pub A4: u32,
pub A5: u32,
pub A6: u32,
pub A7: u32,
pub A8: u32,
pub A9: u32,
pub A10: u32,
pub A11: u32,
pub A12: u32,
pub A13: u32,
pub A14: u32,
pub A15: u32,
pub SAR: u32,
pub EXCCAUSE: u32,
pub EXCVADDR: u32,
pub LBEG: u32,
pub LEND: u32,
pub LCOUNT: u32,
pub THREADPTR: u32,
pub SCOMPARE1: u32,
pub BR: u32,
pub ACCLO: u32,
pub ACCHI: u32,
pub M0: u32,
pub M1: u32,
pub M2: u32,
pub M3: u32,
pub F64R_LO: u32,
pub F64R_HI: u32,
pub F64S: u32,
pub FCR: u32,
pub FSR: u32,
pub F0: u32,
pub F1: u32,
pub F2: u32,
pub F3: u32,
pub F4: u32,
pub F5: u32,
pub F6: u32,
pub F7: u32,
pub F8: u32,
pub F9: u32,
pub F10: u32,
pub F11: u32,
pub F12: u32,
pub F13: u32,
pub F14: u32,
pub F15: u32,
}
impl core::fmt::Debug for Context {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(
fmt,
"Context
PC=0x{:08x} PS=0x{:08x}
A0=0x{:08x} A1=0x{:08x} A2=0x{:08x} A3=0x{:08x} A4=0x{:08x}
A5=0x{:08x} A6=0x{:08x} A7=0x{:08x} A8=0x{:08x} A9=0x{:08x}
A10=0x{:08x} A11=0x{:08x} A12=0x{:08x} A13=0x{:08x} A14=0x{:08x}
A15=0x{:08x}
SAR={:08x}
EXCCAUSE=0x{:08x} EXCVADDR=0x{:08x}
LBEG=0x{:08x} LEND=0x{:08x} LCOUNT=0x{:08x}
THREADPTR=0x{:08x}
SCOMPARE1=0x{:08x}
BR=0x{:08x}
ACCLO=0x{:08x} ACCHI=0x{:08x}
M0=0x{:08x} M1=0x{:08x} M2=0x{:08x} M3=0x{:08x}
F64R_LO=0x{:08x} F64R_HI=0x{:08x} F64S=0x{:08x}
FCR=0x{:08x} FSR=0x{:08x}
F0=0x{:08x} F1=0x{:08x} F2=0x{:08x} F3=0x{:08x} F4=0x{:08x}
F5=0x{:08x} F6=0x{:08x} F7=0x{:08x} F8=0x{:08x} F9=0x{:08x}
F10=0x{:08x} F11=0x{:08x} F12=0x{:08x} F13=0x{:08x} F14=0x{:08x}
F15=0x{:08x}
",
self.PC,
self.PS,
self.A0,
self.A1,
self.A2,
self.A3,
self.A4,
self.A5,
self.A6,
self.A7,
self.A8,
self.A9,
self.A10,
self.A11,
self.A12,
self.A13,
self.A14,
self.A15,
self.SAR,
self.EXCCAUSE,
self.EXCVADDR,
self.LBEG,
self.LEND,
self.LCOUNT,
self.THREADPTR,
self.SCOMPARE1,
self.BR,
self.ACCLO,
self.ACCHI,
self.M0,
self.M1,
self.M2,
self.M3,
self.F64R_LO,
self.F64R_HI,
self.F64S,
self.FCR,
self.FSR,
self.F0,
self.F1,
self.F2,
self.F3,
self.F4,
self.F5,
self.F6,
self.F7,
self.F8,
self.F9,
self.F10,
self.F11,
self.F12,
self.F13,
self.F14,
self.F15,
)
}
}
/// Get an array of backtrace addresses.
pub fn backtrace() -> [Option<usize>; MAX_BACKTRACE_ADDRESSES] {
let sp = unsafe {
let mut _tmp: u32;
asm!("mov {0}, a1", out(reg) _tmp);
_tmp
};
backtrace_internal(sp, 1)
}
pub(crate) fn sanitize_address(address: u32) -> u32 {
(address & 0x3fff_ffff) | 0x4000_0000
}
pub(crate) fn backtrace_internal(
sp: u32,
suppress: i32,
) -> [Option<usize>; MAX_BACKTRACE_ADDRESSES] {
let mut result = [None; 10];
let mut index = 0;
let mut fp = sp;
let mut suppress = suppress;
let mut old_address = 0;
loop {
unsafe {
let address = sanitize_address((fp as *const u32).offset(-4).read_volatile()); // RA/PC
fp = (fp as *const u32).offset(-3).read_volatile(); // next FP
if old_address == address {
break;
}
old_address = address;
if address == 0 {
break;
}
if !crate::is_valid_ram_address(fp) {
break;
}
if fp == 0 {
break;
}
if suppress == 0 {
result[index] = Some(address as usize);
index += 1;
if index >= MAX_BACKTRACE_ADDRESSES {
break;
}
} else {
suppress -= 1;
}
}
}
result
}

View File

@ -25,7 +25,7 @@ embedded-hal-async = "1.0.0"
embedded-hal-bus = "0.1.0"
embedded-io-async = "0.6.1"
esp-alloc = { version = "0.3.0", path = "../esp-alloc" }
esp-backtrace = { version = "0.11.1", features = ["exception-handler", "panic-handler", "println"] }
esp-backtrace = { version = "0.11.1", path = "../esp-backtrace", features = ["exception-handler", "panic-handler", "println"] }
esp-hal = { version = "0.17.0", path = "../esp-hal", features = ["log"] }
esp-hal-smartled = { version = "0.10.0", path = "../esp-hal-smartled", optional = true }
esp-ieee802154 = { version = "0.1.0", path = "../esp-ieee802154", optional = true }
@ -79,7 +79,3 @@ incremental = false
opt-level = 3
lto = 'fat'
overflow-checks = false
# TODO: Remove patch once `esp-backtrace` package has been added to repository
[patch.crates-io]
esp-println = { version = "0.9.1", path = "../esp-println" }

View File

@ -18,11 +18,16 @@ pub mod cargo;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, EnumIter, ValueEnum)]
#[strum(serialize_all = "kebab-case")]
pub enum Package {
EspAlloc,
EspBacktrace,
EspBuild,
EspHal,
EspHalProcmacros,
EspHalSmartled,
EspIeee802154,
EspLpHal,
EspMetadata,
EspPrintln,
EspRiscvRt,
Examples,
HilTest,