Skip to main content

probe_rs_debug/
unit_info.rs

1use std::{collections::HashMap, ops::Range};
2
3use super::{
4    DebugError, DebugRegisters, EndianReader, SourceLocation, VariableCache, debug_info::*,
5    extract_byte_size, extract_file, extract_line, function_die::FunctionDie, variable::*,
6};
7use crate::{language, stack_frame::StackFrameInfo};
8use gimli::{
9    AttributeValue, DebugInfoOffset, DebuggingInformationEntry, EvaluationResult, Location,
10    UnitOffset,
11};
12use probe_rs::MemoryInterface;
13
14/// The result of `UnitInfo::evaluate_expression()` can be the value of a variable, or a memory location.
15#[derive(Debug)]
16pub(crate) enum ExpressionResult {
17    Value(VariableValue),
18    Location(VariableLocation),
19}
20
21/// A struct containing information about a single compilation unit.
22pub struct UnitInfo {
23    pub(crate) unit: gimli::Unit<GimliReader, usize>,
24    dwarf_language: gimli::DwLang,
25    language: Box<dyn language::ProgrammingLanguage>,
26    // A mapping from child die to parent die.
27    parents: HashMap<UnitOffset, UnitOffset>,
28    // Address => function DIE offset
29    function_dies: Vec<(Range<u64>, UnitOffset)>,
30}
31
32impl UnitInfo {
33    /// Create a new `UnitInfo` from a `gimli::Unit`.
34    pub fn new(unit: gimli::Unit<GimliReader, usize>, dwarf: &gimli::Dwarf<GimliReader>) -> Self {
35        let dwarf_language = if let Some(AttributeValue::Language(unit_language)) = unit
36            .entry(unit.root_offset())
37            .ok()
38            .and_then(|root| root.attr_value(gimli::DW_AT_language))
39        {
40            unit_language
41        } else {
42            tracing::warn!("Unable to retrieve DW_AT_language attribute, assuming Rust.");
43            gimli::DW_LANG_Rust
44        };
45
46        let mut this = Self {
47            unit,
48            dwarf_language,
49            language: language::from_dwarf(dwarf_language),
50            parents: HashMap::new(),
51            function_dies: Vec::new(),
52        };
53
54        this.process_unit(dwarf);
55
56        this
57    }
58
59    fn process_unit(&mut self, dwarf: &gimli::Dwarf<GimliReader>) {
60        let mut entries_cursor = self.unit.entries();
61
62        let mut prev_offset = None;
63        let mut previous_depth = entries_cursor.depth();
64        while let Ok(Some(current)) = entries_cursor.next_dfs() {
65            let parent_offset = match current.depth() - previous_depth {
66                1 => {
67                    // Previous die is our parent.
68                    prev_offset
69                }
70                x if x <= 0 => {
71                    let walk_up = |mut levels| {
72                        // If 0:  Previous die is a sibling, we have the same parent.
73                        // If <0: Previous die is a child of one of our siblings. Trace back as many levels as needed, and grab the parent.
74                        let mut cursor = prev_offset.map(|off| self.parents.get(&off).copied())?;
75                        while levels != 0 {
76                            cursor = cursor.map(|off| self.parents.get(&off).copied())?;
77                            levels += 1;
78                        }
79                        cursor
80                    };
81                    walk_up(x)
82                }
83                _ => unreachable!("DFS algorithms never jump down multiple levels in the graph"),
84            };
85
86            if let Some(offset) = parent_offset {
87                self.parents.insert(current.offset(), offset);
88            }
89            previous_depth = current.depth();
90            prev_offset = Some(current.offset());
91
92            // Cache the address ranges if this DIE is a function.
93            if current.tag() == gimli::DW_TAG_subprogram
94                && let Ok(Some(ranges)) = FunctionDie::function_ranges(current, self, dwarf)
95            {
96                for range in ranges {
97                    self.function_dies.push((range, current.offset()));
98                }
99            }
100
101            // TODO: assuming the ranges don't overlap, sort function dies by start address
102        }
103    }
104
105    /// Retrieve the value of the `DW_AT_language` attribute of the compilation unit.
106    ///
107    /// In the unlikely event that we are unable to retrieve the language, we assume Rust.
108    pub(crate) fn get_language(&self) -> gimli::DwLang {
109        self.dwarf_language
110    }
111
112    pub(crate) fn debug_info_offset(&self) -> Result<DebugInfoOffset, DebugError> {
113        self.unit.header.offset().to_debug_info_offset(&self.unit.header).ok_or_else(|| DebugError::Other(
114            "Failed to convert unit header offset to debug info offset. This is a bug, please report it.".to_string()
115        ))
116    }
117
118    /// Get the compilation unit DIEs for the function containing the given address.
119    /// - The first entry in the vector will be the outermost function containing the address.
120    /// - If the address is inlined, the innermost function will be the last entry in the vector.
121    pub(crate) fn get_function_dies<'debug_info>(
122        &'debug_info self,
123        debug_info: &'debug_info super::DebugInfo,
124        address: u64,
125    ) -> Result<Vec<FunctionDie<'debug_info>>, DebugError> {
126        tracing::trace!("Searching Function DIE for address {:#010x}", address);
127
128        // TODO: assuming the ranges don't overlap, binary-search for the function DIE containing the address
129        let Some((_, start_offset)) = self
130            .function_dies
131            .iter()
132            .find(|(range, _)| range.contains(&address))
133            .cloned()
134        else {
135            return Ok(vec![]);
136        };
137
138        let mut entries_cursor = self.unit.entries_at_offset(start_offset)?;
139        while let Ok(Some(current)) = entries_cursor.next_dfs() {
140            let Some(die) = FunctionDie::new(current.clone(), self, debug_info, address)? else {
141                continue;
142            };
143
144            let mut functions = vec![die];
145            tracing::debug!(
146                "Found DIE: name={:?}",
147                functions[0].function_name(debug_info)
148            );
149
150            tracing::debug!("Checking for inlined functions");
151            let inlined_functions =
152                self.find_inlined_functions(debug_info, address, current.offset())?;
153            tracing::debug!(
154                "{} inlined functions for address {:#010x}",
155                inlined_functions.len(),
156                address
157            );
158
159            functions.extend(inlined_functions);
160            return Ok(functions);
161        }
162        Ok(vec![])
163    }
164
165    /// Check if the function located at the given offset contains inlined functions at the
166    /// given address.
167    pub(crate) fn find_inlined_functions<'abbrev>(
168        &'abbrev self,
169        debug_info: &'abbrev DebugInfo,
170        address: u64,
171        parent_offset: UnitOffset,
172    ) -> Result<Vec<FunctionDie<'abbrev>>, DebugError> {
173        // If we don't have any entries at our unit offset, return an empty vector.
174        // This cursor starts at, and includes the entries for the non-inlined function at 'parent_offset'.
175        let Ok(mut cursor) = self.unit.entries_at_offset(parent_offset) else {
176            return Ok(vec![]);
177        };
178
179        // The abort depth is used to control navigation of `cursor.next_dfs()` tree that contains
180        // the inlined functions for the current address.  It is set to the current depth when a
181        // qualifying inlined function is found, and prevents the cursor from searching back up the
182        // tree, for sibling branches.
183        // This is a performance optimization only, and will not affect the correctness of the result.
184        let mut abort_depth = cursor.depth();
185        let mut functions = Vec::new();
186
187        while let Ok(Some(current)) = cursor.next_dfs() {
188            if current.offset() == parent_offset {
189                // We only want children of the non-inlined function DIE at the given `parent_offset`.
190                continue;
191            }
192
193            if current.depth() < abort_depth {
194                // We have found all the inlined functions for the current address
195                // so we can abort the search, before it starts searching other branches of the tree.
196                break;
197            }
198
199            // Keep the current DIE only if it is an inlined function
200            let Some(die) = FunctionDie::new(current.clone(), self, debug_info, address)? else {
201                continue;
202            };
203
204            // Every time we find a qualifying inlined-function, we set the abort depth
205            // to ensure the `cursor.next_dfs()` will be prevented from reversing the depth traversal to search for peers.
206            abort_depth = current.depth();
207
208            functions.push(die);
209        }
210
211        Ok(functions)
212    }
213
214    /// Recurse the ELF structure below the `tree_node`,
215    /// and updates the `cache` with the updated value of the `child_variable`.
216    #[expect(clippy::too_many_arguments)]
217    pub(crate) fn process_tree_node_attributes(
218        &self,
219        debug_info: &DebugInfo,
220        tree_node: &gimli::DebuggingInformationEntry<GimliReader>,
221        parent_variable: &mut Variable,
222        child_variable: &mut Variable,
223        memory: &mut dyn MemoryInterface,
224        cache: &mut VariableCache,
225        frame_info: StackFrameInfo<'_>,
226    ) -> Result<(), DebugError> {
227        // Identify the parent.
228        child_variable.parent_key = parent_variable.variable_key;
229
230        let abstract_entry;
231
232        // We need to determine if we are working with a 'abstract` location, and use that node for the attributes we need
233        let attributes_entry = if let Some(abstract_origin) =
234            tree_node.attr(gimli::DW_AT_abstract_origin)
235        {
236            match abstract_origin.value() {
237                gimli::AttributeValue::UnitRef(unit_ref) => {
238                    // The abstract origin is a reference to another DIE, so we need to resolve that,
239                    // but first we need to process the (optional) memory location using the current DIE.
240                    self.process_memory_location(
241                        debug_info,
242                        tree_node,
243                        parent_variable,
244                        child_variable,
245                        memory,
246                        frame_info,
247                    )?;
248
249                    abstract_entry = self.unit.entry(unit_ref)?;
250
251                    Some(&abstract_entry)
252                }
253                other_attribute_value => {
254                    child_variable.set_value(VariableValue::Error(format!(
255                        "Unimplemented: Attribute Value for DW_AT_abstract_origin {other_attribute_value:?}"
256                    )));
257                    None
258                }
259            }
260        } else {
261            Some(tree_node)
262        };
263
264        let specification_entry;
265
266        // We need to determine if we are working with a variable definition which refers to a declaration,
267        // and use that node for the attributes we need
268        let attributes_entry = if let Some(specification) =
269            tree_node.attr(gimli::DW_AT_specification)
270        {
271            match specification.value() {
272                gimli::AttributeValue::UnitRef(unit_ref) => {
273                    // The abstract origin is a reference to another DIE, so we need to resolve that,
274                    // but first we need to process the (optional) memory location using the current DIE.
275                    self.process_memory_location(
276                        debug_info,
277                        tree_node,
278                        parent_variable,
279                        child_variable,
280                        memory,
281                        frame_info,
282                    )?;
283
284                    specification_entry = self.unit.entry(unit_ref)?;
285
286                    Some(&specification_entry)
287                }
288                other_attribute_value => {
289                    child_variable.set_value(VariableValue::Error(format!(
290                        "Unimplemented: Attribute Value for DW_AT_specification {other_attribute_value:?}"
291                    )));
292                    None
293                }
294            }
295        } else {
296            attributes_entry
297        };
298
299        // For variable attribute resolution, we need to resolve a few attributes in advance of looping through all the other ones.
300        // Try to exact the name first, for easier debugging
301        if let Some(entry) = attributes_entry.as_ref()
302            && let Ok(Some(name)) = extract_name(debug_info, entry)
303        {
304            child_variable.name = VariableName::Named(name);
305        }
306
307        if let Some(attributes_entry) = attributes_entry {
308            child_variable.source_location =
309                self.extract_source_location(debug_info, attributes_entry)?;
310
311            // Now loop through all the unit attributes to extract the remainder of the `Variable` definition.
312            for attr in attributes_entry.attrs() {
313                match attr.name() {
314                    gimli::DW_AT_location | gimli::DW_AT_data_member_location => {
315                        // The child_variable.location is calculated with attribute gimli::DW_AT_type, to ensure it
316                        // gets done before DW_AT_type is processed
317                    }
318                    gimli::DW_AT_name => {
319                        // This was done before we started looping through attributes, so we can ignore it.
320                    }
321                    gimli::DW_AT_decl_file | gimli::DW_AT_decl_line | gimli::DW_AT_decl_column => {
322                        // Handled in extract_source_location()
323                    }
324                    gimli::DW_AT_containing_type => {
325                        // TODO: Implement [documented RUST extensions to DWARF standard](https://rustc-dev-guide.rust-lang.org/debugging-support-in-rustc.html?highlight=dwarf#dwarf-and-rustc)
326                    }
327                    gimli::DW_AT_type => {
328                        // The rules to calculate the type of a child variable are complex, and depend on a number of
329                        // other attributes.
330                        // Depending on the presence and value of these attributes, the [Variable::memory_location] may
331                        // need to be calculated differently.
332                        // - The `DW_AT_type` of the parent (e.g. is it a pointer, or a struct, or an array, etc.).
333                        // - The `DW_AT_address_class of the child (we need to know if it is present, and if it has a
334                        //   value of 0 - unspecified)
335                        // - The `DW_AT_data_member_location` of the child.
336                        // - The `DW_AT_location` of the child.
337                        // - The `DW_AT_byte_size` of the child.
338                        // - The `DW_AT_name` of the data type node.
339                        self.process_type_attribute(
340                            attr,
341                            debug_info,
342                            attributes_entry,
343                            parent_variable,
344                            child_variable,
345                            memory,
346                            frame_info,
347                            cache,
348                        )?;
349                    }
350                    gimli::DW_AT_enum_class => match attr.value() {
351                        gimli::AttributeValue::Flag(true) => {
352                            child_variable
353                                .set_value(VariableValue::Valid(child_variable.type_name()));
354                        }
355                        gimli::AttributeValue::Flag(false) => {
356                            child_variable.set_value(VariableValue::Error(
357                                "Unimplemented: DW_AT_enum_class(false)".to_string(),
358                            ));
359                        }
360                        other_attribute_value => {
361                            child_variable.set_value(VariableValue::Error(format!(
362                                "Unimplemented: Attribute Value for DW_AT_enum_class: {other_attribute_value:?}"
363                            )));
364                        }
365                    },
366                    gimli::DW_AT_const_value => {
367                        let attr_value = attr.value();
368                        let variable_value = if let Some(const_value) = attr_value.udata_value() {
369                            VariableValue::Valid(const_value.to_string())
370                        } else if let Some(const_value) = attr_value.sdata_value() {
371                            VariableValue::Valid(const_value.to_string())
372                        } else {
373                            VariableValue::Error(format!(
374                                "Unimplemented: Attribute Value for DW_AT_const_value: {attr_value:?}"
375                            ))
376                        };
377
378                        child_variable.set_value(variable_value)
379                    }
380                    gimli::DW_AT_alignment => {
381                        // TODO: Figure out when (if at all) we need to do anything with DW_AT_alignment for the
382                        // purposes of decoding data values.
383                    }
384                    gimli::DW_AT_artificial => {
385                        // These are references for entries like discriminant values of `VariantParts`.
386                        child_variable.name = VariableName::Artificial;
387                    }
388                    gimli::DW_AT_discr => match attr.value() {
389                        // This calculates the active discriminant value for the `VariantPart`.
390                        gimli::AttributeValue::UnitRef(unit_ref) => {
391                            let discriminant_node = self.unit.entry(unit_ref)?;
392                            let mut discriminant_variable =
393                                cache.create_variable(parent_variable.variable_key, Some(self))?;
394                            self.process_tree_node_attributes(
395                                debug_info,
396                                &discriminant_node,
397                                parent_variable,
398                                &mut discriminant_variable,
399                                memory,
400                                cache,
401                                frame_info,
402                            )?;
403
404                            let variant_part = if discriminant_variable.is_valid() {
405                                discriminant_variable
406                                    .to_string(cache)
407                                    .parse()
408                                    .unwrap_or(u64::MAX)
409                            } else {
410                                u64::MAX
411                            };
412
413                            parent_variable.role = VariantRole::VariantPart(variant_part);
414                            cache.remove_cache_entry(discriminant_variable.variable_key)?;
415                        }
416                        other_attribute_value => {
417                            child_variable.set_value(VariableValue::Error(format!(
418                                "Unimplemented: Attribute Value for DW_AT_discr {other_attribute_value:?}"
419                            )));
420                        }
421                    },
422                    gimli::DW_AT_linkage_name => {
423                        let value = attr.value();
424                        let raw_str = debug_info.dwarf.attr_string(&self.unit, value).ok();
425
426                        let linkage_name = raw_str.and_then(|r| String::from_utf8(r.to_vec()).ok());
427
428                        child_variable.linkage_name = linkage_name;
429                    }
430                    gimli::DW_AT_accessibility => {
431                        // Silently ignore these for now.
432                        // TODO: Add flag for public/private/protected for `Variable`, once we have a use case.
433                    }
434                    gimli::DW_AT_external => {
435                        // TODO: Implement globally visible variables.
436                    }
437                    gimli::DW_AT_declaration => {
438                        // Unimplemented.
439                    }
440                    gimli::DW_AT_encoding => {
441                        // Ignore these. RUST data types handle this intrinsically.
442                    }
443                    gimli::DW_AT_discr_value => {
444                        // Processed by `extract_variant_discriminant()`.
445                    }
446                    gimli::DW_AT_byte_size => {
447                        // Processed by `extract_byte_size()`.
448                    }
449                    gimli::DW_AT_abstract_origin => {
450                        // Processed before looping through all attributes
451                    }
452                    gimli::DW_AT_address_class => {
453                        // Processed by `extract_type()`
454                    }
455                    gimli::DW_AT_data_bit_offset
456                    | gimli::DW_AT_bit_offset
457                    | gimli::DW_AT_bit_size => {
458                        // Processed by `extract_bitfield_info()`
459                    }
460                    other_attribute => {
461                        tracing::info!(
462                            "Unimplemented: Variable Attribute {:.100} : {:.100}, with children = {}",
463                            format!("{:?}", other_attribute.static_string()),
464                            format!("{:?}", attributes_entry.attr_value(other_attribute)),
465                            attributes_entry.has_children()
466                        );
467                    }
468                }
469            }
470        }
471
472        // Need to process bitfields last as they need type information to be resolved first.
473        self.process_bitfield_info(child_variable, tree_node, cache)?;
474
475        child_variable.extract_value(memory, cache);
476        cache.update_variable(child_variable)?;
477
478        Ok(())
479    }
480
481    #[expect(clippy::too_many_arguments)]
482    fn process_type_attribute(
483        &self,
484        attr: &gimli::Attribute<GimliReader>,
485        debug_info: &DebugInfo,
486        attributes_entry: &gimli::DebuggingInformationEntry<GimliReader>,
487        parent_variable: &Variable,
488        child_variable: &mut Variable,
489        memory: &mut dyn MemoryInterface,
490        frame_info: StackFrameInfo<'_>,
491        cache: &mut VariableCache,
492    ) -> Result<(), DebugError> {
493        // Reference to a type, or an entry to another type or a type modifier which will point to another type.
494        // Before we resolve that type tree, we need to resolve the current node's memory location.
495        // This is because the memory location of the type nodes and child variables often inherit this value.
496        self.process_memory_location(
497            debug_info,
498            attributes_entry,
499            parent_variable,
500            child_variable,
501            memory,
502            frame_info,
503        )?;
504
505        match debug_info.resolve_die_reference_with_unit(attr, self) {
506            Ok((unit_info, referenced_type_tree_node)) => {
507                unit_info.extract_type(
508                    debug_info,
509                    &referenced_type_tree_node,
510                    parent_variable,
511                    child_variable,
512                    memory,
513                    cache,
514                    frame_info,
515                )?;
516            }
517            Err(error) => {
518                child_variable.set_value(VariableValue::Error(format!(
519                    "Failed to process DW_AT_type: {error:?}"
520                )));
521            }
522        }
523
524        Ok(())
525    }
526
527    /// Recurse the ELF structure below the `parent_node`, and ...
528    /// - Consumes the `parent_variable`.
529    /// - Updates the `DebugInfo::VariableCache` with all descendant `Variable`s.
530    /// - Returns a clone of the most up-to-date `parent_variable` in the cache.
531    pub(crate) fn process_tree(
532        &self,
533        debug_info: &DebugInfo,
534        parent_node: gimli::EntriesTreeNode<GimliReader>,
535        parent_variable: &mut Variable,
536        memory: &mut dyn MemoryInterface,
537        cache: &mut VariableCache,
538        frame_info: StackFrameInfo<'_>,
539    ) -> Result<(), DebugError> {
540        if !parent_variable.is_valid() {
541            cache.update_variable(parent_variable)?;
542            return Ok(());
543        }
544
545        tracing::trace!("process_tree for parent {:?}", parent_variable.variable_key);
546
547        let mut child_nodes = parent_node.children();
548        while let Some(child_node) = child_nodes.next()? {
549            match child_node.entry().tag() {
550                gimli::DW_TAG_namespace => {
551                    let variable_name =
552                        if let Ok(Some(name)) = extract_name(debug_info, child_node.entry()) {
553                            VariableName::Namespace(name)
554                        } else {
555                            VariableName::AnonymousNamespace
556                        };
557
558                    // See if this namespace already exists in the cache.
559                    let mut namespace_variable = if let Some(existing_var) = cache
560                        .get_variable_by_name_and_parent(
561                            &variable_name,
562                            parent_variable.variable_key,
563                        ) {
564                        existing_var
565                    } else {
566                        let mut namespace_variable = Variable::new(Some(self));
567
568                        namespace_variable.name = variable_name;
569                        namespace_variable.type_name = VariableType::Namespace;
570                        namespace_variable.memory_location = VariableLocation::Unavailable;
571                        cache
572                            .add_variable(parent_variable.variable_key, &mut namespace_variable)?;
573
574                        namespace_variable
575                    };
576
577                    // Recurse for additional namespace variables.
578                    self.process_tree(
579                        debug_info,
580                        child_node,
581                        &mut namespace_variable,
582                        memory,
583                        cache,
584                        frame_info,
585                    )?;
586
587                    // Do not keep empty namespaces around
588                    if !cache.has_children(&namespace_variable) {
589                        cache.remove_cache_entry(namespace_variable.variable_key)?;
590                    }
591                }
592
593                gimli::DW_TAG_formal_parameter | gimli::DW_TAG_variable | gimli::DW_TAG_member => {
594                    // This branch handles:
595                    //  - Parameters to functions.
596                    //  - Typical top-level variables.
597                    //  - Members of structured types.
598                    //  - Possible values for enumerators, used by extract_type() when processing DW_TAG_enumeration_type.
599                    let mut child_variable =
600                        cache.create_variable(parent_variable.variable_key, Some(self))?;
601                    self.process_tree_node_attributes(
602                        debug_info,
603                        child_node.entry(),
604                        parent_variable,
605                        &mut child_variable,
606                        memory,
607                        cache,
608                        frame_info,
609                    )?;
610
611                    // In the case of C code, we can have entries for both the declaration and the definition of a variable.
612                    // We don't do anything with the declaration right now, so we remove it from the cache.
613                    let is_declaration = if let Some(AttributeValue::Flag(value)) =
614                        child_node.entry().attr_value(gimli::DW_AT_declaration)
615                    {
616                        value
617                    } else {
618                        false
619                    };
620
621                    // Do not keep or process PhantomData nodes, or variant parts that we have already used.
622                    if is_declaration
623                        || child_variable.type_name.is_phantom_data()
624                        || child_variable.name == VariableName::Artificial
625                    {
626                        cache.remove_cache_entry(child_variable.variable_key)?;
627                    } else if child_variable.is_valid() {
628                        // Recursively process each child.
629                        self.process_tree(
630                            debug_info,
631                            child_node,
632                            &mut child_variable,
633                            memory,
634                            cache,
635                            frame_info,
636                        )?;
637                    }
638                }
639                gimli::DW_TAG_variant_part => {
640                    // We need to recurse through the children, to find the DW_TAG_variant with discriminant matching
641                    // the DW_TAG_variant, and ONLY add it's children to the parent variable.
642                    // The structure looks like this (there are other nodes in the structure that we use and discard
643                    // before we get here):
644                    // Level 1: --> An actual variable that has a variant value
645                    //      Level 2: --> this DW_TAG_variant_part node (some child nodes are used to calc the active
646                    //                   Variant discriminant)
647                    //          Level 3: --> Some DW_TAG_variant's that have discriminant values to be matched against
648                    //                       the discriminant
649                    //              Level 4: --> The actual variables, with matching discriminant, which will be added
650                    //                           to `parent_variable`
651                    // TODO: Handle Level 3 nodes that belong to a DW_AT_discr_list, instead of having a discreet
652                    // DW_AT_discr_value
653                    let mut child_variable =
654                        cache.create_variable(parent_variable.variable_key, Some(self))?;
655                    // To determine the discriminant, we use the following rules:
656                    // - If there is no DW_AT_discr, then there will be a single DW_TAG_variant, and this will be the
657                    //   matching value. In the code here, we assign a default value of u64::MAX to both, so that they
658                    //   will be matched as belonging together (https://dwarfstd.org/ShowIssue.php?issue=180517.2)
659                    // - TODO: The [DWARF] standard, 5.7.10, allows for a case where there is no DW_AT_discr attribute,
660                    //   but a DW_AT_type to represent the tag. I have not seen that generated from RUST yet.
661                    // - If there is a DW_AT_discr that has a value, then this is a reference to the member entry for
662                    //   the discriminant. This value will be resolved to match against the appropriate DW_TAG_variant.
663                    // - TODO: The [DWARF] standard, 5.7.10, allows for a DW_AT_discr_list, but I have not seen that
664                    //   generated from RUST yet.
665                    parent_variable.role = VariantRole::VariantPart(u64::MAX);
666                    self.process_tree_node_attributes(
667                        debug_info,
668                        child_node.entry(),
669                        parent_variable,
670                        &mut child_variable,
671                        memory,
672                        cache,
673                        frame_info,
674                    )?;
675                    // At this point we have everything we need (It has updated the parent's `role`) from the
676                    // child_variable, so eliminate it before we continue ...
677                    cache.remove_cache_entry(child_variable.variable_key)?;
678                    self.process_tree(
679                        debug_info,
680                        child_node,
681                        parent_variable,
682                        memory,
683                        cache,
684                        frame_info,
685                    )?;
686                }
687
688                // Variant is a child of a structure, and one of them should have a discriminant value to match the
689                // DW_TAG_variant_part
690                gimli::DW_TAG_variant => {
691                    // We only need to do this if we have not already found our variant,
692                    if !cache.has_children(parent_variable) {
693                        let mut child_variable =
694                            cache.create_variable(parent_variable.variable_key, Some(self))?;
695                        self.extract_variant_discriminant(&child_node, &mut child_variable)?;
696                        self.process_tree_node_attributes(
697                            debug_info,
698                            child_node.entry(),
699                            parent_variable,
700                            &mut child_variable,
701                            memory,
702                            cache,
703                            frame_info,
704                        )?;
705                        if child_variable.is_valid() {
706                            if let VariantRole::Variant(discriminant) = child_variable.role {
707                                // Only process the discriminant variants or when we eventually   encounter the default
708                                if parent_variable.role == VariantRole::VariantPart(discriminant)
709                                    || discriminant == u64::MAX
710                                {
711                                    self.process_memory_location(
712                                        debug_info,
713                                        child_node.entry(),
714                                        parent_variable,
715                                        &mut child_variable,
716                                        memory,
717                                        frame_info,
718                                    )?;
719                                    // Recursively process each relevant child node.
720                                    self.process_tree(
721                                        debug_info,
722                                        child_node,
723                                        &mut child_variable,
724                                        memory,
725                                        cache,
726                                        frame_info,
727                                    )?;
728                                    if child_variable.is_valid() {
729                                        // Eliminate intermediate DWARF nodes, but keep their children
730                                        cache.adopt_grand_children(
731                                            parent_variable,
732                                            &child_variable,
733                                        )?;
734                                    }
735                                } else {
736                                    cache.remove_cache_entry(child_variable.variable_key)?;
737                                }
738                            }
739                        } else {
740                            cache.remove_cache_entry(child_variable.variable_key)?;
741                        }
742                    }
743                }
744                gimli::DW_TAG_lexical_block => {
745                    let Some(program_counter) = frame_info
746                        .registers
747                        .get_program_counter()
748                        .and_then(|reg| reg.value)
749                    else {
750                        return Err(DebugError::WarnAndContinue {
751                            message:
752                                "Cannot unwind `Variable` without a valid PC (program_counter)"
753                                    .to_string(),
754                        });
755                    };
756                    let program_counter = program_counter.try_into()?;
757
758                    // Determine the low and high ranges for which this DIE and children are in scope. These can be
759                    // specified discreetly, or in ranges.
760                    let mut in_scope = false;
761                    if let Some(low_pc_attr) = child_node.entry().attr(gimli::DW_AT_low_pc) {
762                        let low_pc = match low_pc_attr.value() {
763                            gimli::AttributeValue::Addr(value) => value,
764                            _other => u64::MAX,
765                        };
766                        let high_pc = if let Some(high_pc_attr) =
767                            child_node.entry().attr(gimli::DW_AT_high_pc)
768                        {
769                            match high_pc_attr.value() {
770                                gimli::AttributeValue::Addr(addr) => addr,
771                                gimli::AttributeValue::Udata(unsigned_offset) => {
772                                    low_pc + unsigned_offset
773                                }
774                                _other => 0_u64,
775                            }
776                        } else {
777                            0_u64
778                        };
779                        if low_pc == u64::MAX || high_pc == 0_u64 {
780                            // These have not been specified correctly ... something went wrong.
781                            parent_variable.set_value(VariableValue::Error("Error: Processing of variables failed because of invalid/unsupported scope information. Please log a bug at 'https://github.com/probe-rs/probe-rs/issues'".to_string()));
782                        }
783                        let block_range = gimli::Range {
784                            begin: low_pc,
785                            end: high_pc,
786                        };
787                        if block_range.contains(program_counter) {
788                            // We have established positive scope, so no need to continue.
789                            in_scope = true;
790                        }
791                        // No scope info yet, so keep looking.
792                    };
793                    // Searching for ranges has a bit more overhead, so ONLY do this if do not have scope confirmed yet.
794                    if !in_scope && let Some(ranges) = child_node.entry().attr(gimli::DW_AT_ranges)
795                    {
796                        match ranges.value() {
797                            gimli::AttributeValue::RangeListsRef(raw_range_lists_offset) => {
798                                let range_lists_offset = debug_info
799                                    .dwarf
800                                    .ranges_offset_from_raw(&self.unit, raw_range_lists_offset);
801
802                                if let Ok(mut range_iter) =
803                                    debug_info.dwarf.ranges(&self.unit, range_lists_offset)
804                                {
805                                    in_scope = range_iter.contains(program_counter);
806                                }
807                            }
808                            other_range_attribute => {
809                                let error = format!(
810                                    "Found unexpected scope attribute: {:?} for variable {:?}",
811                                    other_range_attribute, parent_variable.name
812                                );
813                                parent_variable.set_value(VariableValue::Error(error));
814                            }
815                        }
816                    }
817                    if in_scope {
818                        // This is IN scope.
819                        // Recursively process each child, but pass the parent_variable, so that we don't create
820                        // intermediate nodes for scope identifiers.
821                        self.process_tree(
822                            debug_info,
823                            child_node,
824                            parent_variable,
825                            memory,
826                            cache,
827                            frame_info,
828                        )?;
829                    } else {
830                        // This lexical block is NOT in scope, but other children of this parent may well be in scope,
831                        // so do NOT invalidate the parent_variable.
832                    }
833                }
834                gimli::DW_TAG_template_type_parameter => {
835                    // The parent node for Rust generic type parameter
836                    // These show up as a child of structures they belong to and points to the type that matches the
837                    // template.
838                    // They are followed by a sibling of `DW_TAG_member` with name '__0' that has all the attributes
839                    // needed to resolve the value.
840                    // TODO: If there are multiple types supported, then I suspect there will be additional
841                    // `DW_TAG_member` siblings. We will need to match those correctly.
842                }
843
844                // Inlined subroutines are handled at the StackFame level
845                gimli::DW_TAG_inlined_subroutine
846                | gimli::DW_TAG_base_type
847                | gimli::DW_TAG_pointer_type
848                | gimli::DW_TAG_structure_type
849                | gimli::DW_TAG_enumeration_type
850                | gimli::DW_TAG_array_type
851                | gimli::DW_TAG_subroutine_type
852                | gimli::DW_TAG_subprogram
853                | gimli::DW_TAG_union_type
854                | gimli::DW_TAG_typedef
855                | gimli::DW_TAG_const_type
856                | gimli::DW_TAG_volatile_type => {
857                    // These will be processed elsewhere, or not at all, until we discover a use case that needs to be
858                    // implemented.
859                }
860                unimplemented => {
861                    tracing::debug!(
862                        "Unimplemented: Encountered unimplemented DwTag {:?} for Variable {:?}",
863                        unimplemented.static_string(),
864                        parent_variable.name
865                    )
866                }
867            }
868        }
869
870        parent_variable.extract_value(memory, cache);
871        cache.update_variable(parent_variable)?;
872
873        Ok(())
874    }
875
876    /// Extract the range information for an array.
877    ///
878    /// This is expected to be contained in an entry with type `DW_TAG_subrange_type`,
879    /// looking like this:
880    ///
881    /// ```text
882    /// 0x00000133:     DW_TAG_subrange_type
883    ///                   DW_AT_type    (0x00000024 "unsigned int")
884    ///                   DW_AT_upper_bound (0x44)
885    /// ```
886    /// Note that there might be multiple ranges, so this function returns a vector of ranges.
887    fn extract_array_range(
888        &self,
889        array_parent_node: UnitOffset,
890    ) -> Result<Vec<Range<u64>>, DebugError> {
891        let mut tree = self.unit.entries_tree(Some(array_parent_node))?;
892
893        let root = tree.root()?;
894
895        let mut children = root.children();
896
897        let mut ranges = vec![];
898        while let Some(child) = children.next()? {
899            match child.entry().tag() {
900                gimli::DW_TAG_subrange_type => {
901                    if let Some(range) = self.extract_array_range_attribute(child.entry())? {
902                        ranges.push(range);
903                    }
904                }
905                other => tracing::debug!(
906                    "Ignoring unexpected child tag {} while extracting array range",
907                    other
908                ),
909            }
910        }
911
912        Ok(ranges)
913    }
914
915    /// Extract the array range values
916    ///
917    /// See [`extract_array_range()`](Self::extract_array_range()) for more information.
918    fn extract_array_range_attribute(
919        &self,
920        entry: &gimli::DebuggingInformationEntry<GimliReader>,
921    ) -> Result<Option<Range<u64>>, DebugError> {
922        let mut lower_bound = None;
923        let mut upper_bound = None;
924
925        // Now loop through all the unit attributes to extract the remainder of the `Variable` definition.
926        for attr in entry.attrs() {
927            match attr.name() {
928                // Property of variables that are of DW_TAG_subrange_type.
929                gimli::DW_AT_lower_bound => match attr.value().udata_value() {
930                    Some(bound) => lower_bound = Some(bound),
931                    None => {
932                        return Err(DebugError::Other(format!(
933                            "Unimplemented: Attribute Value for DW_AT_lower_bound: {:?}",
934                            attr.value()
935                        )));
936                    }
937                },
938                gimli::DW_AT_count => match attr.value().udata_value() {
939                    Some(count) => upper_bound = Some(count),
940                    None => {
941                        return Err(DebugError::Other(format!(
942                            "Unimplemented: Attribute Value for DW_AT_count: {:?}",
943                            attr.value()
944                        )));
945                    }
946                },
947                gimli::DW_AT_upper_bound => {
948                    match attr.value().udata_value() {
949                        // Rust ranges are exclusive, but the DWARF upper bound is inclusive.
950                        Some(bound) => upper_bound = Some(bound + 1),
951                        None => {
952                            return Err(DebugError::Other(format!(
953                                "Unimplemented: Attribute Value for DW_AT_upper_bound: {:?}",
954                                attr.value()
955                            )));
956                        }
957                    }
958                }
959                // Some compilers specify the type of the array size, but we don't use this information
960                // currently.
961                gimli::DW_AT_type => (),
962                other_attribute => {
963                    tracing::debug!(
964                        "Unimplemented: Ignoring attribute {} while extracting array range",
965                        other_attribute,
966                    );
967                }
968            }
969        }
970
971        if let Some(upper_bound) = upper_bound {
972            Ok(Some(lower_bound.unwrap_or_default()..upper_bound))
973        } else {
974            Ok(None)
975        }
976    }
977
978    /// Compute the discriminant value of a DW_TAG_variant variable. If it is not explicitly captured in the DWARF,
979    /// then it is the default value.
980    pub(crate) fn extract_variant_discriminant(
981        &self,
982        node: &gimli::EntriesTreeNode<GimliReader>,
983        variable: &mut Variable,
984    ) -> Result<(), DebugError> {
985        variable.role = match node.entry().attr(gimli::DW_AT_discr_value) {
986            Some(discr_value_attr) => {
987                let attr_value = discr_value_attr.value();
988                let variant = if let Some(const_value) = attr_value.udata_value() {
989                    const_value
990                } else {
991                    variable.set_value(VariableValue::Error(format!(
992                        "Unimplemented: Attribute Value for DW_AT_discr_value: {:.100}",
993                        format!("{attr_value:?}")
994                    )));
995                    u64::MAX
996                };
997
998                VariantRole::Variant(variant)
999            }
1000            None => {
1001                // In the case where the variable is a DW_TAG_variant, but has NO DW_AT_discr_value, then this is the
1002                // "default" to be used.
1003                VariantRole::Variant(u64::MAX)
1004            }
1005        };
1006
1007        Ok(())
1008    }
1009
1010    /// Compute the type (base to complex) of a variable. Only base types have values.
1011    /// Complex types are references to node trees, that require traversal in similar ways to other DIE's like functions.
1012    /// This means [`extract_type()`][e] will call the recursive [`process_tree()`][p] method to build an integrated
1013    /// `tree` of variables with types and values.
1014    ///
1015    /// [e]: Self::extract_type()
1016    /// [p]: Self::process_tree()
1017    #[expect(clippy::too_many_arguments)]
1018    fn extract_type(
1019        &self,
1020        debug_info: &DebugInfo,
1021        node: &gimli::DebuggingInformationEntry<GimliReader>,
1022        parent_variable: &Variable,
1023        child_variable: &mut Variable,
1024        memory: &mut dyn MemoryInterface,
1025        cache: &mut VariableCache,
1026        frame_info: StackFrameInfo<'_>,
1027    ) -> Result<(), DebugError> {
1028        let type_name = match self.extract_type_name(debug_info, node) {
1029            Ok(name) => name,
1030            Err(error) => {
1031                let message = format!("Error: evaluating type name: {error:?}");
1032                child_variable.set_value(VariableValue::Error(message.clone()));
1033                Some(message)
1034            }
1035        };
1036
1037        if !child_variable.is_valid() {
1038            cache.update_variable(child_variable)?;
1039
1040            return Ok(());
1041        }
1042        child_variable.type_node_offset = Some(node.offset());
1043
1044        match node.tag() {
1045            gimli::DW_TAG_base_type => {
1046                child_variable.type_name = VariableType::Base(
1047                    type_name.unwrap_or_else(|| "<unnamed base type>".to_string()),
1048                );
1049                self.process_memory_location(
1050                    debug_info,
1051                    node,
1052                    parent_variable,
1053                    child_variable,
1054                    memory,
1055                    frame_info,
1056                )?;
1057            }
1058            gimli::DW_TAG_pointer_type => {
1059                child_variable.type_name = VariableType::Pointer(type_name);
1060                self.process_memory_location(
1061                    debug_info,
1062                    node,
1063                    parent_variable,
1064                    child_variable,
1065                    memory,
1066                    frame_info,
1067                )?;
1068
1069                // This needs to resolve the pointer before the regular recursion can continue.
1070                match node.attr_value(gimli::DW_AT_type) {
1071                    Some(gimli::AttributeValue::UnitRef(unit_ref)) => {
1072                        // NOTE: surprisingly, as opposed to `void*`, this can be a `const void*`.
1073                        if !cache.has_children(child_variable) {
1074                            let mut referenced_variable =
1075                                cache.create_variable(child_variable.variable_key, Some(self))?;
1076
1077                            // TODO: This is language specific, and should be moved to the language implementations.
1078                            referenced_variable.name = match &child_variable.name {
1079                                VariableName::Named(name) if name.starts_with("Some ") => {
1080                                    VariableName::Named(name.replacen('&', "*", 1))
1081                                }
1082                                VariableName::Named(name) => {
1083                                    VariableName::Named(format!("*{name}"))
1084                                }
1085                                other => VariableName::Named(format!(
1086                                    "Error: Unable to generate name, parent variable does not have a name but is special variable {other:?}"
1087                                )),
1088                            };
1089
1090                            let referenced_node = self.unit.entry(unit_ref)?;
1091
1092                            self.extract_type(
1093                                debug_info,
1094                                &referenced_node,
1095                                child_variable,
1096                                &mut referenced_variable,
1097                                memory,
1098                                cache,
1099                                frame_info,
1100                            )?;
1101
1102                            if matches!(referenced_variable.type_name.inner(), VariableType::Base(name) if name == "()")
1103                            {
1104                                // Only use this, if it is NOT a unit datatype.
1105                                cache.remove_cache_entry(referenced_variable.variable_key)?;
1106                            }
1107                        }
1108                    }
1109                    Some(other_attribute_value) => {
1110                        child_variable.set_value(VariableValue::Error(format!(
1111                            "Unimplemented: Attribute Value for DW_AT_type {:.100}",
1112                            format!("{other_attribute_value:?}")
1113                        )));
1114                    }
1115                    None => {
1116                        // NOTE: this can be a `void*` pointer. Some C compilers model `void` as
1117                        // a type without `DW_AT_type`.
1118                        // FIXME: this differs from `const void*` which may be surprising. Should we
1119                        // add a dummy child variable?
1120                        child_variable.set_value(
1121                            self.language.process_tag_with_no_type(
1122                                child_variable,
1123                                gimli::DW_TAG_pointer_type,
1124                            ),
1125                        );
1126                    }
1127                }
1128            }
1129            gimli::DW_TAG_structure_type => {
1130                self.extract_struct(
1131                    type_name,
1132                    debug_info,
1133                    node,
1134                    parent_variable,
1135                    child_variable,
1136                    memory,
1137                    cache,
1138                    frame_info,
1139                )?;
1140            }
1141            gimli::DW_TAG_enumeration_type => {
1142                self.extract_enumeration_type(
1143                    child_variable,
1144                    type_name,
1145                    debug_info,
1146                    node,
1147                    parent_variable,
1148                    memory,
1149                    frame_info,
1150                )?;
1151            }
1152            gimli::DW_TAG_array_type => {
1153                self.extract_array_type(
1154                    node,
1155                    debug_info,
1156                    parent_variable,
1157                    child_variable,
1158                    memory,
1159                    frame_info,
1160                    cache,
1161                )?;
1162            }
1163            gimli::DW_TAG_union_type => {
1164                child_variable.type_name =
1165                    VariableType::Base(type_name.unwrap_or_else(|| "<unnamed union>".to_string()));
1166                self.process_memory_location(
1167                    debug_info,
1168                    node,
1169                    parent_variable,
1170                    child_variable,
1171                    memory,
1172                    frame_info,
1173                )?;
1174
1175                let mut tree = self.unit.entries_tree(Some(node.offset()))?;
1176
1177                // Recursively process a child types.
1178                self.process_tree(
1179                    debug_info,
1180                    tree.root()?,
1181                    child_variable,
1182                    memory,
1183                    cache,
1184                    frame_info,
1185                )?;
1186                if child_variable.is_valid() && !cache.has_children(child_variable) {
1187                    // Empty structs don't have values.
1188                    child_variable.set_value(VariableValue::Valid(child_variable.type_name()));
1189                }
1190            }
1191            gimli::DW_TAG_subroutine_type => {
1192                // The type_name will be found in the DW_AT_TYPE child of this entry.
1193                // NOTE: There might be value in going beyond just getting the name, but also the parameters (children) and return type (extract_type()).
1194                match node.attr(gimli::DW_AT_type) {
1195                    Some(data_type_attribute) => match data_type_attribute.value() {
1196                        gimli::AttributeValue::UnitRef(unit_ref) => {
1197                            let subroutine_type_node =
1198                                self.unit.header.entry(&self.unit.abbreviations, unit_ref)?;
1199
1200                            child_variable.type_name =
1201                                match extract_name(debug_info, &subroutine_type_node) {
1202                                    Ok(Some(name_attr)) => VariableType::Other(name_attr),
1203                                    Ok(None) => VariableType::Unknown,
1204                                    Err(error) => VariableType::Other(format!(
1205                                        "Error: evaluating subroutine type name: {error:?} "
1206                                    )),
1207                                };
1208                        }
1209                        other_attribute_value => {
1210                            child_variable.set_value(VariableValue::Error(format!(
1211                                "Unimplemented: Attribute Value for DW_AT_type {:.100}",
1212                                format!("{other_attribute_value:?}")
1213                            )));
1214                        }
1215                    },
1216
1217                    None => {
1218                        // TODO: Better indication for no return value
1219                        child_variable
1220                            .set_value(VariableValue::Valid("<No Return Value>".to_string()));
1221                        child_variable.type_name = VariableType::Unknown;
1222                    }
1223                }
1224            }
1225            other @ (gimli::DW_TAG_typedef
1226            | gimli::DW_TAG_const_type
1227            | gimli::DW_TAG_volatile_type
1228            | gimli::DW_TAG_restrict_type
1229            | gimli::DW_TAG_atomic_type) => match node.attr(gimli::DW_AT_type) {
1230                Some(attr) => {
1231                    self.process_type_attribute(
1232                        attr,
1233                        debug_info,
1234                        node,
1235                        parent_variable,
1236                        child_variable,
1237                        memory,
1238                        frame_info,
1239                        cache,
1240                    )?;
1241
1242                    let modifier = match other {
1243                        gimli::DW_TAG_typedef => {
1244                            if child_variable.variable_node_type.is_deferred() {
1245                                // Invalidate the value so we can read it again using the resolved
1246                                // type information.
1247                                child_variable.value = VariableValue::Empty;
1248                            }
1249                            Modifier::Typedef(
1250                                type_name.unwrap_or_else(|| "<unnamed typedef>".to_string()),
1251                            )
1252                        }
1253                        gimli::DW_TAG_const_type => Modifier::Const,
1254                        gimli::DW_TAG_volatile_type => Modifier::Volatile,
1255                        gimli::DW_TAG_restrict_type => Modifier::Restrict,
1256                        gimli::DW_TAG_atomic_type => Modifier::Atomic,
1257                        _ => unreachable!(),
1258                    };
1259
1260                    child_variable.type_name = VariableType::Modified(
1261                        modifier,
1262                        Box::new(std::mem::replace(
1263                            &mut child_variable.type_name,
1264                            VariableType::Unknown,
1265                        )),
1266                    );
1267                }
1268
1269                None => child_variable.set_value(
1270                    self.language
1271                        .process_tag_with_no_type(child_variable, other),
1272                ),
1273            },
1274
1275            // Do not expand this type.
1276            other => {
1277                child_variable.set_value(VariableValue::Error(format!(
1278                    "<unimplemented: type: {other}>"
1279                )));
1280                child_variable.type_name = VariableType::Other("unimplemented".to_string());
1281                cache.remove_cache_entry_children(child_variable.variable_key)?;
1282            }
1283        }
1284
1285        child_variable.extract_value(memory, cache);
1286        cache.update_variable(child_variable)?;
1287
1288        Ok(())
1289    }
1290
1291    #[expect(clippy::too_many_arguments)]
1292    fn extract_struct(
1293        &self,
1294        type_name: Option<String>,
1295        debug_info: &DebugInfo,
1296        node: &DebuggingInformationEntry<GimliReader>,
1297        parent_variable: &Variable,
1298        child_variable: &mut Variable,
1299        memory: &mut dyn MemoryInterface,
1300        cache: &mut VariableCache,
1301        frame_info: StackFrameInfo<'_>,
1302    ) -> Result<(), DebugError> {
1303        let type_name = type_name.unwrap_or_else(|| "<unnamed struct>".to_string());
1304        child_variable.type_name = VariableType::Struct(type_name.clone());
1305        self.process_memory_location(
1306            debug_info,
1307            node,
1308            parent_variable,
1309            child_variable,
1310            memory,
1311            frame_info,
1312        )?;
1313
1314        if child_variable.memory_location != VariableLocation::Unavailable {
1315            // The default behaviour is to defer the processing of child types.
1316            child_variable.variable_node_type =
1317                VariableNodeType::TypeOffset(self.debug_info_offset()?, node.offset());
1318            // In some cases, it really simplifies the UX if we can auto resolve the
1319            // children and derive a value that is visible at first glance to the user.
1320            if self.language.auto_resolve_children(&type_name) {
1321                let temp_node_type = std::mem::replace(
1322                    &mut child_variable.variable_node_type,
1323                    VariableNodeType::RecurseToBaseType,
1324                );
1325
1326                let mut tree = self.unit.entries_tree(Some(node.offset()))?;
1327
1328                self.process_tree(
1329                    debug_info,
1330                    tree.root()?,
1331                    child_variable,
1332                    memory,
1333                    cache,
1334                    frame_info,
1335                )?;
1336                child_variable.variable_node_type = temp_node_type;
1337            }
1338        } else {
1339            // If something is already broken, then do nothing ...
1340            child_variable.variable_node_type = VariableNodeType::DoNotRecurse;
1341        }
1342
1343        self.language.process_struct(
1344            self,
1345            debug_info,
1346            node,
1347            child_variable,
1348            memory,
1349            cache,
1350            frame_info,
1351        )
1352    }
1353
1354    #[expect(clippy::too_many_arguments)]
1355    fn extract_array_type(
1356        &self,
1357        node: &DebuggingInformationEntry<GimliReader>,
1358        debug_info: &DebugInfo,
1359        parent_variable: &Variable,
1360        child_variable: &mut Variable,
1361        memory: &mut dyn MemoryInterface,
1362        frame_info: StackFrameInfo,
1363        cache: &mut VariableCache,
1364    ) -> Result<(), DebugError> {
1365        let subranges = match self.extract_array_range(node.offset()) {
1366            Ok(subranges) => subranges,
1367            Err(error) => {
1368                child_variable.set_value(VariableValue::Error(format!(
1369                    "Error: Failed to extract array range: {error:?}"
1370                )));
1371                return Ok(());
1372            }
1373        };
1374
1375        match node.attr_value(gimli::DW_AT_type) {
1376            Some(gimli::AttributeValue::UnitRef(unit_ref)) => {
1377                // The memory location of array members build on top of the memory location of the child_variable.
1378                self.process_memory_location(
1379                    debug_info,
1380                    node,
1381                    parent_variable,
1382                    child_variable,
1383                    memory,
1384                    frame_info,
1385                )?;
1386
1387                // Now we can explode the array members.
1388                if let Ok(array_member_type_node) = self.unit.entry(unit_ref) {
1389                    // - Next, process this DW_TAG_array_type's DW_AT_type full tree.
1390                    // - We have to do this repeatedly, for every array member in the range.
1391                    // - We have to do this recursively because some compilers encode nested arrays as multiple subranges on the same node.
1392                    self.expand_array_members(
1393                        debug_info,
1394                        &array_member_type_node,
1395                        cache,
1396                        child_variable,
1397                        memory,
1398                        &subranges,
1399                        frame_info,
1400                    )?;
1401                };
1402            }
1403            Some(other_attribute_value) => {
1404                child_variable.set_value(VariableValue::Error(format!(
1405                    "Unimplemented: Attribute Value for DW_AT_type {other_attribute_value:?}"
1406                )));
1407            }
1408            None => {
1409                child_variable.set_value(
1410                    self.language
1411                        .process_tag_with_no_type(child_variable, gimli::DW_TAG_array_type),
1412                );
1413            }
1414        }
1415
1416        Ok(())
1417    }
1418
1419    #[expect(clippy::too_many_arguments)]
1420    fn extract_enumeration_type(
1421        &self,
1422        child_variable: &mut Variable,
1423        type_name: Option<String>,
1424        debug_info: &DebugInfo,
1425        node: &DebuggingInformationEntry<GimliReader>,
1426        parent_variable: &Variable,
1427        memory: &mut dyn MemoryInterface,
1428        frame_info: StackFrameInfo,
1429    ) -> Result<(), DebugError> {
1430        child_variable.type_name =
1431            VariableType::Enum(type_name.unwrap_or_else(|| "<unnamed enum>".to_string()));
1432
1433        self.process_memory_location(
1434            debug_info,
1435            node,
1436            parent_variable,
1437            child_variable,
1438            memory,
1439            frame_info,
1440        )?;
1441
1442        let mut tree = self.unit.entries_tree(Some(node.offset()))?;
1443        let enumerator_values = self.process_enumerator(debug_info, tree.root()?)?;
1444
1445        if !(parent_variable.is_valid() && child_variable.is_valid()) {
1446            return Ok(());
1447        }
1448
1449        let value = if let VariableLocation::Address(address) = child_variable.memory_location {
1450            // NOTE: hard-coding value of variable.byte_size to 1 ... replace with code if necessary.
1451            let mut buff = 0u8;
1452            memory.read(address, std::slice::from_mut(&mut buff))?;
1453            let this_enum_const_value = buff.to_string();
1454
1455            let enumerator_value = match enumerator_values
1456                .iter()
1457                .find(|(_name, value)| value.to_string() == this_enum_const_value)
1458            {
1459                Some((name, _value)) => name,
1460                None => &VariableName::Named("<Error: Unresolved enum value>".to_string()),
1461            };
1462
1463            self.language
1464                .format_enum_value(&child_variable.type_name, enumerator_value)
1465        } else {
1466            VariableValue::Error(format!(
1467                "Unsupported variable location {:?}",
1468                child_variable.memory_location
1469            ))
1470        };
1471
1472        child_variable.set_value(value);
1473
1474        Ok(())
1475    }
1476
1477    /// Extract the different variants of an enumeration
1478    ///
1479    /// This is used for C-style enums, where the enum is an integer type,
1480    /// and all the different variants are different integer values.
1481    fn process_enumerator(
1482        &self,
1483        debug_info: &DebugInfo,
1484        parent_node: gimli::EntriesTreeNode<GimliReader>,
1485    ) -> Result<Vec<(VariableName, VariableValue)>, DebugError> {
1486        let mut enumerator_values = Vec::new();
1487
1488        let mut child_nodes = parent_node.children();
1489        while let Some(child_node) = child_nodes.next()? {
1490            match child_node.entry().tag() {
1491                gimli::DW_TAG_enumerator => {
1492                    let attributes_entry = child_node.entry();
1493
1494                    let name_result = extract_name(debug_info, attributes_entry);
1495
1496                    let Some(attr_value) = attributes_entry.attr_value(gimli::DW_AT_const_value)
1497                    else {
1498                        // Ignore enumerators without a value.
1499                        continue;
1500                    };
1501                    let variable_value = if let Some(const_value) = attr_value.udata_value() {
1502                        VariableValue::Valid(const_value.to_string())
1503                    } else if let Some(const_value) = attr_value.sdata_value() {
1504                        VariableValue::Valid(const_value.to_string())
1505                    } else {
1506                        VariableValue::Error(format!(
1507                            "Unimplemented: Attribute Value for DW_AT_const_value: {attr_value:?}"
1508                        ))
1509                    };
1510
1511                    let enumerator_name = if let Ok(Some(ref name)) = name_result {
1512                        name.to_string()
1513                    } else {
1514                        tracing::warn!("Enumerator has no name");
1515
1516                        format!("<unknown enumerator {}", enumerator_values.len())
1517                    };
1518
1519                    enumerator_values.push((VariableName::Named(enumerator_name), variable_value))
1520                }
1521                // Function implemented on the enum type, ignored here.
1522                gimli::DW_TAG_subprogram => (),
1523                other => {
1524                    tracing::debug!("Ignoring tag {other} under DW_TAG_enumeration_type");
1525                }
1526            }
1527        }
1528
1529        Ok(enumerator_values)
1530    }
1531
1532    /// Create child variable entries to represent array members and their values.
1533    #[expect(clippy::too_many_arguments)]
1534    pub(crate) fn expand_array_members(
1535        &self,
1536        debug_info: &DebugInfo,
1537        array_member_type_node: &DebuggingInformationEntry<GimliReader>,
1538        cache: &mut VariableCache,
1539        array_variable: &mut Variable,
1540        memory: &mut dyn MemoryInterface,
1541        subranges: &[Range<u64>],
1542        frame_info: StackFrameInfo<'_>,
1543    ) -> Result<(), DebugError> {
1544        let Some((current_range, remaining_ranges)) = subranges.split_first() else {
1545            array_variable.set_value(VariableValue::Error(
1546                "Error processing range for array, unexpected empty range. \
1547                    This is a known issue, see https://github.com/probe-rs/probe-rs/issues/2687"
1548                    .to_string(),
1549            ));
1550            return Ok(());
1551        };
1552
1553        // We need to process at least one element to get the array's type right.
1554        let explode_range = if current_range.is_empty() {
1555            0..1
1556        } else {
1557            current_range.clone()
1558        };
1559
1560        for member_index in explode_range.clone() {
1561            let mut array_member_variable =
1562                cache.create_variable(array_variable.variable_key, Some(self))?;
1563            array_member_variable.name = VariableName::Indexed(member_index);
1564            array_member_variable.source_location = array_variable.source_location.clone();
1565
1566            // Set the byte size and push the element to its correct location.
1567            // This call only sets size if:
1568            //  - The parent array's size is known (after processing its first index)
1569            //  - Or the member is a leaf member (i.e. not an array)
1570            // The first index of the parent array will receive its binary size after processing
1571            // its children.
1572            self.process_memory_location(
1573                debug_info,
1574                array_member_type_node,
1575                array_variable,
1576                &mut array_member_variable,
1577                memory,
1578                frame_info,
1579            )?;
1580
1581            if !remaining_ranges.is_empty() {
1582                // Recursively process the nested array and place
1583                // its items under the current variable.
1584                self.expand_array_members(
1585                    debug_info,
1586                    array_member_type_node,
1587                    cache,
1588                    &mut array_member_variable,
1589                    memory,
1590                    remaining_ranges,
1591                    frame_info,
1592                )?;
1593            } else {
1594                self.extract_type(
1595                    debug_info,
1596                    array_member_type_node,
1597                    array_variable,
1598                    &mut array_member_variable,
1599                    memory,
1600                    cache,
1601                    frame_info,
1602                )?;
1603            }
1604
1605            if member_index == explode_range.start {
1606                let item_count = current_range.clone().count();
1607
1608                array_variable.type_name = VariableType::Array {
1609                    count: item_count,
1610                    item_type_name: Box::new(array_member_variable.type_name.clone()),
1611                };
1612                if let Some(item_byte_size) = array_member_variable.byte_size {
1613                    array_variable.byte_size = Some(item_byte_size * item_count as u64);
1614                }
1615            }
1616
1617            array_member_variable.extract_value(memory, cache);
1618            cache.update_variable(&array_member_variable)?;
1619        }
1620
1621        // We want to remove the child entry if the array is empty. It was needed to process the
1622        // array type, but it doesn't actually exist.
1623        if current_range.is_empty() {
1624            cache.remove_cache_entry_children(array_variable.variable_key)?;
1625        }
1626
1627        Ok(())
1628    }
1629
1630    /// Process a memory location for a variable, by first evaluating the `byte_size`, and then calling the `self.extract_location`.
1631    pub(crate) fn process_memory_location(
1632        &self,
1633        debug_info: &DebugInfo,
1634        node_die: &gimli::DebuggingInformationEntry<GimliReader>,
1635        parent_variable: &Variable,
1636        child_variable: &mut Variable,
1637        memory: &mut dyn MemoryInterface,
1638        frame_info: StackFrameInfo<'_>,
1639    ) -> Result<(), DebugError> {
1640        // The `byte_size` is used for arrays, etc. to offset the memory location of the next element.
1641        // For nested arrays, the `byte_size` may need to be calculated as the product of the `byte_size` and array upper bound.
1642        child_variable.byte_size = child_variable
1643            .byte_size
1644            .or_else(|| extract_byte_size(node_die))
1645            .or_else(|| {
1646                if let VariableType::Array { count, .. } = parent_variable.type_name {
1647                    parent_variable
1648                        .byte_size
1649                        .map(|byte_size| byte_size.checked_div(count as u64).unwrap_or(byte_size))
1650                } else {
1651                    None
1652                }
1653            });
1654
1655        if child_variable.memory_location == VariableLocation::Unknown {
1656            // Any expected errors should be handled by one of the variants in the Ok() result.
1657            let expression_result = match self.extract_location(
1658                debug_info,
1659                node_die,
1660                &parent_variable.memory_location,
1661                memory,
1662                frame_info,
1663            ) {
1664                Ok(expr) => expr,
1665                Err(debug_error) => {
1666                    // An Err() result indicates something happened that we have not accounted for. Currently, we support all known location expressions for non-optimized code.
1667                    child_variable.memory_location = VariableLocation::Error(
1668                        "Unsupported location expression while resolving the location. Please reduce optimization levels in your build profile.".to_string()
1669                    );
1670                    let variable_name = &child_variable.name;
1671                    tracing::debug!(
1672                        "Encountered an unsupported location expression while resolving the location for variable {variable_name:?}: {debug_error:?}. Please reduce optimization levels in your build profile."
1673                    );
1674                    return Ok(());
1675                }
1676            };
1677
1678            match expression_result {
1679                ExpressionResult::Value(value_from_expression @ VariableValue::Valid(_)) => {
1680                    // The ELF contained the actual value, not just a location to it.
1681                    child_variable.memory_location = VariableLocation::Value;
1682                    child_variable.set_value(value_from_expression);
1683                }
1684                ExpressionResult::Value(value_from_expression) => {
1685                    child_variable.set_value(value_from_expression);
1686                }
1687                ExpressionResult::Location(VariableLocation::Unavailable) => {
1688                    child_variable.set_value(VariableValue::Error(
1689                        "<value optimized away by compiler, out of scope, or dropped>".to_string(),
1690                    ));
1691                }
1692                ExpressionResult::Location(
1693                    ref location @ VariableLocation::Error(ref error_message)
1694                    | ref location @ VariableLocation::Unsupported(ref error_message),
1695                ) => {
1696                    child_variable.set_value(VariableValue::Error(error_message.clone()));
1697                    child_variable.memory_location = location.clone();
1698                }
1699                ExpressionResult::Location(location_from_expression) => {
1700                    child_variable.memory_location = location_from_expression;
1701                }
1702            }
1703        }
1704
1705        self.handle_memory_location_special_cases(
1706            node_die.offset(),
1707            child_variable,
1708            parent_variable,
1709            memory,
1710        );
1711
1712        Ok(())
1713    }
1714
1715    /// - Find the location using either DW_AT_location, DW_AT_data_member_location, or DW_AT_frame_base attribute.
1716    ///
1717    /// Return values are implemented as follows:
1718    /// - `Result<_, DebugError>`: This happens when we encounter an error we did not expect, and will propagate upwards until the debugger request is failed. **NOT GRACEFUL**, and should be avoided.
1719    /// - `Result<ExpressionResult::Value(),_>`: The value is statically stored in the binary, and can be returned, and has no relevant memory location.
1720    /// - `Result<ExpressionResult::Location(),_>`: One of the variants of VariableLocation, and needs to be interpreted for handling the 'expected' errors we encounter during evaluation.
1721    pub(crate) fn extract_location(
1722        &self,
1723        debug_info: &DebugInfo,
1724        node_die: &gimli::DebuggingInformationEntry<GimliReader>,
1725        parent_location: &VariableLocation,
1726        memory: &mut dyn MemoryInterface,
1727        frame_info: StackFrameInfo<'_>,
1728    ) -> Result<ExpressionResult, DebugError> {
1729        trait ResultExt {
1730            /// Turns UnwindIncompleteResults into Unavailable locations
1731            fn convert_incomplete(self) -> Result<ExpressionResult, DebugError>;
1732        }
1733
1734        impl ResultExt for Result<ExpressionResult, DebugError> {
1735            fn convert_incomplete(self) -> Result<ExpressionResult, DebugError> {
1736                match self {
1737                    Ok(result) => Ok(result),
1738                    Err(DebugError::WarnAndContinue { message }) => {
1739                        tracing::warn!("UnwindIncompleteResults: {:?}", message);
1740                        Ok(ExpressionResult::Location(VariableLocation::Unavailable))
1741                    }
1742                    e => e,
1743                }
1744            }
1745        }
1746
1747        for attr in node_die.attrs() {
1748            let result = match attr.name() {
1749                gimli::DW_AT_location
1750                | gimli::DW_AT_frame_base
1751                | gimli::DW_AT_data_member_location => match attr.value() {
1752                    gimli::AttributeValue::Exprloc(expression) => self
1753                        .evaluate_expression(memory, expression, frame_info)
1754                        .convert_incomplete()?,
1755
1756                    gimli::AttributeValue::Udata(offset_from_location) => {
1757                        let location = if let VariableLocation::Address(address) = parent_location {
1758                            let Some(location) = address.checked_add(offset_from_location) else {
1759                                return Err(DebugError::WarnAndContinue {
1760                                    message: "Overflow calculating variable address".to_string(),
1761                                });
1762                            };
1763
1764                            VariableLocation::Address(location)
1765                        } else {
1766                            parent_location.clone()
1767                        };
1768
1769                        ExpressionResult::Location(location)
1770                    }
1771
1772                    gimli::AttributeValue::LocationListsRef(location_list_offset) => self
1773                        .evaluate_location_list_ref(
1774                            debug_info,
1775                            location_list_offset,
1776                            frame_info,
1777                            memory,
1778                        )
1779                        .convert_incomplete()?,
1780
1781                    other_attribute_value => {
1782                        ExpressionResult::Location(VariableLocation::Unsupported(format!(
1783                            "Unimplemented: extract_location() Could not extract location from: {:.100}",
1784                            format!("{other_attribute_value:?}")
1785                        )))
1786                    }
1787                },
1788
1789                gimli::DW_AT_address_class => {
1790                    let location = match attr.value() {
1791                        gimli::AttributeValue::AddressClass(gimli::DwAddr(0)) => {
1792                            // We pass on the location of the parent, which will later to be used along with DW_AT_data_member_location to calculate the location of this variable.
1793                            parent_location.clone()
1794                        }
1795                        gimli::AttributeValue::AddressClass(address_class) => {
1796                            VariableLocation::Unsupported(format!(
1797                                "Unimplemented: extract_location() found unsupported DW_AT_address_class(gimli::DwAddr({address_class:?}))"
1798                            ))
1799                        }
1800                        other_attribute_value => VariableLocation::Unsupported(format!(
1801                            "Unimplemented: extract_location() found invalid DW_AT_address_class: {:.100}",
1802                            format!("{other_attribute_value:?}")
1803                        )),
1804                    };
1805
1806                    ExpressionResult::Location(location)
1807                }
1808
1809                _other_attributes => {
1810                    // These will be handled elsewhere.
1811                    continue;
1812                }
1813            };
1814
1815            return Ok(result);
1816        }
1817
1818        // If we get here, we did not find a location attribute, then leave the value as Unknown.
1819        Ok(ExpressionResult::Location(VariableLocation::Unknown))
1820    }
1821
1822    fn evaluate_location_list_ref(
1823        &self,
1824        debug_info: &DebugInfo,
1825        location_list_offset: gimli::LocationListsOffset,
1826        frame_info: StackFrameInfo<'_>,
1827        memory: &mut dyn MemoryInterface,
1828    ) -> Result<ExpressionResult, DebugError> {
1829        let mut locations = match debug_info.locations_section.locations(
1830            location_list_offset,
1831            self.unit.header.encoding(),
1832            self.unit.low_pc,
1833            &debug_info.address_section,
1834            self.unit.addr_base,
1835        ) {
1836            Ok(locations) => locations,
1837            Err(error) => {
1838                return Ok(ExpressionResult::Location(VariableLocation::Error(
1839                    format!("Error: Resolving variable Location: {error:?}"),
1840                )));
1841            }
1842        };
1843        let Some(program_counter) = frame_info
1844            .registers
1845            .get_program_counter()
1846            .and_then(|reg| reg.value)
1847        else {
1848            return Ok(ExpressionResult::Location(VariableLocation::Error(
1849                "Cannot determine variable location without a valid program counter.".to_string(),
1850            )));
1851        };
1852
1853        let mut expression = None;
1854        'find_range: loop {
1855            let location = match locations.next() {
1856                Ok(Some(location_lists_entry)) => location_lists_entry,
1857                Ok(None) => break 'find_range,
1858                Err(error) => {
1859                    return Ok(ExpressionResult::Location(VariableLocation::Error(
1860                        format!("Error while iterating LocationLists for this variable: {error:?}"),
1861                    )));
1862                }
1863            };
1864
1865            if let Ok(program_counter) = program_counter.try_into()
1866                && location.range.contains(program_counter)
1867            {
1868                expression = Some(location.data);
1869                break 'find_range;
1870            }
1871        }
1872
1873        let Some(valid_expression) = expression else {
1874            return Ok(ExpressionResult::Location(VariableLocation::Unavailable));
1875        };
1876
1877        self.evaluate_expression(memory, valid_expression, frame_info)
1878    }
1879
1880    /// Evaluate a [`gimli::Expression`] as a valid memory location.
1881    /// Return values are implemented as follows:
1882    /// - `Result<_, DebugError>`: This happens when we encounter an error we did not expect, and will propagate upwards until the debugger request is failed. NOT GRACEFUL, and should be avoided.
1883    /// - `Result<ExpressionResult::Value(),_>`: The value is statically stored in the binary, and can be returned, and has no relevant memory location.
1884    /// - `Result<ExpressionResult::Location(),_>`: One of the variants of VariableLocation, and needs to be interpreted for handling the 'expected' errors we encounter during evaluation.
1885    pub(crate) fn evaluate_expression(
1886        &self,
1887        memory: &mut dyn MemoryInterface,
1888        expression: gimli::Expression<GimliReader>,
1889        frame_info: StackFrameInfo<'_>,
1890    ) -> Result<ExpressionResult, DebugError> {
1891        fn evaluate_address(address: u64, memory: &mut dyn MemoryInterface) -> ExpressionResult {
1892            let location = if address >= u32::MAX as u64 && !memory.supports_native_64bit_access() {
1893                VariableLocation::Error(format!(
1894                    "The memory location for this variable value ({address:#010X}) is invalid. Please report this as a bug."
1895                ))
1896            } else {
1897                VariableLocation::Address(address)
1898            };
1899
1900            ExpressionResult::Location(location)
1901        }
1902
1903        let pieces = self.expression_to_piece(memory, expression, frame_info)?;
1904
1905        if pieces.is_empty() {
1906            return Ok(ExpressionResult::Location(VariableLocation::Error(
1907                "Error: expr_to_piece() returned 0 results".to_string(),
1908            )));
1909        }
1910        if pieces.len() > 1 {
1911            return Ok(ExpressionResult::Location(VariableLocation::Error(
1912                "<unsupported memory implementation>".to_string(),
1913            )));
1914        }
1915
1916        let result = match &pieces[0].location {
1917            Location::Empty => {
1918                // This means the value was optimized away.
1919                ExpressionResult::Location(VariableLocation::Unavailable)
1920            }
1921            Location::Address { address: 0 } => {
1922                let error = "The value of this variable may have been optimized out of the debug info, by the compiler.".to_string();
1923                ExpressionResult::Location(VariableLocation::Error(error))
1924            }
1925            Location::Address { address } => evaluate_address(*address, memory),
1926            Location::Value { value } => {
1927                let value = match value {
1928                    gimli::Value::Generic(value) => value.to_string(),
1929                    gimli::Value::I8(value) => value.to_string(),
1930                    gimli::Value::U8(value) => value.to_string(),
1931                    gimli::Value::I16(value) => value.to_string(),
1932                    gimli::Value::U16(value) => value.to_string(),
1933                    gimli::Value::I32(value) => value.to_string(),
1934                    gimli::Value::U32(value) => value.to_string(),
1935                    gimli::Value::I64(value) => value.to_string(),
1936                    gimli::Value::U64(value) => value.to_string(),
1937                    gimli::Value::F32(value) => value.to_string(),
1938                    gimli::Value::F64(value) => value.to_string(),
1939                };
1940
1941                ExpressionResult::Value(VariableValue::Valid(value))
1942            }
1943            Location::Register { register } => {
1944                if let Some(value) = frame_info
1945                    .registers
1946                    .get_register_by_dwarf_id(register.0)
1947                    .and_then(|register| register.value)
1948                {
1949                    ExpressionResult::Location(VariableLocation::RegisterValue(value))
1950                } else {
1951                    ExpressionResult::Location(VariableLocation::Error(format!(
1952                        "Error: Cannot resolve register: {register:?}"
1953                    )))
1954                }
1955            }
1956            l => ExpressionResult::Location(VariableLocation::Error(format!(
1957                "Unimplemented: extract_location() found a location type: {:.100}",
1958                format!("{l:?}")
1959            ))),
1960        };
1961
1962        Ok(result)
1963    }
1964
1965    /// Tries to get the result of a DWARF expression in the form of a Piece.
1966    pub(crate) fn expression_to_piece(
1967        &self,
1968        memory: &mut dyn MemoryInterface,
1969        expression: gimli::Expression<GimliReader>,
1970        frame_info: StackFrameInfo<'_>,
1971    ) -> Result<Vec<gimli::Piece<GimliReader, usize>>, DebugError> {
1972        let mut evaluation = expression.evaluation(self.unit.encoding());
1973        let mut result = evaluation.evaluate()?;
1974
1975        loop {
1976            result = match result {
1977                EvaluationResult::Complete => return Ok(evaluation.result()),
1978                EvaluationResult::RequiresMemory { address, size, .. } => {
1979                    read_memory(size, memory, address, &mut evaluation)?
1980                }
1981                EvaluationResult::RequiresFrameBase => {
1982                    provide_frame_base(frame_info.frame_base, &mut evaluation)?
1983                }
1984                EvaluationResult::RequiresRegister {
1985                    register,
1986                    base_type,
1987                } => provide_register(frame_info.registers, register, base_type, &mut evaluation)?,
1988                EvaluationResult::RequiresRelocatedAddress(address_index) => {
1989                    // The address_index as an offset from 0, so just pass it into the next step.
1990                    evaluation.resume_with_relocated_address(address_index)?
1991                }
1992                EvaluationResult::RequiresCallFrameCfa => {
1993                    provide_cfa(frame_info.canonical_frame_address, &mut evaluation)?
1994                }
1995                unimplemented_expression => {
1996                    return Err(DebugError::WarnAndContinue {
1997                        message: format!(
1998                            "Unimplemented: Expressions that include {unimplemented_expression:?} are not currently supported."
1999                        ),
2000                    });
2001                }
2002            }
2003        }
2004    }
2005
2006    /// A helper function, to handle memory_location for special cases, such as array members, pointers, and intermediate nodes.
2007    /// Normally, the memory_location is calculated before the type is calculated,
2008    ///     but special cases require the type related info of the variable to correctly compute the memory_location.
2009    fn handle_memory_location_special_cases(
2010        &self,
2011        unit_ref: UnitOffset,
2012        child_variable: &mut Variable,
2013        parent_variable: &Variable,
2014        memory: &mut dyn MemoryInterface,
2015    ) {
2016        let location = if let VariableName::Indexed(child_member_index) = child_variable.name {
2017            // Push the array member to the proper location according to its index.
2018            if let VariableLocation::Address(address) = parent_variable.memory_location {
2019                if let Some(byte_size) = child_variable.byte_size {
2020                    let Some(location) = address.checked_add(child_member_index * byte_size) else {
2021                        child_variable.set_value(VariableValue::Error(
2022                            "Overflow calculating variable address".to_string(),
2023                        ));
2024                        return;
2025                    };
2026
2027                    VariableLocation::Address(location)
2028                } else {
2029                    // If this array member doesn't have a byte_size, it may be because it is the first member of an array itself.
2030                    // In this case, the byte_size will be calculated when the nested array members are resolved.
2031                    // The first member of an array will have a memory location of the same as it's parent.
2032                    parent_variable.memory_location.clone()
2033                }
2034            } else {
2035                VariableLocation::Unavailable
2036            }
2037        } else if child_variable.memory_location == VariableLocation::Unknown {
2038            // Non-array members can inherit their memory location from their parent, but only if the parent has a valid memory location.
2039            if self.is_pointer(child_variable, parent_variable, unit_ref) {
2040                match &parent_variable.memory_location {
2041                    address @ (VariableLocation::Address(_)
2042                    | VariableLocation::RegisterValue(_)) => {
2043                        // Now, retrieve the location by reading the address pointed to by the parent variable.
2044                        match memory.read_word_32(address.memory_address().unwrap()) {
2045                            Ok(memory_location) => {
2046                                VariableLocation::Address(memory_location as u64)
2047                            }
2048                            Err(error) => {
2049                                tracing::debug!(
2050                                    "Failed to read referenced variable address from memory location {} : {error}.",
2051                                    parent_variable.memory_location
2052                                );
2053                                VariableLocation::Error(format!(
2054                                    "Failed to read referenced variable address from memory location {} : {error}.",
2055                                    parent_variable.memory_location
2056                                ))
2057                            }
2058                        }
2059                    }
2060                    other => VariableLocation::Unsupported(format!(
2061                        "Location {other:?} not supported for referenced variables."
2062                    )),
2063                }
2064            } else {
2065                // If the parent variable is not a pointer, or it is a pointer to the actual data location
2066                // (not the address of the data location) then it can inherit it's memory location from it's parent.
2067                parent_variable.memory_location.clone()
2068            }
2069        } else {
2070            return;
2071        };
2072
2073        child_variable.memory_location = location;
2074    }
2075
2076    /// Returns `true` if the variable is a pointer, `false` otherwise.
2077    fn is_pointer(
2078        &self,
2079        child_variable: &mut Variable,
2080        parent_variable: &Variable,
2081        unit_ref: UnitOffset,
2082    ) -> bool {
2083        // Address Pointer Conditions (any of):
2084        // 1. Variable names that start with '*' (e.g '*__0), AND the variable is a variant of the parent.
2085        // 2. Pointer names that start with '*' (e.g. '*const u8')
2086        // 3. Pointers to base types (includes &str types)
2087        // 4. Pointers to variable names that start with `*`
2088        // 5. Pointers to types with referenced memory addresses (e.g. variants, generics, arrays, etc.)
2089        (matches!(child_variable.name, VariableName::Named(ref var_name) if var_name.starts_with('*'))
2090                && matches!(parent_variable.role, VariantRole::VariantPart(_)))
2091            || matches!(&parent_variable.type_name, VariableType::Pointer(Some(pointer_name)) if pointer_name.starts_with('*'))
2092            || (matches!(&parent_variable.type_name, VariableType::Pointer(_))
2093                && (matches!(child_variable.type_name, VariableType::Base(_))
2094                    || matches!(child_variable.type_name, VariableType::Struct(ref type_name) if type_name.starts_with("&str"))
2095                    || matches!(child_variable.name, VariableName::Named(ref var_name) if var_name.starts_with('*'))
2096                    || self.has_address_pointer(unit_ref).unwrap_or_else(|error| {
2097                        child_variable.set_value(VariableValue::Error(format!("Failed to determine if a struct has variant or generic type fields: {error}")));
2098                        false
2099                    })))
2100    }
2101
2102    /// A helper function to determine if the type we are referencing requires a pointer to the address of the referenced variable (e.g. variants, generics, arrays, etc.)
2103    fn has_address_pointer(&self, unit_ref: UnitOffset) -> Result<bool, DebugError> {
2104        let mut entries_tree = self.unit.entries_tree(Some(unit_ref))?;
2105        let entry_node = entries_tree.root()?;
2106        if matches!(
2107            entry_node.entry().tag(),
2108            gimli::DW_TAG_array_type | gimli::DW_TAG_enumeration_type | gimli::DW_TAG_union_type
2109        ) {
2110            return Ok(true);
2111        }
2112        // If the child node has a variant_part, then the variant will be a pointer to the address of the referenced variable.
2113        let mut child_nodes = entry_node.children();
2114        while let Some(child_node) = child_nodes.next()? {
2115            if child_node.entry().tag() == gimli::DW_TAG_variant_part {
2116                return Ok(true);
2117            }
2118        }
2119        Ok(false)
2120    }
2121
2122    /// Returns the `DW_AT_name` attribute in the subtree of a given node or recurses into the node referenced by the `DW_AT_type` attribute.
2123    pub(crate) fn extract_type_name(
2124        &self,
2125        debug_info: &DebugInfo,
2126        entry: &gimli::DebuggingInformationEntry<GimliReader>,
2127    ) -> Result<Option<String>, gimli::Error> {
2128        match entry.attr(gimli::DW_AT_name) {
2129            Some(attr) => {
2130                let name = match attr.value() {
2131                    gimli::AttributeValue::DebugStrRef(name_ref) => {
2132                        if let Ok(name_raw) = debug_info.dwarf.string(name_ref) {
2133                            String::from_utf8_lossy(&name_raw).to_string()
2134                        } else {
2135                            "Invalid DW_AT_name value".to_string()
2136                        }
2137                    }
2138                    gimli::AttributeValue::String(name) => {
2139                        String::from_utf8_lossy(&name).to_string()
2140                    }
2141                    other => format!("Unimplemented: Evaluate name from {other:?}"),
2142                };
2143
2144                Ok(Some(name))
2145            }
2146            None => {
2147                let Some(attr) = entry.attr(gimli::DW_AT_type) else {
2148                    // No type attribute.
2149                    return Ok(None);
2150                };
2151
2152                let gimli::AttributeValue::UnitRef(unit_ref) = attr.value() else {
2153                    // TODO: should we handle other types of references?
2154                    return Ok(None);
2155                };
2156
2157                // Try to read the name of the referenced type node.
2158                let node = self.unit.header.entry(&self.unit.abbreviations, unit_ref)?;
2159                self.extract_type_name(debug_info, &node)
2160            }
2161        }
2162    }
2163
2164    fn process_bitfield_info(
2165        &self,
2166        child_variable: &mut Variable,
2167        entry: &gimli::DebuggingInformationEntry<GimliReader>,
2168        cache: &mut VariableCache,
2169    ) -> Result<(), DebugError> {
2170        if !child_variable.is_valid() {
2171            // Only bother with bitfields if we haven't encountered an error yet
2172            return Ok(());
2173        }
2174        match self.extract_bitfield_info(child_variable, entry) {
2175            Ok(Some(bitfield)) => {
2176                if let Some(byte_size) = child_variable.byte_size {
2177                    let bitfield = bitfield.normalize(byte_size);
2178                    child_variable.type_name = VariableType::Bitfield(
2179                        bitfield,
2180                        Box::new(std::mem::replace(
2181                            &mut child_variable.type_name,
2182                            VariableType::Unknown,
2183                        )),
2184                    );
2185                    // Invalidate value that was read before we knew about the bitfield.
2186                    child_variable.value = VariableValue::Empty;
2187                    cache.update_variable(child_variable)?;
2188                } else {
2189                    child_variable.set_value(VariableValue::Error(
2190                        "Error: Failed to decode bitfield information: byte_size not found"
2191                            .to_string(),
2192                    ));
2193                }
2194            }
2195            Ok(None) => {}
2196            Err(e) => child_variable.set_value(VariableValue::Error(format!(
2197                "Error: Failed to decode bitfield information: {e:?}"
2198            ))),
2199        }
2200
2201        Ok(())
2202    }
2203
2204    fn extract_bitfield_info(
2205        &self,
2206        child_variable: &mut Variable,
2207        entry: &gimli::DebuggingInformationEntry<GimliReader>,
2208    ) -> Result<Option<Bitfield>, gimli::Error> {
2209        let offset = if let Some(attr) = entry.attr(gimli::DW_AT_data_bit_offset) {
2210            // Available since DWARF 4+
2211            match attr.value().udata_value() {
2212                Some(offset) => Some(BitOffset::FromLsb(offset)),
2213                None => {
2214                    child_variable.set_value(VariableValue::Error(format!(
2215                        "Unimplemented: Attribute Value for DW_AT_data_bit_offset: {:?}",
2216                        attr.value()
2217                    )));
2218                    return Ok(None);
2219                }
2220            }
2221        } else if let Some(attr) = entry.attr(gimli::DW_AT_bit_offset) {
2222            // Deprecated in DWARF 5, but still used by some compilers.
2223            // Specifies offset from MSB. We're handling this as a separate offset variant
2224            // because we haven't yet processed the byte size of the variable.
2225            if let Some(offset) = attr.value().udata_value() {
2226                Some(BitOffset::FromMsb(offset))
2227            } else {
2228                child_variable.set_value(VariableValue::Error(format!(
2229                    "Unimplemented: Attribute Value for DW_AT_bit_offset: {:?}",
2230                    attr.value()
2231                )));
2232                return Ok(None);
2233            }
2234        } else {
2235            None
2236        };
2237
2238        let size = if let Some(attr) = entry.attr(gimli::DW_AT_bit_size) {
2239            match attr.value().udata_value() {
2240                Some(length) => Some(length),
2241                None => {
2242                    child_variable.set_value(VariableValue::Error(format!(
2243                        "Unimplemented: Attribute Value for DW_AT_bit_size: {:?}",
2244                        attr.value()
2245                    )));
2246                    return Ok(None);
2247                }
2248            }
2249        } else {
2250            None
2251        };
2252
2253        if let (None, None) = (size, offset) {
2254            return Ok(None);
2255        }
2256
2257        Ok(Some(Bitfield {
2258            length: size.unwrap_or(0),
2259            offset: offset.unwrap_or(BitOffset::FromLsb(0)),
2260        }))
2261    }
2262
2263    fn extract_source_location(
2264        &self,
2265        debug_info: &DebugInfo,
2266        entry: &gimli::DebuggingInformationEntry<GimliReader>,
2267    ) -> Result<Option<SourceLocation>, gimli::Error> {
2268        let Some(file_attr) = entry.attr_value(gimli::DW_AT_decl_file) else {
2269            return Ok(None);
2270        };
2271
2272        let Some(path) = extract_file(debug_info, &self.unit, file_attr) else {
2273            return Ok(None);
2274        };
2275
2276        let mut source_location = SourceLocation {
2277            path,
2278            line: None,
2279            column: None,
2280            address: None,
2281        };
2282
2283        // Now loop through all the unit attributes to extract the remainder of the `Variable` definition.
2284        for attr in entry.attrs() {
2285            match attr.name() {
2286                gimli::DW_AT_decl_line => {
2287                    if let Some(line_number) = extract_line(attr.value()) {
2288                        source_location.line = Some(line_number);
2289                    }
2290                }
2291                gimli::DW_AT_decl_column => {
2292                    if let Some(column_number) = attr.udata_value() {
2293                        // According to the DWARF standard, a value of 0 means no column is specified.
2294                        if column_number != 0 {
2295                            source_location.column = Some(super::ColumnType::Column(column_number));
2296                        }
2297                    }
2298                }
2299                // Other attributes are not relevant for extracting source location.
2300                _ => (),
2301            }
2302        }
2303
2304        Ok(Some(source_location))
2305    }
2306
2307    pub(crate) fn parent_offset(&self, offset: UnitOffset) -> Option<UnitOffset> {
2308        self.parents.get(&offset).copied()
2309    }
2310}
2311
2312fn extract_name(
2313    debug_info: &DebugInfo,
2314    entry: &gimli::DebuggingInformationEntry<GimliReader>,
2315) -> Result<Option<String>, gimli::Error> {
2316    let Some(attr) = entry.attr_value(gimli::DW_AT_name) else {
2317        return Ok(None);
2318    };
2319
2320    let name = match attr {
2321        gimli::AttributeValue::DebugStrRef(name_ref) => {
2322            if let Ok(name_raw) = debug_info.dwarf.string(name_ref) {
2323                String::from_utf8_lossy(&name_raw).to_string()
2324            } else {
2325                "Invalid DW_AT_name value".to_string()
2326            }
2327        }
2328        gimli::AttributeValue::String(name) => String::from_utf8_lossy(&name).to_string(),
2329        other => format!("Unimplemented: Evaluate name from {other:?}"),
2330    };
2331
2332    Ok(Some(name))
2333}
2334
2335/// Gets necessary register information for the DWARF resolver.
2336fn provide_register(
2337    stack_frame_registers: &DebugRegisters,
2338    register: gimli::Register,
2339    base_type: UnitOffset,
2340    evaluation: &mut gimli::Evaluation<EndianReader>,
2341) -> Result<EvaluationResult<EndianReader>, DebugError> {
2342    match stack_frame_registers
2343        .get_register_by_dwarf_id(register.0)
2344        .and_then(|reg| reg.value)
2345    {
2346        Some(raw_value) if base_type == gimli::UnitOffset(0) => {
2347            let register_value = gimli::Value::Generic(raw_value.try_into()?);
2348            Ok(evaluation.resume_with_register(register_value)?)
2349        }
2350        Some(_) => Err(DebugError::WarnAndContinue {
2351            message: format!("Unimplemented: Support for type {base_type:?} in `RequiresRegister`"),
2352        }),
2353        None => Err(DebugError::WarnAndContinue {
2354            message: format!(
2355                "Error while calculating `Variable::memory_location`. No value for register #:{}.",
2356                register.0
2357            ),
2358        }),
2359    }
2360}
2361
2362/// Gets necessary framebase information for the DWARF resolver.
2363fn provide_frame_base(
2364    frame_base: Option<u64>,
2365    evaluation: &mut gimli::Evaluation<EndianReader>,
2366) -> Result<EvaluationResult<EndianReader>, DebugError> {
2367    let Some(frame_base) = frame_base else {
2368        return Err(DebugError::WarnAndContinue {
2369            message: "Cannot unwind `Variable` location without a valid frame base address.)"
2370                .to_string(),
2371        });
2372    };
2373    match evaluation.resume_with_frame_base(frame_base) {
2374        Ok(evaluation_result) => Ok(evaluation_result),
2375        Err(error) => Err(DebugError::WarnAndContinue {
2376            message: format!("Error while calculating `Variable::memory_location`:{error}."),
2377        }),
2378    }
2379}
2380
2381/// Gets necessary CFA information for the DWARF resolver.
2382fn provide_cfa(
2383    cfa: Option<u64>,
2384    evaluation: &mut gimli::Evaluation<EndianReader>,
2385) -> Result<EvaluationResult<EndianReader>, DebugError> {
2386    let Some(cfa) = cfa else {
2387        return Err(DebugError::WarnAndContinue {
2388            message: "Cannot unwind `Variable` location without a valid canonical frame address.)"
2389                .to_string(),
2390        });
2391    };
2392    match evaluation.resume_with_call_frame_cfa(cfa) {
2393        Ok(evaluation_result) => Ok(evaluation_result),
2394        Err(error) => Err(DebugError::WarnAndContinue {
2395            message: format!("Error while calculating `Variable::memory_location`:{error}."),
2396        }),
2397    }
2398}
2399
2400/// Reads memory requested by the DWARF resolver.
2401fn read_memory(
2402    size: u8,
2403    memory: &mut dyn MemoryInterface,
2404    address: u64,
2405    evaluation: &mut gimli::Evaluation<EndianReader>,
2406) -> Result<EvaluationResult<EndianReader>, DebugError> {
2407    /// Reads `SIZE` bytes from the memory.
2408    fn read<const SIZE: usize>(
2409        memory: &mut dyn MemoryInterface,
2410        address: u64,
2411    ) -> Result<[u8; SIZE], DebugError> {
2412        let mut buff = [0u8; SIZE];
2413        memory.read(address, &mut buff).map_err(|error| {
2414            DebugError::WarnAndContinue {
2415                message: format!("Unexpected error while reading debug expressions from target memory: {error:?}. Please report this as a bug.")
2416            }
2417        })?;
2418        Ok(buff)
2419    }
2420
2421    let val = match size {
2422        1 => {
2423            let buff = read::<1>(memory, address)?;
2424            gimli::Value::U8(buff[0])
2425        }
2426        2 => {
2427            let buff = read::<2>(memory, address)?;
2428            gimli::Value::U16(u16::from_le_bytes(buff))
2429        }
2430        4 => {
2431            let buff = read::<4>(memory, address)?;
2432            gimli::Value::U32(u32::from_le_bytes(buff))
2433        }
2434        x => {
2435            return Err(DebugError::WarnAndContinue {
2436                message: format!(
2437                    "Unimplemented: Requested memory with size {x}, which is not supported yet."
2438                ),
2439            });
2440        }
2441    };
2442
2443    Ok(evaluation.resume_with_memory(val)?)
2444}
2445
2446pub(crate) trait RangeExt {
2447    fn contains(self, addr: u64) -> bool;
2448}
2449
2450impl RangeExt for &mut gimli::RngListIter<GimliReader> {
2451    fn contains(self, addr: u64) -> bool {
2452        while let Ok(Some(range)) = self.next() {
2453            if range.contains(addr) {
2454                return true;
2455            }
2456        }
2457
2458        false
2459    }
2460}
2461
2462impl RangeExt for gimli::Range {
2463    fn contains(self, addr: u64) -> bool {
2464        self.begin <= addr && addr < self.end
2465    }
2466}