Skip to main content

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