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