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