probe_rs_debug/
debug_info.rs

1use super::{
2    DebugError, DebugRegisters, StackFrame, VariableCache,
3    exception_handling::ExceptionInterface,
4    function_die::{Die, FunctionDie},
5    get_object_reference,
6    unit_info::UnitInfo,
7    variable::*,
8};
9use crate::{SourceLocation, VerifiedBreakpoint, stack_frame::StackFrameInfo, unit_info::RangeExt};
10use gimli::{
11    BaseAddresses, DebugFrame, RunTimeEndian, UnwindContext, UnwindSection, UnwindTableRow,
12};
13use object::read::{Object, ObjectSection};
14use probe_rs::{Error, MemoryInterface, RegisterDataType, RegisterRole, RegisterValue, UnwindRule};
15use probe_rs_target::InstructionSet;
16use std::{
17    borrow, cmp::Ordering, num::NonZeroU64, ops::ControlFlow, path::Path, rc::Rc, str::from_utf8,
18};
19use typed_path::{TypedPath, TypedPathBuf};
20
21pub(crate) type GimliReader = gimli::EndianReader<RunTimeEndian, std::rc::Rc<[u8]>>;
22pub(crate) type GimliReaderOffset =
23    <gimli::EndianReader<RunTimeEndian, Rc<[u8]>> as gimli::Reader>::Offset;
24
25pub(crate) type GimliAttribute = gimli::Attribute<GimliReader>;
26
27pub(crate) type DwarfReader = gimli::read::EndianRcSlice<RunTimeEndian>;
28
29/// Debug information which is parsed from DWARF debugging information.
30pub struct DebugInfo {
31    pub(crate) dwarf: gimli::Dwarf<DwarfReader>,
32    pub(crate) frame_section: gimli::DebugFrame<DwarfReader>,
33    pub(crate) locations_section: gimli::LocationLists<DwarfReader>,
34    pub(crate) address_section: gimli::DebugAddr<DwarfReader>,
35    pub(crate) debug_line_section: gimli::DebugLine<DwarfReader>,
36
37    pub(crate) unit_infos: Vec<UnitInfo>,
38    pub(crate) endianness: gimli::RunTimeEndian,
39
40    pub(crate) addr2line: Option<addr2line::Loader>,
41}
42
43impl DebugInfo {
44    /// Read debug info directly from a ELF file.
45    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<DebugInfo, DebugError> {
46        let data = std::fs::read(path.as_ref())?;
47
48        let mut this = DebugInfo::from_raw(&data)?;
49        this.addr2line = addr2line::Loader::new(path).ok();
50        Ok(this)
51    }
52
53    /// Parse debug information directly from a buffer containing an ELF file.
54    pub fn from_raw(data: &[u8]) -> Result<Self, DebugError> {
55        let object = object::File::parse(data)?;
56
57        let endianness = if object.is_little_endian() {
58            RunTimeEndian::Little
59        } else {
60            RunTimeEndian::Big
61        };
62
63        // Load a section and return as `Cow<[u8]>`.
64        let load_section = |id: gimli::SectionId| -> Result<DwarfReader, gimli::Error> {
65            let data = object
66                .section_by_name(id.name())
67                .and_then(|section| section.uncompressed_data().ok())
68                .unwrap_or_else(|| borrow::Cow::Borrowed(&[][..]));
69
70            Ok(gimli::read::EndianRcSlice::new(
71                Rc::from(&*data),
72                endianness,
73            ))
74        };
75
76        // Load all of the sections.
77        let dwarf_cow = gimli::Dwarf::load(&load_section)?;
78
79        use gimli::Section;
80        let mut frame_section = gimli::DebugFrame::load(load_section)?;
81        let address_section = gimli::DebugAddr::load(load_section)?;
82        let debug_loc = gimli::DebugLoc::load(load_section)?;
83        let debug_loc_lists = gimli::DebugLocLists::load(load_section)?;
84        let locations_section = gimli::LocationLists::new(debug_loc, debug_loc_lists);
85        let debug_line_section = gimli::DebugLine::load(load_section)?;
86
87        let mut unit_infos = Vec::new();
88
89        let mut iter = dwarf_cow.units();
90
91        while let Ok(Some(header)) = iter.next() {
92            if let Ok(unit) = dwarf_cow.unit(header) {
93                // The DWARF V5 standard, section 2.4 specifies that the address size
94                // for the object file (or the target architecture default) will be used for
95                // DWARF debugging information.
96                // The following line is a workaround for instances where the address size of the
97                // CIE (Common Information Entry) is not correctly set.
98                // The frame section address size is only used for CIE versions before 4.
99                frame_section.set_address_size(unit.encoding().address_size);
100
101                unit_infos.push(UnitInfo::new(unit));
102            };
103        }
104
105        Ok(DebugInfo {
106            dwarf: dwarf_cow,
107            frame_section,
108            locations_section,
109            address_section,
110            debug_line_section,
111            unit_infos,
112            endianness,
113            addr2line: None,
114        })
115    }
116
117    /// Try get the [`SourceLocation`] for a given address.
118    pub fn get_source_location(&self, address: u64) -> Option<SourceLocation> {
119        for unit_info in &self.unit_infos {
120            let unit = &unit_info.unit;
121
122            let mut ranges = match self.dwarf.unit_ranges(unit) {
123                Ok(ranges) => ranges,
124                Err(error) => {
125                    tracing::warn!(
126                        "No valid source code ranges found for unit {:?}: {:?}",
127                        unit.dwo_name(),
128                        error
129                    );
130                    continue;
131                }
132            };
133
134            while let Ok(Some(range)) = ranges.next() {
135                if !(range.begin <= address && address < range.end) {
136                    continue;
137                }
138                // Get the DWARF LineProgram.
139                let ilnp = unit.line_program.as_ref()?.clone();
140
141                let (program, sequences) = match ilnp.sequences() {
142                    Ok(value) => value,
143                    Err(error) => {
144                        tracing::warn!(
145                            "No valid source code ranges found for address {}: {:?}",
146                            address,
147                            error
148                        );
149                        continue;
150                    }
151                };
152
153                // Normalize the address.
154                let mut target_seq = None;
155
156                for seq in sequences {
157                    if seq.start <= address && address < seq.end {
158                        target_seq = Some(seq);
159                        break;
160                    }
161                }
162
163                let Some(target_seq) = target_seq.as_ref() else {
164                    continue;
165                };
166
167                let mut previous_row: Option<gimli::LineRow> = None;
168
169                let mut rows = program.resume_from(target_seq);
170
171                while let Ok(Some((_, row))) = rows.next_row() {
172                    match row.address().cmp(&address) {
173                        Ordering::Greater => {
174                            // The address is after the current row, so we use the previous row data.
175                            //
176                            // (If we don't do this, you get the artificial effect where the debugger
177                            // steps to the top of the file when it is steppping out of a function.)
178                            if let Some(previous_row) = previous_row
179                                && let Some(path) =
180                                    self.find_file_and_directory(unit, previous_row.file_index())
181                            {
182                                tracing::debug!("{:#010x} - {:?}", address, previous_row.isa());
183                                return Some(SourceLocation {
184                                    line: previous_row.line().map(NonZeroU64::get),
185                                    column: Some(previous_row.column().into()),
186                                    path,
187                                    address: Some(previous_row.address()),
188                                });
189                            }
190                        }
191                        Ordering::Less => {}
192                        Ordering::Equal => {
193                            if let Some(path) = self.find_file_and_directory(unit, row.file_index())
194                            {
195                                tracing::debug!("{:#010x} - {:?}", address, row.isa());
196
197                                return Some(SourceLocation {
198                                    line: row.line().map(NonZeroU64::get),
199                                    column: Some(row.column().into()),
200                                    path,
201                                    address: Some(row.address()),
202                                });
203                            }
204                        }
205                    }
206                    previous_row = Some(*row);
207                }
208            }
209        }
210        None
211    }
212
213    /// We do not actually resolve the children of `[VariableName::StaticScope]` automatically,
214    /// and only create the necessary header in the `VariableCache`.
215    /// This allows us to resolve the `[VariableName::StaticScope]` on demand/lazily, when a user requests it from the debug client.
216    /// This saves a lot of overhead when a user only wants to see the `[VariableName::LocalScope]` or `
217    /// [VariableName::Registers]` while stepping through code (the most common use cases)
218    pub fn create_static_scope_cache(&self) -> VariableCache {
219        VariableCache::new_static_cache()
220    }
221
222    /// Creates the unpopulated cache for `function` variables
223    pub(crate) fn create_function_scope_cache(
224        &self,
225        die_cursor_state: &FunctionDie,
226        unit_info: &UnitInfo,
227    ) -> Result<VariableCache, DebugError> {
228        let function_variable_cache = VariableCache::new_dwarf_cache(
229            die_cursor_state.function_die.offset(),
230            VariableName::LocalScopeRoot,
231            unit_info,
232        )?;
233
234        Ok(function_variable_cache)
235    }
236
237    /// This effects the on-demand expansion of lazy/deferred load of all the 'child' `Variable`s for a given 'parent'.
238    #[tracing::instrument(level = "trace", skip_all, fields(parent_variable = ?parent_variable.variable_key()))]
239    pub fn cache_deferred_variables(
240        &self,
241        cache: &mut VariableCache,
242        memory: &mut dyn MemoryInterface,
243        parent_variable: &mut Variable,
244        frame_info: StackFrameInfo<'_>,
245    ) -> Result<(), DebugError> {
246        if !parent_variable.is_valid() {
247            // Do nothing. The parent_variable.get_value() will already report back the debug_error value.
248            return Ok(());
249        }
250
251        // Only attempt this part if we have not yet resolved the referenced children.
252        if cache.has_children(parent_variable) {
253            return Ok(());
254        }
255
256        match parent_variable.variable_node_type {
257            VariableNodeType::TypeOffset(header_offset, unit_offset)
258            | VariableNodeType::DirectLookup(header_offset, unit_offset) => {
259                let Some(unit_info) = self
260                    .unit_infos
261                    .iter()
262                    .find(|unit_info| unit_info.unit.header.offset() == header_offset.into())
263                else {
264                    return Err(DebugError::Other(
265                        "Failed to find unit info for offset lookup.".to_string(),
266                    ));
267                };
268
269                // Find the parent node
270                let mut type_tree = unit_info.unit.entries_tree(Some(unit_offset))?;
271                let parent_node = type_tree.root()?;
272
273                unit_info.process_tree(
274                    self,
275                    parent_node,
276                    parent_variable,
277                    memory,
278                    cache,
279                    frame_info,
280                )?;
281            }
282            VariableNodeType::UnitsLookup => {
283                if self.unit_infos.is_empty() {
284                    // No unit infos
285                    return Err(DebugError::Other("Missing unit infos".to_string()));
286                }
287
288                // Look up static variables from all units
289                for unit_info in self.unit_infos.iter() {
290                    let mut entries = unit_info.unit.entries();
291
292                    // Only process statics for this unit header.
293                    // Navigate the current unit from the header down.
294                    let (_, unit_node) = entries.next_dfs()?.unwrap();
295                    let unit_offset = unit_node.offset();
296
297                    let mut type_tree = unit_info.unit.entries_tree(Some(unit_offset))?;
298                    let parent_node = type_tree.root()?;
299
300                    unit_info.process_tree(
301                        self,
302                        parent_node,
303                        parent_variable,
304                        memory,
305                        cache,
306                        frame_info,
307                    )?;
308                }
309            }
310            _ => {
311                // Do nothing. These have already been recursed to their maximum.
312            }
313        }
314        Ok(())
315    }
316
317    /// Best-effort way to look up a function name without debuginfo.
318    fn get_stackframe_from_symbols(
319        &self,
320        address: u64,
321        unwind_registers: &DebugRegisters,
322    ) -> Result<Vec<StackFrame>, DebugError> {
323        let Some(ref addr2line) = self.addr2line else {
324            return Ok(vec![]);
325        };
326        let Some(fn_name) = addr2line.find_symbol(address) else {
327            return Ok(vec![]);
328        };
329
330        let mut fn_name = fn_name.to_string();
331        for lang in [
332            gimli::DW_LANG_Rust,
333            gimli::DW_LANG_C_plus_plus,
334            gimli::DW_LANG_C_plus_plus_03,
335            gimli::DW_LANG_C_plus_plus_11,
336            gimli::DW_LANG_C_plus_plus_14,
337        ] {
338            if let Some(demangle) = addr2line::demangle(&fn_name, lang) {
339                fn_name = demangle;
340                break;
341            }
342        }
343
344        Ok(vec![StackFrame {
345            id: get_object_reference(),
346            function_name: format!(
347                "{fn_name} @ {address:#0width$x}>",
348                width = (unwind_registers.get_address_size_bytes() * 2 + 2)
349            ),
350            source_location: None,
351            registers: unwind_registers.clone(),
352            pc: RegisterValue::from(address),
353            frame_base: None,
354            is_inlined: false,
355            local_variables: None,
356            canonical_frame_address: None,
357        }])
358    }
359
360    /// Returns a populated (resolved) [`StackFrame`] struct.
361    /// This function will also populate the `DebugInfo::VariableCache` with in scope `Variable`s for each `StackFrame`,
362    /// while taking into account the appropriate strategy for lazy-loading of variables.
363    pub(crate) fn get_stackframe_info(
364        &self,
365        memory: &mut impl MemoryInterface,
366        address: u64,
367        cfa: Option<u64>,
368        unwind_registers: &DebugRegisters,
369    ) -> Result<Vec<StackFrame>, DebugError> {
370        // When reporting the address, we format it as a hex string, with the width matching
371        // the configured size of the datatype used in the `RegisterValue` address.
372        let unknown_function = || {
373            format!(
374                "<unknown function @ {address:#0width$x}>",
375                width = (unwind_registers.get_address_size_bytes() * 2 + 2)
376            )
377        };
378
379        let Ok((unit_info, functions)) = self.get_function_dies(address) else {
380            // No function found at the given address.
381            return self.get_stackframe_from_symbols(address, unwind_registers);
382        };
383        if functions.is_empty() {
384            // No function found at the given address.
385            return self.get_stackframe_from_symbols(address, unwind_registers);
386        }
387
388        // The first function is the non-inlined function, and the rest are inlined functions.
389        // The frame base only exists for the non-inlined function, so we can reuse it for all the inlined functions.
390        let frame_base = functions[0].frame_base(
391            self,
392            memory,
393            StackFrameInfo {
394                registers: unwind_registers,
395                frame_base: None,
396                canonical_frame_address: cfa,
397            },
398        )?;
399
400        let mut frames = Vec::new();
401
402        // Handle all functions which contain further inlined functions. For
403        // these functions, the location is the call site of the inlined function.
404        for function_pair in functions.windows(2) {
405            let function_die = &function_pair[0];
406            let next_function = &function_pair[1];
407
408            let function_name = function_die
409                .function_name(self)
410                .unwrap_or_else(unknown_function);
411
412            tracing::debug!("UNWIND: Function name: {}", function_name);
413
414            assert!(next_function.is_inline());
415
416            // Calculate the call site for this function, so that we can use it later to create an additional 'callee' `StackFrame` from that PC.
417            let address_size = unit_info.unit.header.address_size() as u64;
418
419            let Some(next_function_low_pc) = next_function.low_pc() else {
420                tracing::warn!(
421                    "UNWIND: Unknown starting address for inlined function {}.",
422                    function_name
423                );
424                continue;
425            };
426
427            if !(next_function_low_pc > address_size && next_function_low_pc < u32::MAX as u64) {
428                tracing::warn!("UNWIND: Unknown call site for inlined function {function_name}.");
429                continue;
430            }
431
432            // The first instruction of the inlined function is used as the call site
433            let inlined_call_site = RegisterValue::from(next_function_low_pc);
434
435            tracing::debug!(
436                "UNWIND: Callsite for inlined function {:?}",
437                next_function.function_name(self)
438            );
439
440            let inlined_caller_source_location = next_function.inline_call_location(self);
441
442            tracing::debug!("UNWIND: Call site: {inlined_caller_source_location:?}");
443
444            // Now that we have the function_name and function_source_location, we can create the appropriate variable caches for this stack frame.
445            // Resolve the statics that belong to the compilation unit that this function is in.
446            // Next, resolve and cache the function variables.
447            let local_variables = self
448                .create_function_scope_cache(function_die, unit_info)
449                .inspect_err(|error| {
450                    tracing::error!("Could not resolve function variables. {error}. Continuing...");
451                })
452                .ok();
453
454            frames.push(StackFrame {
455                id: get_object_reference(),
456                function_name,
457                source_location: inlined_caller_source_location,
458                registers: unwind_registers.clone(),
459                pc: inlined_call_site,
460                frame_base,
461                is_inlined: function_die.is_inline(),
462                local_variables,
463                canonical_frame_address: cfa,
464            });
465        }
466
467        // Handle last function, which contains no further inlined functions
468        // `unwrap`: Checked at beginning of loop, functions must contain at least one value
469        #[expect(clippy::unwrap_used)]
470        let last_function = functions.last().unwrap();
471
472        let function_name = last_function
473            .function_name(self)
474            .unwrap_or_else(unknown_function);
475
476        let function_location = self.get_source_location(address);
477
478        // Now that we have the function_name and function_source_location, we can create the appropriate variable caches for this stack frame.
479        // Resolve and cache the function variables.
480        let local_variables =
481            self.create_function_scope_cache(last_function, unit_info)
482                .map_or_else(
483                    |error| {
484                        tracing::error!(
485                            "Could not resolve function variables. {error}. Continuing...",
486                        );
487                        None
488                    },
489                    Some,
490                );
491
492        frames.push(StackFrame {
493            id: get_object_reference(),
494            function_name,
495            source_location: function_location,
496            registers: unwind_registers.clone(),
497            pc: match unwind_registers.get_address_size_bytes() {
498                4 => RegisterValue::U32(address as u32),
499                8 => RegisterValue::U64(address),
500                _ => RegisterValue::from(address),
501            },
502            frame_base,
503            is_inlined: last_function.is_inline(),
504            local_variables,
505            canonical_frame_address: cfa,
506        });
507
508        Ok(frames)
509    }
510
511    /// Performs the logical unwind of the stack and returns a `Vec<StackFrame>`
512    /// - The first 'StackFrame' represents the frame at the current PC (program counter), and ...
513    /// - Each subsequent `StackFrame` represents the **previous or calling** `StackFrame` in the call stack.
514    /// - The majority of the work happens in the `'unwind: while` loop, where each iteration
515    ///   will create a `StackFrame` where possible, and update the `unwind_registers` to prepare for
516    ///   the next iteration.
517    ///
518    /// The unwind loop will continue until we meet one of the following conditions:
519    /// - We can no longer unwind a valid PC value to be used for the next frame.
520    /// - We encounter a LR register value of 0x0 or 0xFFFFFFFF (Arm 'Reset' value for that register).
521    /// - We can not intelligently calculate a valid LR register value from the other registers,
522    ///   or the `gimli::RegisterRule` result is a value of 0x0.
523    ///   Note: [DWARF](https://dwarfstd.org) 6.4.4 - CIE defines the return register address
524    ///   used in the `gimli::RegisterRule` tables for unwind operations.
525    ///   Theoretically, if we encounter a function that has `Undefined` `gimli::RegisterRule` for
526    ///   the return register address, it means we have reached the bottom of the stack
527    ///   OR the function is a 'no return' type of function.
528    ///   I have found actual examples (e.g. local functions) where we get `Undefined` for register
529    ///   rule when we cannot apply this logic.
530    ///   Example 1: local functions in main.rs will have LR rule as `Undefined`.
531    ///   Example 2: main()-> ! that is called from a trampoline will have a valid LR rule.
532    /// - Similarly, certain error conditions encountered in `StackFrameIterator` will also break out of the unwind loop.
533    ///
534    /// Note: In addition to populating the `StackFrame`s, this function will also
535    /// populate the `DebugInfo::VariableCache` with `Variable`s for available Registers
536    /// as well as static and function variables.
537    /// TODO: Separate logic for stackframe creation and cache population
538    pub fn unwind(
539        &self,
540        core: &mut impl MemoryInterface,
541        initial_registers: DebugRegisters,
542        exception_handler: &dyn ExceptionInterface,
543        instruction_set: Option<InstructionSet>,
544    ) -> Result<Vec<StackFrame>, probe_rs::Error> {
545        self.unwind_impl(initial_registers, core, exception_handler, instruction_set)
546    }
547
548    pub(crate) fn unwind_impl(
549        &self,
550        initial_registers: DebugRegisters,
551        memory: &mut impl MemoryInterface,
552        exception_handler: &dyn ExceptionInterface,
553        instruction_set: Option<InstructionSet>,
554    ) -> Result<Vec<StackFrame>, probe_rs::Error> {
555        let mut stack_frames = Vec::<StackFrame>::new();
556
557        let mut unwind_context = Box::new(gimli::UnwindContext::new());
558
559        let mut unwind_registers = initial_registers;
560
561        // Unwind [StackFrame]'s for as long as we can unwind a valid PC value.
562        'unwind: while let Some(frame_pc_register_value) =
563            unwind_registers.get_program_counter().and_then(|pc| {
564                if pc.is_zero() | pc.is_max_value() {
565                    None
566                } else {
567                    pc.value
568                }
569            })
570        {
571            let frame_pc = frame_pc_register_value.try_into().map_err(|error| {
572                let message = format!("Cannot convert register value for program counter to a 64-bit integer value: {error:?}");
573                probe_rs::Error::Register(message)
574            })?;
575
576            // PART 1: Construct the `StackFrame`s for the current program counter.
577            //
578            //         Multiple stack frames can be constructed if we are inside inlined functions.
579            tracing::trace!(
580                "UNWIND: Will generate `StackFrame` for function at address (PC) {frame_pc_register_value:#}"
581            );
582            let unwind_info = get_unwind_info(&mut unwind_context, &self.frame_section, frame_pc);
583
584            // Determining the frame base may need the CFA (Canonical Frame Address) to be calculated first.
585            let cfa = unwind_info
586                .as_ref()
587                .ok()
588                .and_then(|unwind_info| determine_cfa(&unwind_registers, unwind_info).ok())
589                .flatten();
590
591            // PART 1-a: Prepare the `StackFrame`s that holds the current frame information.
592            let cached_stack_frames =
593                match self.get_stackframe_info(memory, frame_pc, cfa, &unwind_registers) {
594                    Ok(cached_stack_frames) => cached_stack_frames,
595                    Err(e) => {
596                        tracing::error!("UNWIND: Unable to complete `StackFrame` information: {e}");
597                        // There is no point in continuing with the unwind, so let's get out of here.
598                        break;
599                    }
600                };
601
602            // Add the found stackframes to the list, in reverse order. `get_stackframe_info` returns the frames in
603            // the order of the most recently called function last, but the stack frames should be
604            // in the order of the most recently called function first.
605            if !cached_stack_frames.is_empty() {
606                for frame in cached_stack_frames.into_iter().rev() {
607                    if frame.is_inlined {
608                        tracing::trace!(
609                            "UNWIND: Found inlined function - name={}, pc={}",
610                            frame.function_name,
611                            frame.pc
612                        );
613                    }
614                    stack_frames.push(frame);
615                }
616            } else {
617                // We have no valid code for the current frame, so we
618                // construct a frame, using what information we have.
619                stack_frames.push(StackFrame {
620                    id: get_object_reference(),
621                    function_name: format!(
622                        "<unknown function @ {:#0width$x}>",
623                        frame_pc,
624                        width = (unwind_registers.get_address_size_bytes() * 2 + 2)
625                    ),
626                    source_location: self.get_source_location(frame_pc),
627                    registers: unwind_registers.clone(),
628                    pc: frame_pc_register_value,
629                    frame_base: None,
630                    is_inlined: false,
631                    local_variables: None,
632                    canonical_frame_address: None,
633                });
634            };
635
636            // PART 2: Setup the registers for the next iteration (a.k.a. unwind previous frame, a.k.a. "callee", in the call stack).
637            tracing::trace!("UNWIND - Preparing to unwind the registers for the previous frame.");
638
639            // Because we will be updating the `unwind_registers` with previous frame unwind info,
640            // we need to keep a copy of the current frame's registers that can be used to resolve [DWARF](https://dwarfstd.org) expressions.
641            let callee_frame_registers = unwind_registers.clone();
642
643            // PART 2-a: get the `gimli::FrameDescriptorEntry` for the program counter
644            // and then the unwind info associated with this row.
645            let unwind_info = match unwind_info {
646                Ok(unwind_info) => {
647                    tracing::trace!("UNWIND: Found unwind info for address {frame_pc:#010x}");
648                    unwind_info
649                }
650                Err(err) => {
651                    tracing::trace!(
652                        "UNWIND: Unable to find unwind info for address {frame_pc:#010x}: {err}"
653                    );
654                    if let ControlFlow::Break(error) = exception_handler.unwind_without_debuginfo(
655                        &mut unwind_registers,
656                        frame_pc,
657                        &stack_frames,
658                        instruction_set,
659                        memory,
660                    ) {
661                        if let Some(error) = error {
662                            // This is not fatal, but we cannot continue unwinding beyond the current frame.
663                            tracing::error!("{:?}", &error);
664                            if let Some(first_frame) = stack_frames.first_mut() {
665                                first_frame.function_name =
666                                    format!("{} : ERROR : {error}", first_frame.function_name);
667                            };
668                        }
669                        break 'unwind;
670                    }
671
672                    if callee_frame_registers == unwind_registers {
673                        tracing::debug!("No change, preventing infinite loop");
674                        break;
675                    }
676                    continue 'unwind;
677                }
678            };
679
680            // PART 2-b: Unwind registers for the "previous/calling" frame.
681            for debug_register in unwind_registers.0.iter_mut() {
682                // The program counter is handled later
683                if debug_register
684                    .core_register
685                    .register_has_role(RegisterRole::ProgramCounter)
686                {
687                    continue;
688                }
689
690                match unwind_register(
691                    debug_register,
692                    &callee_frame_registers,
693                    unwind_info,
694                    cfa,
695                    memory,
696                ) {
697                    Err(error) => {
698                        tracing::error!("{:?}", &error);
699                        if let Some(first_frame) = stack_frames.last_mut() {
700                            first_frame.function_name =
701                                format!("{} : ERROR: {error}", first_frame.function_name);
702                        };
703                        break 'unwind;
704                    }
705                    Ok(val) => {
706                        debug_register.value = val;
707                    }
708                };
709            }
710
711            // PART 3: Check if we entered the current frame from an exception handler.
712            // - If we are at an exception handler frame:
713            //   - Create a "handler" stackframe that can be inserted into the stack_frames list,
714            //     instead of "unknown function @ address";
715            //   - Overwrite the unwind registers with the exception context.
716            // - If for some reason we cannot determine the exception context, we silently continue with the rest of the unwind.
717            // At worst, the unwind will be able to unwind the stack to the frame of the most recent exception handler.
718            if unwind_registers
719                .get_return_address()
720                .is_some_and(|ra| ra.value.is_some())
721            {
722                match exception_handler.exception_details(memory, &unwind_registers, self) {
723                    Ok(Some(exception_info)) => {
724                        tracing::trace!(
725                            "UNWIND: Stack unwind reached an exception handler {}",
726                            exception_info.description
727                        );
728                        unwind_registers = exception_info.handler_frame.registers.clone();
729                        stack_frames.push(exception_info.handler_frame);
730                        // We have everything we need to unwind the next frame in the stack.
731                        continue 'unwind;
732                    }
733                    Ok(None) => {
734                        tracing::trace!(
735                            "UNWIND: No exception context found. Stack unwind will continue."
736                        );
737                    }
738                    Err(e) => {
739                        let message = format!(
740                            "UNWIND: Error while checking for exception context. The stack trace will not include the calling frames. : {e:?}"
741                        );
742                        tracing::warn!("{message}");
743                        stack_frames.push(StackFrame {
744                            id: get_object_reference(),
745                            function_name: message,
746                            source_location: None,
747                            registers: unwind_registers.clone(),
748                            pc: frame_pc_register_value,
749                            frame_base: None,
750                            is_inlined: false,
751                            local_variables: None,
752                            canonical_frame_address: None,
753                        });
754                        break 'unwind;
755                    }
756                };
757            }
758
759            let unwound_return_address = unwind_registers
760                .get_register_by_role(&RegisterRole::ReturnAddress)
761                .ok()
762                .and_then(|reg| reg.value);
763
764            let program_counter = unwind_registers.get_program_counter_mut().unwrap();
765
766            let Ok(current_pc) =
767                callee_frame_registers.get_register_value_by_role(&RegisterRole::ProgramCounter)
768            else {
769                let error =    probe_rs::Error::Other(
770                        "UNWIND: Tried to unwind return address value where current program counter is unknown.".to_string()
771                    );
772                tracing::error!("{:?}", &error);
773                if let Some(first_frame) = stack_frames.last_mut() {
774                    first_frame.function_name =
775                        format!("{} : ERROR: {error}", first_frame.function_name);
776                };
777                break 'unwind;
778            };
779            // NOTE: PC = Value of the unwound LR, i.e. the first instruction after the one that called this function.
780            // If both the LR and PC registers have undefined rules, this will prevent the unwind from continuing.
781            let mut register_rule_string = "PC=(unwound LR) (dwarf Undefined)".to_string();
782            program_counter.value = unwound_return_address.and_then(|return_address| {
783                unwind_program_counter_register(
784                    return_address,
785                    current_pc,
786                    instruction_set,
787                    &mut register_rule_string,
788                )
789            });
790
791            tracing::trace!(
792                "UNWIND - {:>10}: Caller: {}\tCallee: {}\tRule: {}",
793                program_counter.get_register_name(),
794                program_counter.value.unwrap_or_default(),
795                callee_frame_registers
796                    .get_register(program_counter.core_register.id)
797                    .and_then(|reg| reg.value)
798                    .unwrap_or_default(),
799                register_rule_string,
800            );
801
802            if callee_frame_registers == unwind_registers {
803                tracing::debug!("No change, preventing infinite loop");
804                break;
805            }
806        }
807
808        Ok(stack_frames)
809    }
810
811    /// Find the program counter where a breakpoint should be set,
812    /// given a source file, a line and optionally a column.
813    // TODO: Move (and fix) this to the [`InstructionSequence::for_source_location`] method.
814    #[tracing::instrument(skip_all)]
815    pub fn get_breakpoint_location(
816        &self,
817        path: TypedPath,
818        line: u64,
819        column: Option<u64>,
820    ) -> Result<VerifiedBreakpoint, DebugError> {
821        tracing::debug!(
822            "Looking for breakpoint location for {}:{}:{}",
823            path.display(),
824            line,
825            column
826                .map(|c| c.to_string())
827                .unwrap_or_else(|| "-".to_owned())
828        );
829        VerifiedBreakpoint::for_source_location(self, path, line, column)
830    }
831
832    /// Get the path for an entry in a line program header, using the compilation unit's directory and file entries.
833    // TODO: Determine if it is necessary to navigate the include directories to find the file absolute path for C files.
834    pub(crate) fn get_path(
835        &self,
836        unit: &gimli::read::Unit<DwarfReader>,
837        file_index: u64,
838    ) -> Option<TypedPathBuf> {
839        let line_program = unit.line_program.as_ref()?;
840        let header = line_program.header();
841        let Some(file_entry) = header.file(file_index) else {
842            tracing::warn!(
843                "Unable to extract file entry for file_index {:?}.",
844                file_index
845            );
846            return None;
847        };
848        let file_name_attr_string = self.dwarf.attr_string(unit, file_entry.path_name()).ok()?;
849        let name_path = from_utf8(&file_name_attr_string).ok()?;
850
851        let dir_name_attr_string = file_entry
852            .directory(header)
853            .and_then(|dir| self.dwarf.attr_string(unit, dir).ok());
854
855        let dir_path = dir_name_attr_string.and_then(|dir_name| {
856            from_utf8(&dir_name)
857                .ok()
858                .map(|p| TypedPath::derive(p).to_path_buf())
859        });
860
861        let mut combined_path = match dir_path {
862            Some(dir_path) => dir_path.join(name_path),
863            None => TypedPath::derive(name_path).to_path_buf(),
864        };
865
866        if combined_path.is_relative() {
867            let comp_dir = unit
868                .comp_dir
869                .as_ref()
870                .map(|dir| from_utf8(dir))
871                .transpose()
872                .ok()?
873                .map(TypedPath::derive);
874            if let Some(comp_dir) = comp_dir {
875                combined_path = comp_dir.join(&combined_path);
876            }
877        }
878
879        Some(combined_path)
880    }
881
882    pub(crate) fn find_file_and_directory(
883        &self,
884        unit: &gimli::read::Unit<DwarfReader>,
885        file_index: u64,
886    ) -> Option<TypedPathBuf> {
887        let combined_path = self.get_path(unit, file_index)?;
888
889        Some(combined_path)
890    }
891
892    // Return the compilation unit that contains the given address
893    pub(crate) fn compile_unit_info(
894        &self,
895        address: u64,
896    ) -> Result<&super::unit_info::UnitInfo, DebugError> {
897        for header in &self.unit_infos {
898            match self.dwarf.unit_ranges(&header.unit) {
899                Ok(mut ranges) => {
900                    while let Ok(Some(range)) = ranges.next() {
901                        if range.contains(address) {
902                            return Ok(header);
903                        }
904                    }
905                }
906                Err(_) => continue,
907            };
908        }
909        Err(DebugError::WarnAndContinue {
910            message: format!(
911                "No debug information available for the instruction at {address:#010x}. Please consider using instruction level stepping."
912            ),
913        })
914    }
915
916    /// Search accross all compilation untis, and retrive the DIEs for the function containing the given address.
917    /// This is distinct from [`UnitInfo::get_function_dies`] in that it will search all compilation units.
918    /// - The first entry in the vector will be the outermost function containing the address.
919    /// - If the address is inlined, the innermost function will be the last entry in the vector.
920    pub(crate) fn get_function_dies(
921        &self,
922        address: u64,
923    ) -> Result<(&UnitInfo, Vec<FunctionDie<'_>>), DebugError> {
924        for unit_info in &self.unit_infos {
925            let function_dies = unit_info.get_function_dies(self, address)?;
926
927            if !function_dies.is_empty() {
928                return Ok((unit_info, function_dies));
929            }
930        }
931        Err(DebugError::Other(format!(
932            "No function DIE's at address {address:#x}."
933        )))
934    }
935
936    /// Look up the DIE reference for the given attribute, if it exists.
937    pub(crate) fn resolve_die_reference<'debug_info, 'unit_info>(
938        &'debug_info self,
939        attribute: gimli::DwAt,
940        die: &Die,
941        unit_info: &'unit_info UnitInfo,
942    ) -> Option<Die<'debug_info, 'debug_info>>
943    where
944        'unit_info: 'debug_info,
945    {
946        let attr = die.attr(attribute).ok().flatten()?;
947
948        self.resolve_die_reference_with_unit(&attr, unit_info)
949            .ok()
950            .map(|(_, die)| die)
951    }
952
953    /// The program binary's (and core's) endianness.
954    pub fn endianness(&self) -> RunTimeEndian {
955        self.endianness
956    }
957
958    /// Returns the UnitInfo and DIE for the given attribute.
959    pub(crate) fn resolve_die_reference_with_unit<'debug_info, 'unit_info>(
960        &'debug_info self,
961        attr: &gimli::Attribute<GimliReader>,
962        unit_info: &'unit_info UnitInfo,
963    ) -> Result<(&'debug_info UnitInfo, Die<'debug_info, 'debug_info>), DebugError>
964    where
965        'unit_info: 'debug_info,
966    {
967        match attr.value() {
968            gimli::AttributeValue::UnitRef(unit_ref) => {
969                Ok((unit_info, unit_info.unit.entry(unit_ref)?))
970            }
971            gimli::AttributeValue::DebugInfoRef(offset) => {
972                for unit_info in &self.unit_infos {
973                    let Some(unit_offset) = offset.to_unit_offset(&unit_info.unit.header) else {
974                        continue;
975                    };
976
977                    let entry = unit_info.unit.entry(unit_offset).map_err(|error| {
978                        DebugError::Other(format!(
979                            "Error reading DIE at debug info offset {:#x} : {}",
980                            offset.0, error
981                        ))
982                    })?;
983                    return Ok((unit_info, entry));
984                }
985
986                Err(DebugError::Other(format!(
987                    "Unable to find unit info for debug info offset {:#x}",
988                    offset.0
989                )))
990            }
991            other_attribute_value => Err(DebugError::Other(format!(
992                "Unimplemented attribute value {other_attribute_value:?}"
993            ))),
994        }
995    }
996}
997
998/// Uses the [`TypedPathBuf::normalize`] function to normalize both paths before comparing them
999pub(crate) fn canonical_path_eq(primary_path: TypedPath, secondary_path: TypedPath) -> bool {
1000    primary_path.normalize() == secondary_path.normalize()
1001}
1002
1003/// Get a handle to the [`gimli::UnwindTableRow`] for this call frame, so that we can reference it to unwind register values.
1004pub fn get_unwind_info<'a>(
1005    unwind_context: &'a mut UnwindContext<GimliReaderOffset>,
1006    frame_section: &DebugFrame<DwarfReader>,
1007    frame_program_counter: u64,
1008) -> Result<&'a gimli::UnwindTableRow<GimliReaderOffset>, DebugError> {
1009    let transform_error = |error| {
1010        DebugError::Other(format!(
1011            "UNWIND: Error reading FrameDescriptorEntry at PC={frame_program_counter:x} : {error}"
1012        ))
1013    };
1014
1015    let unwind_bases = BaseAddresses::default();
1016
1017    let frame_descriptor_entry = frame_section
1018        .fde_for_address(
1019            &unwind_bases,
1020            frame_program_counter,
1021            DebugFrame::cie_from_offset,
1022        )
1023        .map_err(transform_error)?;
1024
1025    frame_descriptor_entry
1026        .unwind_info_for_address(
1027            frame_section,
1028            &unwind_bases,
1029            unwind_context,
1030            frame_program_counter,
1031        )
1032        .map_err(transform_error)
1033}
1034
1035/// Determines the CFA (canonical frame address) for the current [`gimli::UnwindTableRow`], using the current register values.
1036pub fn determine_cfa<R: gimli::ReaderOffset>(
1037    unwind_registers: &DebugRegisters,
1038    unwind_info: &UnwindTableRow<R>,
1039) -> Result<Option<u64>, probe_rs::Error> {
1040    let gimli::CfaRule::RegisterAndOffset { register, offset } = unwind_info.cfa() else {
1041        unimplemented!()
1042    };
1043
1044    let reg_val = unwind_registers
1045        .get_register_by_dwarf_id(register.0)
1046        .and_then(|register| register.value);
1047
1048    let cfa = match reg_val {
1049        None => {
1050            tracing::error!(
1051                "UNWIND: `StackFrameIterator` unable to determine the unwind CFA: Missing value of register {}",
1052                register.0
1053            );
1054            None
1055        }
1056
1057        Some(reg_val) if reg_val.is_zero() => {
1058            // If we encounter this rule for CFA, it implies the scenario depends on a FP/frame pointer to continue successfully.
1059            // Therefore, if reg_val is zero (i.e. FP is zero), then we do not have enough information to determine the CFA by rule.
1060            tracing::trace!(
1061                "UNWIND: Stack unwind complete - The FP register value unwound to a value of zero."
1062            );
1063            None
1064        }
1065
1066        Some(reg_val) => {
1067            let unwind_cfa = add_to_address(
1068                reg_val.try_into()?,
1069                *offset,
1070                unwind_registers.get_address_size_bytes(),
1071            );
1072            tracing::trace!(
1073                "UNWIND - CFA : {:#010x}\tRule: {:?}",
1074                unwind_cfa,
1075                unwind_info.cfa()
1076            );
1077            Some(unwind_cfa)
1078        }
1079    };
1080
1081    Ok(cfa)
1082}
1083
1084/// Unwind the program counter for the caller frame, using the LR value from the callee frame.
1085pub fn unwind_pc_without_debuginfo(
1086    unwind_registers: &mut DebugRegisters,
1087    _frame_pc: u64,
1088    instruction_set: Option<probe_rs::InstructionSet>,
1089) -> ControlFlow<Option<DebugError>> {
1090    // For non exception frames, we cannot do stack unwinding if we do not have debug info.
1091    // However, there is one case where we can continue. When the frame registers have a valid
1092    // return address/LR value, we can use the LR value to calculate the PC for the calling frame.
1093    // The current logic will then use that PC to get the next frame's unwind info, and if that exists,
1094    // we will be able to continue unwinding.
1095    // If the calling frame has no debug info, then the unwinding will end with that frame.
1096    let callee_frame_registers = unwind_registers.clone();
1097    let unwound_return_address: Option<RegisterValue> = unwind_registers
1098        .get_return_address()
1099        .and_then(|lr| lr.value);
1100
1101    // This will update the program counter in the `unwind_registers` with the PC value calculated from the LR value.
1102    if let Some(calling_pc) = unwind_registers.get_program_counter_mut() {
1103        let mut register_rule_string = "PC=(unwound LR) (dwarf Undefined)".to_string();
1104
1105        let Ok(current_pc) =
1106            callee_frame_registers.get_register_value_by_role(&RegisterRole::ProgramCounter)
1107        else {
1108            return ControlFlow::Break(
1109                Some(probe_rs::Error::Other(
1110                    "UNWIND: Tried to unwind return address value where current program counter is unknown.".to_string()
1111                ).into())
1112            );
1113        };
1114        // NOTE: PC = Value of the unwound LR, i.e. the first instruction after the one that called this function.
1115        // If both the LR and PC registers have undefined rules, this will prevent the unwind from continuing.
1116        calling_pc.value = unwound_return_address.and_then(|return_address| {
1117            unwind_program_counter_register(
1118                return_address,
1119                current_pc,
1120                instruction_set,
1121                &mut register_rule_string,
1122            )
1123        });
1124    }
1125
1126    ControlFlow::Continue(())
1127}
1128
1129/// A per_register unwind, applying register rules and updating the [`registers::DebugRegister`] value as appropriate, before returning control to the calling function.
1130pub fn unwind_register(
1131    debug_register: &super::DebugRegister,
1132    // The callee_frame_registers are used to lookup values and never updated.
1133    callee_frame_registers: &DebugRegisters,
1134    unwind_info: &gimli::UnwindTableRow<GimliReaderOffset>,
1135    unwind_cfa: Option<u64>,
1136    memory: &mut dyn MemoryInterface,
1137) -> Result<Option<RegisterValue>, probe_rs::Error> {
1138    use gimli::read::RegisterRule;
1139
1140    // If we do not have unwind info, or there is no register rule, then use UnwindRule::Undefined.
1141    let register_rule = debug_register
1142        .dwarf_id
1143        .map(|register_position| unwind_info.register(gimli::Register(register_position)))
1144        .unwrap_or(RegisterRule::Undefined);
1145
1146    unwind_register_using_rule(
1147        debug_register.core_register,
1148        callee_frame_registers,
1149        unwind_cfa,
1150        memory,
1151        register_rule,
1152    )
1153}
1154
1155fn unwind_register_using_rule(
1156    debug_register: &probe_rs::CoreRegister,
1157    callee_frame_registers: &DebugRegisters,
1158    unwind_cfa: Option<u64>,
1159    memory: &mut dyn MemoryInterface,
1160    register_rule: gimli::RegisterRule<usize>,
1161) -> Result<Option<RegisterValue>, probe_rs::Error> {
1162    use gimli::read::RegisterRule;
1163
1164    let mut register_rule_string = format!("{register_rule:?}");
1165
1166    let new_value = match register_rule {
1167        RegisterRule::Undefined => {
1168            // In many cases, the DWARF has `Undefined` rules for variables like frame pointer, program counter, etc.,
1169            // so we hard-code some rules here to make sure unwinding can continue. If there is a valid rule, it will bypass these hardcoded ones.
1170            match &debug_register {
1171                fp if fp.register_has_role(RegisterRole::FramePointer) => {
1172                    register_rule_string = "FP=CFA (dwarf Undefined)".to_string();
1173                    unwind_cfa.map(|unwind_cfa| {
1174                        if fp.data_type == RegisterDataType::UnsignedInteger(32) {
1175                            RegisterValue::U32(unwind_cfa as u32 & !0b11)
1176                        } else {
1177                            RegisterValue::U64(unwind_cfa & !0b11)
1178                        }
1179                    })
1180                }
1181                sp if sp.register_has_role(RegisterRole::StackPointer) => {
1182                    // NOTE: [ARMv7-M Architecture Reference Manual](https://developer.arm.com/documentation/ddi0403/ee), Section B.1.4.1: Treat bits [1:0] as `Should be Zero or Preserved`
1183                    // - Applying this logic to RISC-V has no adverse effects, since all incoming addresses are already 32-bit aligned.
1184                    register_rule_string = "SP=CFA (dwarf Undefined)".to_string();
1185                    unwind_cfa.map(|unwind_cfa| {
1186                        if sp.data_type == RegisterDataType::UnsignedInteger(32) {
1187                            RegisterValue::U32(unwind_cfa as u32 & !0b11)
1188                        } else {
1189                            RegisterValue::U64(unwind_cfa & !0b11)
1190                        }
1191                    })
1192                }
1193                lr if lr.register_has_role(RegisterRole::ReturnAddress) => {
1194                    let Ok(current_pc) = callee_frame_registers
1195                        .get_register_value_by_role(&RegisterRole::ProgramCounter)
1196                    else {
1197                        return Err(
1198                            probe_rs::Error::Other(
1199                                "UNWIND: Tried to unwind return address value where current program counter is unknown.".to_string()
1200                            )
1201                        );
1202                    };
1203                    let Some(current_lr) = callee_frame_registers
1204                        .get_register_by_role(&RegisterRole::ReturnAddress)
1205                        .ok()
1206                        .and_then(|lr| lr.value)
1207                    else {
1208                        return Err(
1209                            probe_rs::Error::Other(
1210                                "UNWIND: Tried to unwind return address value where current return address is unknown.".to_string()
1211                            )
1212                        );
1213                    };
1214
1215                    let current_lr_value: u64 = current_lr.try_into()?;
1216
1217                    if current_pc == current_lr_value & !0b1 {
1218                        // If the previous PC is the same as the half-word aligned current LR,
1219                        // we have no way of inferring the previous frames LR until we have the PC.
1220                        register_rule_string = "LR=Undefined (dwarf Undefined)".to_string();
1221                        None
1222                    } else {
1223                        // We can attempt to continue unwinding with the current LR value, e.g. inlined code.
1224                        register_rule_string = "LR=Current LR (dwarf Undefined)".to_string();
1225                        Some(current_lr)
1226                    }
1227                }
1228                pc if pc.register_has_role(RegisterRole::ProgramCounter) => {
1229                    unreachable!("The program counter is handled separately")
1230                }
1231                other_register => {
1232                    // If the the register rule was not specified, then we either carry the previous value forward,
1233                    // or we clear the register value, depending on the architecture and register type.
1234                    match other_register.unwind_rule {
1235                        UnwindRule::Preserve => {
1236                            register_rule_string = "Preserve".to_string();
1237                            callee_frame_registers
1238                                .get_register(other_register.id)
1239                                .and_then(|reg| reg.value)
1240                        }
1241                        UnwindRule::Clear => {
1242                            register_rule_string = "Clear".to_string();
1243                            None
1244                        }
1245                        UnwindRule::SpecialRule => {
1246                            // When no DWARF rules are available, and it is not a special register like PC, SP, FP, etc.,
1247                            // we will clear the value. It is possible it might have its value set later if
1248                            // exception frame information is available.
1249                            register_rule_string = "Clear (no unwind rules specified)".to_string();
1250                            None
1251                        }
1252                    }
1253                }
1254            }
1255        }
1256
1257        RegisterRule::SameValue => callee_frame_registers
1258            .get_register(debug_register.id)
1259            .and_then(|reg| reg.value),
1260
1261        RegisterRule::Offset(address_offset) => {
1262            // "The previous value of this register is saved at the address CFA+N where CFA is the current CFA value and N is a signed offset"
1263            let Some(unwind_cfa) = unwind_cfa else {
1264                return Err(probe_rs::Error::Other(
1265                    "UNWIND: Tried to unwind `RegisterRule` at CFA = None.".to_string(),
1266                ));
1267            };
1268            let address_size = callee_frame_registers.get_address_size_bytes();
1269            let previous_frame_register_address =
1270                add_to_address(unwind_cfa, address_offset, address_size);
1271
1272            register_rule_string = format!("CFA {register_rule:?}");
1273
1274            // TODO: This should be the size of the register, not the address size.
1275            let result = match address_size {
1276                4 => {
1277                    let mut buff = [0u8; 4];
1278                    memory
1279                        .read(previous_frame_register_address, &mut buff)
1280                        .map(|_| RegisterValue::U32(u32::from_le_bytes(buff)))
1281                }
1282                8 => {
1283                    let mut buff = [0u8; 8];
1284                    memory
1285                        .read(previous_frame_register_address, &mut buff)
1286                        .map(|_| RegisterValue::U64(u64::from_le_bytes(buff)))
1287                }
1288                _ => {
1289                    return Err(Error::Other(format!(
1290                        "UNWIND: Address size {address_size} not supported."
1291                    )));
1292                }
1293            };
1294
1295            match result {
1296                Ok(register_value) => Some(register_value),
1297                Err(error) => {
1298                    tracing::error!(
1299                        "UNWIND: Rule: Offset {} from address {:#010x}",
1300                        address_offset,
1301                        unwind_cfa
1302                    );
1303
1304                    return Err(Error::Other(format!(
1305                        "UNWIND: Failed to read value for register {} from address {} ({} bytes): {}",
1306                        debug_register,
1307                        RegisterValue::from(previous_frame_register_address),
1308                        4,
1309                        error
1310                    )));
1311                }
1312            }
1313        }
1314        //TODO: Implement the remainder of these `RegisterRule`s
1315        _ => unimplemented!(),
1316    };
1317
1318    tracing::trace!(
1319        "UNWIND - {:>10}: Caller: {}\tCallee: {}\tRule: {}",
1320        debug_register,
1321        new_value.unwrap_or_default(),
1322        callee_frame_registers
1323            .get_register(debug_register.id)
1324            .and_then(|reg| reg.value)
1325            .unwrap_or_default(),
1326        register_rule_string,
1327    );
1328    Ok(new_value)
1329}
1330
1331/// Helper function to determine the program counter value for the previous frame.
1332fn unwind_program_counter_register(
1333    return_address: RegisterValue,
1334    current_pc: u64,
1335    instruction_set: Option<InstructionSet>,
1336    register_rule_string: &mut String,
1337) -> Option<RegisterValue> {
1338    if return_address.is_max_value() || return_address.is_zero() {
1339        tracing::warn!(
1340            "No reliable return address is available, so we cannot determine the program counter to unwind the previous frame."
1341        );
1342        return None;
1343    }
1344
1345    match return_address {
1346        RegisterValue::U32(return_address) => {
1347            match instruction_set {
1348                Some(InstructionSet::Thumb2) => {
1349                    // NOTE: [ARMv7-M Architecture Reference Manual](https://developer.arm.com/documentation/ddi0403/ee), Section A5.1.2:
1350                    //
1351                    // We have to clear the last bit to ensure the PC is half-word aligned. (on ARM architecture,
1352                    // when in Thumb state for certain instruction types will set the LSB to 1)
1353                    *register_rule_string =
1354                        "PC=(unwound (LR - 2) & !0b1) (dwarf Undefined)".to_string();
1355                    Some(RegisterValue::U32((return_address - 2) & !0b1))
1356                }
1357                Some(InstructionSet::RV32C) => {
1358                    *register_rule_string = "PC=(unwound x1 - 2) (dwarf Undefined)".to_string();
1359                    Some(RegisterValue::U32(return_address - 2))
1360                }
1361                Some(InstructionSet::RV32) => {
1362                    *register_rule_string = "PC=(unwound x1 - 4) (dwarf Undefined)".to_string();
1363                    Some(RegisterValue::U32(return_address - 4))
1364                }
1365                Some(InstructionSet::Xtensa) => {
1366                    let upper_bits = (current_pc as u32) & 0xC000_0000;
1367                    *register_rule_string = "PC=(unwound x0 - 3) (dwarf Undefined)".to_string();
1368                    Some(RegisterValue::U32(
1369                        (return_address & 0x3FFF_FFFF | upper_bits) - 3,
1370                    ))
1371                }
1372                _ => Some(RegisterValue::U32(return_address)),
1373            }
1374        }
1375        RegisterValue::U64(return_address) => Some(RegisterValue::U64(return_address)),
1376        RegisterValue::U128(_) => {
1377            tracing::warn!("128 bit address space not supported");
1378            None
1379        }
1380    }
1381}
1382
1383/// Helper function to handle adding a signed offset to a [`RegisterValue`] address.
1384/// The numerical overflow is handled based on the byte size (`address_size_in_bytes` parameter  )
1385/// of the [`RegisterValue`], as opposed to just the datatype of the `address` parameter.
1386/// In the case of unwinding stack frame register values, it makes no sense to wrap,
1387/// because it will result in invalid register address reads.
1388/// Instead, when we detect over/underflow, we return an address value of 0x0,
1389/// which will trigger a graceful (and logged) end of a stack unwind.
1390fn add_to_address(address: u64, offset: i64, address_size_in_bytes: usize) -> u64 {
1391    match address_size_in_bytes {
1392        4 => {
1393            if offset >= 0 {
1394                (address as u32)
1395                    .checked_add(offset as u32)
1396                    .map(u64::from)
1397                    .unwrap_or(0x0)
1398            } else {
1399                (address as u32).saturating_sub(offset.unsigned_abs() as u32) as u64
1400            }
1401        }
1402        8 => {
1403            if offset >= 0 {
1404                address.checked_add(offset as u64).unwrap_or(0x0)
1405            } else {
1406                address.saturating_sub(offset.unsigned_abs())
1407            }
1408        }
1409        _ => {
1410            panic!(
1411                "UNWIND: Address size {address_size_in_bytes} not supported.  Please report this as a bug."
1412            );
1413        }
1414    }
1415}
1416
1417#[cfg(test)]
1418mod test {
1419    use crate::{
1420        DebugInfo, DebugRegister, DebugRegisters,
1421        exception_handling::{
1422            armv6m::ArmV6MExceptionHandler, armv7m::ArmV7MExceptionHandler,
1423            exception_handler_for_core,
1424        },
1425        stack_frame::{StackFrameInfo, TestFormatter},
1426    };
1427
1428    use gimli::RegisterRule;
1429    use probe_rs::{
1430        CoreDump, RegisterValue,
1431        architecture::arm::core::registers::cortex_m::{self, CORTEX_M_CORE_REGISTERS},
1432        test::MockMemory,
1433    };
1434    use std::path::{Path, PathBuf};
1435    use test_case::test_case;
1436
1437    use super::unwind_register_using_rule;
1438
1439    /// Get the full path to a file in the `tests` directory.
1440    fn get_path_for_test_files(relative_file: &str) -> PathBuf {
1441        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1442        path.push("tests");
1443        path.push(relative_file);
1444        path
1445    }
1446
1447    /// Load the DebugInfo from the `elf_file` for the test.
1448    /// `elf_file` should be the name of a file(or relative path) in the `tests` directory.
1449    fn load_test_elf_as_debug_info(elf_file: &str) -> DebugInfo {
1450        let path = get_path_for_test_files(elf_file);
1451        DebugInfo::from_file(&path).unwrap_or_else(|err: crate::DebugError| {
1452            panic!("Failed to open file {}: {:?}", path.display(), err)
1453        })
1454    }
1455
1456    #[test]
1457    fn unwinding_first_instruction_after_exception() {
1458        let debug_info = load_test_elf_as_debug_info("exceptions");
1459
1460        // Registers:
1461        // R0        : 0x00000001
1462        // R1        : 0x2001ffcf
1463        // R2        : 0x20000044
1464        // R3        : 0x20000044
1465        // R4        : 0x00000000
1466        // R5        : 0x00000000
1467        // R6        : 0x00000000
1468        // R7        : 0x2001fff0
1469        // R8        : 0x00000000
1470        // R9        : 0x00000000
1471        // R10       : 0x00000000
1472        // R11       : 0x00000000
1473        // R12       : 0x00000000
1474        // R13       : 0x2001ffd0
1475        // R14       : 0xfffffff9
1476        // R15       : 0x00000182
1477        // MSP       : 0x2001ffd0
1478        // PSP       : 0x00000000
1479        // XPSR      : 0x2100000b
1480        // EXTRA     : 0x00000000
1481        // FPSCR     : 0x00000000
1482
1483        let values: Vec<_> = [
1484            0x00000001, // R0
1485            0x2001ffcf, // R1
1486            0x20000044, // R2
1487            0x20000044, // R3
1488            0x00000000, // R4
1489            0x00000000, // R5
1490            0x00000000, // R6
1491            0x2001fff0, // R7
1492            0x00000000, // R8
1493            0x00000000, // R9
1494            0x00000000, // R10
1495            0x00000000, // R11
1496            0x00000000, // R12
1497            0x2001ffd0, // R13
1498            0xfffffff9, // R14
1499            0x00000182, // R15
1500            0x2001ffd0, // MSP
1501            0x00000000, // PSP
1502            0x2100000b, // XPSR
1503        ]
1504        .into_iter()
1505        .enumerate()
1506        .map(|(id, r)| DebugRegister {
1507            dwarf_id: Some(id as u16),
1508            core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1509            value: Some(RegisterValue::U32(r)),
1510        })
1511        .collect();
1512
1513        let regs = DebugRegisters(values);
1514
1515        let expected_regs = regs.clone();
1516
1517        let mut mocked_mem = MockMemory::new();
1518
1519        // Stack:
1520        // 0x2001ffd0 = 0x00000001
1521        // 0x2001ffd4 = 0x2001ffcf
1522        // 0x2001ffd8 = 0x20000044
1523        // 0x2001ffdc = 0x20000044
1524        // 0x2001ffe0 = 0x00000000
1525        // 0x2001ffe4 = 0x0000017f
1526        // 0x2001ffe8 = 0x00000180
1527        // 0x2001ffec = 0x21000000
1528        // 0x2001fff0 = 0x2001fff8
1529        // 0x2001fff4 = 0x00000161
1530        // 0x2001fff8 = 0x00000000
1531        // 0x2001fffc = 0x0000013d
1532
1533        mocked_mem.add_word_range(
1534            0x2001_ffd0,
1535            &[
1536                0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000, 0x0000017f, 0x00000180,
1537                0x21000000, 0x2001fff8, 0x00000161, 0x00000000, 0x0000013d,
1538            ],
1539        );
1540
1541        let exception_handler = Box::new(ArmV6MExceptionHandler {});
1542
1543        let frames = debug_info
1544            .unwind_impl(
1545                regs,
1546                &mut mocked_mem,
1547                exception_handler.as_ref(),
1548                Some(probe_rs_target::InstructionSet::Thumb2),
1549            )
1550            .unwrap();
1551
1552        let first_frame = &frames[0];
1553
1554        assert_eq!(first_frame.pc, RegisterValue::U32(0x00000182));
1555
1556        assert_eq!(
1557            first_frame.function_name,
1558            "__cortex_m_rt_SVCall_trampoline".to_string()
1559        );
1560
1561        assert_eq!(first_frame.registers, expected_regs);
1562
1563        let next_frame = &frames[1];
1564        assert_eq!(next_frame.function_name, "SVC");
1565        assert_eq!(next_frame.pc, RegisterValue::U32(0x0000017f));
1566
1567        // Expected stack frame(s):
1568        // Frame 0: __cortex_m_rt_SVCall_trampoline @ 0x00000182
1569        //        /home/dominik/code/probe-rs/probe-rs-repro/nrf/exceptions/src/main.rs:22:1
1570        //
1571        // <--- A frame seems to be missing here, to indicate the exception entry
1572        //
1573        // Frame 1: __cortex_m_rt_main @ 0x00000180   (<--- This should be 0x17e). See the doc comment
1574        // on probe_rs::architecture::arm::core::exception_handling::armv6m_armv7m_shared::EXCEPTION_STACK_REGISTERS
1575        // for the explanation of why this is the case.
1576        //        /home/dominik/code/probe-rs/probe-rs-repro/nrf/exceptions/src/main.rs:19:5
1577        // Frame 2: __cortex_m_rt_main_trampoline @ 0x00000160
1578        //        /home/dominik/code/probe-rs/probe-rs-repro/nrf/exceptions/src/main.rs:11:1
1579        // Frame 3: memmove @ 0x0000013c
1580        // Frame 4: memmove @ 0x0000013c
1581
1582        // Registers in frame 1:
1583        // R0        : 0x00000001
1584        // R1        : 0x2001ffcf
1585        // R2        : 0x20000044
1586        // R3        : 0x20000044
1587        // R4        : 0x00000000
1588        // R5        : 0x00000000
1589        // R6        : 0x00000000
1590        // R7        : 0x2001fff0
1591        // R8        : 0x00000000
1592        // R9        : 0x00000000
1593        // R10       : 0x00000000
1594        // R11       : 0x00000000
1595        // R12       : 0x00000000
1596        // R13       : 0x2001fff0
1597        // R14       : 0x0000017f
1598        // R15       : 0x0000017e
1599        // MSP       : 0x2001fff0
1600        // PSP       : 0x00000000
1601        // XPSR      : 0x21000000
1602        // EXTRA     : 0x00000000
1603        // XPSR      : 0x21000000
1604    }
1605
1606    #[test]
1607    fn unwinding_in_exception_handler() {
1608        let debug_info = load_test_elf_as_debug_info("exceptions");
1609
1610        // Registers:
1611        // R0        : 0x00000001
1612        // R1        : 0x2001ff9f
1613        // R2        : 0x20000047
1614        // R3        : 0x20000047
1615        // R4        : 0x00000000
1616        // R5        : 0x00000000
1617        // R6        : 0x00000000
1618        // R7        : 0x2001ffc0
1619        // R8        : 0x00000000
1620        // R9        : 0x00000000
1621        // R10       : 0x00000000
1622        // R11       : 0x00000000
1623        // R12       : 0x00000000
1624        // R13       : 0x2001ffc0
1625        // R14       : 0x0000042f
1626        // R15       : 0x000001a4
1627        // MSP       : 0x2001ffc0
1628        // PSP       : 0x00000000
1629        // XPSR      : 0x2100000b
1630        // EXTRA     : 0x00000000
1631
1632        let values: Vec<_> = [
1633            0x00000001, // R0
1634            0x2001ff9f, // R1
1635            0x20000047, // R2
1636            0x20000047, // R3
1637            0x00000000, // R4
1638            0x00000000, // R5
1639            0x00000000, // R6
1640            0x2001ffc0, // R7
1641            0x00000000, // R8
1642            0x00000000, // R9
1643            0x00000000, // R10
1644            0x00000000, // R11
1645            0x00000000, // R12
1646            0x2001ffc0, // R13
1647            0x0000042f, // R14
1648            0x000001a4, // R15
1649            0x2001ffc0, // MSP
1650            0x00000000, // PSP
1651            0x2100000b, // XPSR
1652        ]
1653        .into_iter()
1654        .enumerate()
1655        .map(|(id, r)| DebugRegister {
1656            dwarf_id: Some(id as u16),
1657            core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1658            value: Some(RegisterValue::U32(r)),
1659        })
1660        .collect();
1661
1662        let regs = DebugRegisters(values);
1663
1664        let mut dummy_mem = MockMemory::new();
1665
1666        // Stack:
1667        // 0x2001ffc0 = 0x2001ffc8
1668        // 0x2001ffc4 = 0x0000018b
1669        // 0x2001ffc8 = 0x2001fff0
1670        // 0x2001ffcc = 0xfffffff9
1671        // 0x2001ffd0 = 0x00000001
1672        // 0x2001ffd4 = 0x2001ffcf
1673        // 0x2001ffd8 = 0x20000044
1674        // 0x2001ffdc = 0x20000044
1675        // 0x2001ffe0 = 0x00000000
1676        // 0x2001ffe4 = 0x0000017f
1677        // 0x2001ffe8 = 0x00000180
1678        // 0x2001ffec = 0x21000000
1679        // 0x2001fff0 = 0x2001fff8
1680        // 0x2001fff4 = 0x00000161
1681        // 0x2001fff8 = 0x00000000
1682        // 0x2001fffc = 0x0000013d
1683
1684        dummy_mem.add_word_range(
1685            0x2001_ffc0,
1686            &[
1687                0x2001ffc8, 0x0000018b, 0x2001fff0, 0xfffffff9, 0x00000001, 0x2001ffcf, 0x20000044,
1688                0x20000044, 0x00000000, 0x0000017f, 0x00000180, 0x21000000, 0x2001fff8, 0x00000161,
1689                0x00000000, 0x0000013d,
1690            ],
1691        );
1692
1693        let exception_handler = Box::new(ArmV6MExceptionHandler {});
1694
1695        let frames = debug_info
1696            .unwind_impl(
1697                regs,
1698                &mut dummy_mem,
1699                exception_handler.as_ref(),
1700                Some(probe_rs_target::InstructionSet::Thumb2),
1701            )
1702            .unwrap();
1703
1704        assert_eq!(frames[0].pc, RegisterValue::U32(0x000001a4));
1705
1706        assert_eq!(
1707            frames[1].function_name,
1708            "__cortex_m_rt_SVCall_trampoline".to_string()
1709        );
1710
1711        assert_eq!(frames[1].pc, RegisterValue::U32(0x00000188)); // <-- This is the instruction for the jump into the topmost frame.
1712
1713        // The PC value in the exception data
1714        // depends on the exception type, and for some exceptions, it will
1715        // be the address of the instruction that caused the exception, while for other exceptions
1716        // it will be the address of the next instruction after the instruction that caused the exception.
1717        // See: https://developer.arm.com/documentation/ddi0403/d/System-Level-Architecture/System-Level-Programmers--Model/ARMv7-M-exception-model/Exception-entry-behavior?lang=en
1718        assert_eq!(
1719            frames[1]
1720                .registers
1721                .get_register(probe_rs::RegisterId(7))
1722                .and_then(|r| r.value),
1723            Some(RegisterValue::U32(0x2001ffc8))
1724        );
1725
1726        let printed_backtrace = frames
1727            .into_iter()
1728            .map(|f| TestFormatter(&f).to_string())
1729            .collect::<Vec<String>>()
1730            .join("");
1731
1732        insta::assert_snapshot!(printed_backtrace);
1733    }
1734
1735    #[test]
1736    fn unwinding_in_exception_trampoline() {
1737        let debug_info = load_test_elf_as_debug_info("exceptions");
1738
1739        // Registers:
1740        // R0        : 0x00000001
1741        // R1        : 0x2001ffcf
1742        // R2        : 0x20000044
1743        // R3        : 0x20000044
1744        // R4        : 0x00000000
1745        // R5        : 0x00000000
1746        // R6        : 0x00000000
1747        // R7        : 0x2001ffc8
1748        // R8        : 0x00000000
1749        // R9        : 0x00000000
1750        // R10       : 0x00000000
1751        // R11       : 0x00000000
1752        // R12       : 0x00000000
1753        // R13       : 0x2001ffc8
1754        // R14       : 0x0000018B
1755        // R15       : 0x0000018A
1756        // MSP       : 0x2001ffc8
1757        // PSP       : 0x00000000
1758        // XPSR      : 0x2100000b
1759        // EXTRA     : 0x00000000
1760
1761        let values: Vec<_> = [
1762            0x00000001, // R0
1763            0x2001ffcf, // R1
1764            0x20000044, // R2
1765            0x20000044, // R3
1766            0x00000000, // R4
1767            0x00000000, // R5
1768            0x00000000, // R6
1769            0x2001ffc8, // R7
1770            0x00000000, // R8
1771            0x00000000, // R9
1772            0x00000000, // R10
1773            0x00000000, // R11
1774            0x00000000, // R12
1775            0x2001ffc8, // R13
1776            0x0000018B, // R14
1777            0x0000018A, // R15
1778            0x2001ffc8, // MSP
1779            0x00000000, // PSP
1780            0x2100000b, // XPSR
1781        ]
1782        .into_iter()
1783        .enumerate()
1784        .map(|(id, r)| DebugRegister {
1785            dwarf_id: Some(id as u16),
1786            core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1787            value: Some(RegisterValue::U32(r)),
1788        })
1789        .collect();
1790
1791        let regs = DebugRegisters(values);
1792
1793        let mut dummy_mem = MockMemory::new();
1794
1795        // Stack:
1796        // 0x2001ffc8 = 0x2001fff0
1797        // 0x2001ffcc = 0xfffffff9
1798        // 0x2001ffd0 = 0x00000001
1799        // 0x2001ffd4 = 0x2001ffcf
1800        // 0x2001ffd8 = 0x20000044
1801        // 0x2001ffdc = 0x20000044
1802        // 0x2001ffe0 = 0x00000000
1803        // 0x2001ffe4 = 0x0000017f
1804        // 0x2001ffe8 = 0x00000180
1805        // 0x2001ffec = 0x21000000
1806        // 0x2001fff0 = 0x2001fff8
1807        // 0x2001fff4 = 0x00000161
1808        // 0x2001fff8 = 0x00000000
1809        // 0x2001fffc = 0x0000013d
1810
1811        dummy_mem.add_word_range(
1812            0x2001_ffc8,
1813            &[
1814                0x2001fff0, 0xfffffff9, 0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000,
1815                0x0000017f, 0x00000180, 0x21000000, 0x2001fff8, 0x00000161, 0x00000000, 0x0000013d,
1816            ],
1817        );
1818
1819        let exception_handler = Box::new(ArmV6MExceptionHandler {});
1820
1821        let frames = debug_info
1822            .unwind_impl(
1823                regs,
1824                &mut dummy_mem,
1825                exception_handler.as_ref(),
1826                Some(probe_rs_target::InstructionSet::Thumb2),
1827            )
1828            .unwrap();
1829
1830        let printed_backtrace = frames
1831            .into_iter()
1832            .map(|f| TestFormatter(&f).to_string())
1833            .collect::<Vec<String>>()
1834            .join("");
1835
1836        insta::assert_snapshot!(printed_backtrace);
1837    }
1838
1839    #[test]
1840    fn unwinding_inlined() {
1841        let debug_info = load_test_elf_as_debug_info("inlined-functions");
1842
1843        // Registers:
1844        // R0        : 0xfffffecc
1845        // R1        : 0x00000001
1846        // R2        : 0x00000000
1847        // R3        : 0x40008140
1848        // R4        : 0x000f4240
1849        // R5        : 0xfffffec0
1850        // R6        : 0x00000000
1851        // R7        : 0x20003ff0
1852        // R8        : 0x00000000
1853        // R9        : 0x00000000
1854        // R10       : 0x00000000
1855        // R11       : 0x00000000
1856        // R12       : 0x5000050c
1857        // R13       : 0x20003ff0
1858        // R14       : 0x00200000
1859        // R15       : 0x000002e4
1860        // MSP       : 0x20003ff0
1861        // PSP       : 0x00000000
1862        // XPSR      : 0x61000000
1863        // EXTRA     : 0x00000000
1864        // FPSCR     : 0x00000000
1865
1866        let values: Vec<_> = [
1867            0xfffffecc, // R0
1868            0x00000001, // R1
1869            0x00000000, // R2
1870            0x40008140, // R3
1871            0x000f4240, // R4
1872            0xfffffec0, // R5
1873            0x00000000, // R6
1874            0x20003ff0, // R7
1875            0x00000000, // R8
1876            0x00000000, // R9
1877            0x00000000, // R10
1878            0x00000000, // R11
1879            0x5000050c, // R12
1880            0x20003ff0, // R13 (SP)
1881            0x00200000, // R14 (RA)
1882            0x000002e4, // R15 (PC)
1883            0x20003ff0, // MSP
1884            0x00000000, // PSP
1885            0x61000000, // XPSR
1886        ]
1887        .into_iter()
1888        .enumerate()
1889        .map(|(id, r)| DebugRegister {
1890            dwarf_id: Some(id as u16),
1891            core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1892            value: Some(RegisterValue::U32(r)),
1893        })
1894        .collect();
1895
1896        let regs = DebugRegisters(values);
1897
1898        let mut dummy_mem = MockMemory::new();
1899
1900        // Stack:
1901        // 0x20003ff0 = 0x20003ff8
1902        // 0x20003ff4 = 0x00000161
1903        // 0x20003ff8 = 0x00000000
1904        // 0x20003ffc = 0x0000013d
1905
1906        dummy_mem.add_word_range(
1907            0x2000_3ff0,
1908            &[0x20003ff8, 0x00000161, 0x00000000, 0x0000013d],
1909        );
1910
1911        let exception_handler = Box::new(ArmV7MExceptionHandler);
1912
1913        let frames = debug_info
1914            .unwind_impl(
1915                regs,
1916                &mut dummy_mem,
1917                exception_handler.as_ref(),
1918                Some(probe_rs_target::InstructionSet::Thumb2),
1919            )
1920            .unwrap();
1921
1922        let printed_backtrace = frames
1923            .into_iter()
1924            .map(|f| TestFormatter(&f).to_string())
1925            .collect::<Vec<String>>()
1926            .join("");
1927
1928        insta::assert_snapshot!(printed_backtrace);
1929    }
1930
1931    #[test]
1932    fn test_print_stacktrace() {
1933        let elf = Path::new("./tests/gpio-hal-blinky/elf");
1934        let coredump = include_bytes!("../tests/gpio-hal-blinky/coredump");
1935
1936        let mut adapter = CoreDump::load_raw(coredump).unwrap();
1937        let debug_info = DebugInfo::from_file(elf).unwrap();
1938
1939        let initial_registers = DebugRegisters::from_coredump(&adapter);
1940        let exception_handler = exception_handler_for_core(adapter.core_type());
1941        let instruction_set = adapter.instruction_set();
1942
1943        let stack_frames = debug_info
1944            .unwind(
1945                &mut adapter,
1946                initial_registers,
1947                exception_handler.as_ref(),
1948                Some(instruction_set),
1949            )
1950            .unwrap();
1951
1952        let printed_backtrace = stack_frames
1953            .into_iter()
1954            .map(|f| TestFormatter(&f).to_string())
1955            .collect::<Vec<String>>()
1956            .join("");
1957
1958        insta::assert_snapshot!(printed_backtrace);
1959    }
1960
1961    #[test_case("RP2040_full_unwind"; "full_unwind Armv6-m using RP2040")]
1962    #[test_case("RP2040_svcall"; "svcall Armv6-m using RP2040")]
1963    #[test_case("RP2040_systick"; "systick Armv6-m using RP2040")]
1964    #[test_case("nRF52833_xxAA_full_unwind"; "full_unwind Armv7-m using nRF52833_xxAA")]
1965    #[test_case("nRF52833_xxAA_svcall"; "svcall Armv7-m using nRF52833_xxAA")]
1966    #[test_case("nRF52833_xxAA_systick"; "systick Armv7-m using nRF52833_xxAA")]
1967    #[test_case("nRF52833_xxAA_hardfault_from_usagefault"; "hardfault_from_usagefault Armv7-m using nRF52833_xxAA")]
1968    #[test_case("nRF52833_xxAA_hardfault_from_busfault"; "hardfault_from_busfault Armv7-m using nRF52833_xxAA")]
1969    #[test_case("nRF52833_xxAA_hardfault_in_systick"; "hardfault_in_systick Armv7-m using nRF52833_xxAA")]
1970    #[test_case("atsamd51p19a"; "Armv7-em from C source code")]
1971    #[test_case("esp32c3_full_unwind"; "full_unwind RISC-V32E using esp32c3")]
1972    #[test_case("esp32s3_esp_hal_panic"; "Xtensa unwinding on an esp32s3 in a panic handler")]
1973    #[test_case("esp32c6_coredump_elf"; "Unwind using a RISC-V coredump in ELF format")]
1974    #[test_case("esp32s3_coredump_elf"; "Unwind using an Xtensa coredump in ELF format")]
1975    fn full_unwind(test_name: &str) {
1976        let debug_info =
1977            load_test_elf_as_debug_info(format!("debug-unwind-tests/{test_name}.elf").as_str());
1978
1979        let coredump_path = coredump_path(format!("debug-unwind-tests/{test_name}"));
1980        let mut adapter = CoreDump::load(&coredump_path).unwrap();
1981
1982        let snapshot_name = test_name.to_string();
1983
1984        let initial_registers = DebugRegisters::from_coredump(&adapter);
1985        let exception_handler = exception_handler_for_core(adapter.core_type());
1986        let instruction_set = adapter.instruction_set();
1987
1988        let mut stack_frames = debug_info
1989            .unwind(
1990                &mut adapter,
1991                initial_registers,
1992                exception_handler.as_ref(),
1993                Some(instruction_set),
1994            )
1995            .unwrap();
1996
1997        // Expand and validate the static and local variables for each stack frame.
1998        for frame in stack_frames.iter_mut() {
1999            let mut variable_caches = Vec::new();
2000            if let Some(local_variables) = &mut frame.local_variables {
2001                variable_caches.push(local_variables);
2002            }
2003            for variable_cache in variable_caches {
2004                // Cache the deferred top level children of the of the cache.
2005                variable_cache.recurse_deferred_variables(
2006                    &debug_info,
2007                    &mut adapter,
2008                    10,
2009                    StackFrameInfo {
2010                        registers: &frame.registers,
2011                        frame_base: frame.frame_base,
2012                        canonical_frame_address: frame.canonical_frame_address,
2013                    },
2014                );
2015            }
2016        }
2017
2018        // Using YAML output because it is easier to read than the default snapshot output,
2019        // and also because they provide better diffs.
2020        insta::assert_yaml_snapshot!(snapshot_name, stack_frames);
2021    }
2022
2023    #[test_case("RP2040_full_unwind"; "Armv6-m using RP2040")]
2024    #[test_case("nRF52833_xxAA_full_unwind"; "Armv7-m using nRF52833_xxAA")]
2025    #[test_case("atsamd51p19a"; "Armv7-em from C source code")]
2026    //TODO:  #[test_case("esp32c3"; "RISC-V32E using esp32c3")]
2027    fn static_variables(chip_name: &str) {
2028        // TODO: Add RISC-V tests.
2029
2030        let debug_info =
2031            load_test_elf_as_debug_info(format!("debug-unwind-tests/{chip_name}.elf").as_str());
2032
2033        let coredump_path = coredump_path(format!("debug-unwind-tests/{chip_name}"));
2034        let mut adapter = CoreDump::load(&coredump_path).unwrap();
2035
2036        let initial_registers = DebugRegisters::from_coredump(&adapter);
2037
2038        let snapshot_name = format!("{chip_name}_static_variables");
2039
2040        let mut static_variables = debug_info.create_static_scope_cache();
2041
2042        static_variables.recurse_deferred_variables(
2043            &debug_info,
2044            &mut adapter,
2045            10,
2046            StackFrameInfo {
2047                registers: &initial_registers,
2048                frame_base: None,
2049                canonical_frame_address: None,
2050            },
2051        );
2052        // Using YAML output because it is easier to read than the default snapshot output,
2053        // and also because they provide better diffs.
2054        insta::assert_yaml_snapshot!(snapshot_name, static_variables);
2055    }
2056
2057    fn coredump_path(base: String) -> PathBuf {
2058        let possible_coredump_paths = [
2059            get_path_for_test_files(format!("{base}.coredump").as_str()),
2060            get_path_for_test_files(format!("{base}_coredump.elf").as_str()),
2061        ];
2062
2063        possible_coredump_paths
2064            .iter()
2065            .find(|path| path.exists())
2066            .unwrap_or_else(|| {
2067                panic!(
2068                    "No coredump found for chip {base}. Expected one of: {possible_coredump_paths:?}"
2069                )
2070            })
2071            .clone()
2072    }
2073
2074    #[test]
2075    fn unwind_same_value() {
2076        let rule = gimli::RegisterRule::SameValue;
2077
2078        let mut callee_frame_registers = DebugRegisters::default();
2079        let debug_register = CORTEX_M_CORE_REGISTERS.core_registers().next().unwrap();
2080
2081        let expected_value = Some(RegisterValue::U32(0x1234));
2082
2083        callee_frame_registers.0.push(DebugRegister {
2084            core_register: debug_register,
2085            dwarf_id: Some(0),
2086            value: expected_value,
2087        });
2088
2089        let mut memory = MockMemory::new();
2090
2091        let value = unwind_register_using_rule(
2092            debug_register,
2093            &callee_frame_registers,
2094            None,
2095            &mut memory,
2096            rule,
2097        )
2098        .unwrap();
2099
2100        assert_eq!(value, expected_value);
2101    }
2102
2103    #[test]
2104    fn unwind_offset() {
2105        let cfa = 0x1000;
2106        let offset = 4;
2107        let rule = gimli::RegisterRule::Offset(offset as i64);
2108        let expected_value = 0xcafe;
2109
2110        let expected_register_value = Some(RegisterValue::U32(expected_value));
2111
2112        let mut memory = MockMemory::new();
2113        memory.add_word_range(cfa + offset, &[expected_value]);
2114
2115        let mut callee_frame_registers = DebugRegisters::default();
2116        let debug_register = CORTEX_M_CORE_REGISTERS.core_registers().next().unwrap();
2117
2118        callee_frame_registers.0.push(DebugRegister {
2119            core_register: debug_register,
2120            dwarf_id: Some(0),
2121            value: None,
2122        });
2123
2124        // This is necessary for the unwind code to determine the address size of the system
2125        callee_frame_registers.0.push(DebugRegister {
2126            core_register: &cortex_m::PC,
2127            dwarf_id: Some(15),
2128            value: Some(RegisterValue::U32(0x0)),
2129        });
2130
2131        let value = unwind_register_using_rule(
2132            debug_register,
2133            &callee_frame_registers,
2134            Some(cfa),
2135            &mut memory,
2136            rule,
2137        )
2138        .unwrap();
2139
2140        assert_eq!(value, expected_register_value);
2141    }
2142
2143    #[test]
2144    fn unwind_undefined_for_frame_pointer() {
2145        let mut callee_frame_registers = DebugRegisters::default();
2146        callee_frame_registers.0.push(DebugRegister {
2147            core_register: &cortex_m::FP,
2148            dwarf_id: Some(7),
2149            value: Some(RegisterValue::U32(0x100)),
2150        });
2151
2152        // This is necessary for the unwind code to determine the address size of the system
2153        callee_frame_registers.0.push(DebugRegister {
2154            core_register: &cortex_m::PC,
2155            dwarf_id: Some(15),
2156            value: Some(RegisterValue::U32(0x0)),
2157        });
2158
2159        let cfa = 0x200;
2160
2161        let mut memory = MockMemory::new();
2162
2163        let value = unwind_register_using_rule(
2164            &cortex_m::FP,
2165            &callee_frame_registers,
2166            Some(cfa),
2167            &mut memory,
2168            RegisterRule::Undefined,
2169        )
2170        .unwrap();
2171
2172        // If there is no rule defined for the frame pointer,
2173        // we assume that it is the same as the canonical frame address.
2174        assert_eq!(value, Some(RegisterValue::U32(0x200)));
2175    }
2176}