Repository: probe-rs/probe-rs
Pull request #3279 (2025-05-04 07:59:20)

Deduplicate processing deferred variables


Patch:
diff --git a/changelog/added-debug-info-ref.md b/changelog/added-debug-info-ref.md
new file mode 100644
index 00000000..804f5f00
--- /dev/null
+++ b/changelog/added-debug-info-ref.md
@@ -0,0 +1,1 @@
+Debugger now handles DW_AT_type DebugInfoRef
\ No newline at end of file
diff --git a/probe-rs-debug/src/debug_info.rs b/probe-rs-debug/src/debug_info.rs
index 35b52957..d7e29b1a 100644
--- a/probe-rs-debug/src/debug_info.rs
+++ b/probe-rs-debug/src/debug_info.rs
@@ -10,8 +10,7 @@ use crate::{
     SourceLocation, VerifiedBreakpoint, registers, stack_frame::StackFrameInfo, unit_info::RangeExt,
 };
 use gimli::{
-    BaseAddresses, DebugFrame, DebugInfoOffset, RunTimeEndian, UnwindContext, UnwindSection,
-    UnwindTableRow,
+    BaseAddresses, DebugFrame, RunTimeEndian, UnwindContext, UnwindSection, UnwindTableRow,
 };
 use object::read::{Object, ObjectSection};
 use probe_rs::{Error, MemoryInterface, RegisterDataType, RegisterRole, RegisterValue, UnwindRule};
@@ -253,30 +252,20 @@ impl DebugInfo {
         }
 
         match parent_variable.variable_node_type {
-            VariableNodeType::TypeOffset(header_offset, type_offset) => {
-                let unit_header = self.dwarf.debug_info.header_from_offset(header_offset)?;
-                let unit_info = UnitInfo::new(gimli::Unit::new(&self.dwarf, unit_header)?);
-
-                // Find the parent node
-                let mut type_tree = unit_info.unit.entries_tree(Some(type_offset))?;
-                let parent_node = type_tree.root()?;
-
-                unit_info.process_tree(
-                    self,
-                    parent_node,
-                    parent_variable,
-                    memory,
-                    cache,
-                    frame_info,
-                )?;
-            }
-            VariableNodeType::DirectLookup(header_offset, unit_offset) => {
-                let unit_header = self.dwarf.debug_info.header_from_offset(header_offset)?;
-                let unit_info = UnitInfo::new(gimli::Unit::new(&self.dwarf, unit_header)?);
+            VariableNodeType::TypeOffset(header_offset, unit_offset)
+            | VariableNodeType::DirectLookup(header_offset, unit_offset) => {
+                let Some(unit_info) = self
+                    .unit_infos
+                    .iter()
+                    .find(|unit_info| unit_info.unit.header.offset() == header_offset.into())
+                else {
+                    return Err(DebugError::Other(
+                        "Failed to find unit info for offset lookup.".to_string(),
+                    ));
+                };
 
                 // Find the parent node
                 let mut type_tree = unit_info.unit.entries_tree(Some(unit_offset))?;
-
                 let parent_node = type_tree.root()?;
 
                 unit_info.process_tree(
@@ -289,43 +278,26 @@ impl DebugInfo {
                 )?;
             }
             VariableNodeType::UnitsLookup => {
-                // Look up static variables from all units
-                let mut unit_infos = self.unit_infos.iter();
-
-                let Some(unit_info) = unit_infos.next() else {
+                if self.unit_infos.is_empty() {
                     // No unit infos
                     return Err(DebugError::Other("Missing unit infos".to_string()));
-                };
-
-                let mut entries = unit_info.unit.entries();
-
-                // Only process statics for this unit header.
-                // Navigate the current unit from the header down.
-                let (_, unit_node) = entries.next_dfs()?.unwrap();
-
-                let mut tree = unit_info.unit.entries_tree(Some(unit_node.offset()))?;
-
-                unit_info.process_tree(
-                    self,
-                    tree.root()?,
-                    parent_variable,
-                    memory,
-                    cache,
-                    frame_info,
-                )?;
+                }
 
-                for unit in unit_infos {
-                    let mut entries = unit.unit.entries();
+                // Look up static variables from all units
+                for unit_info in self.unit_infos.iter() {
+                    let mut entries = unit_info.unit.entries();
 
                     // Only process statics for this unit header.
                     // Navigate the current unit from the header down.
                     let (_, unit_node) = entries.next_dfs()?.unwrap();
+                    let unit_offset = unit_node.offset();
 
-                    let mut tree = unit.unit.entries_tree(Some(unit_node.offset()))?;
+                    let mut type_tree = unit_info.unit.entries_tree(Some(unit_offset))?;
+                    let parent_node = type_tree.root()?;
 
-                    unit.process_tree(
+                    unit_info.process_tree(
                         self,
-                        tree.root()?,
+                        parent_node,
                         parent_variable,
                         memory,
                         cache,
@@ -917,25 +889,6 @@ impl DebugInfo {
         )))
     }
 
-    /// Get the DIE at the given offset into the debug info section.
-    pub(crate) fn get_die_at_offset(&self, offset: DebugInfoOffset) -> Result<Die, DebugError> {
-        for unit_info in &self.unit_infos {
-            if let Some(unit_offset) = offset.to_unit_offset(&unit_info.unit.header) {
-                return unit_info.unit.entry(unit_offset).map_err(|error| {
-                    DebugError::Other(format!(
-                        "Error reading DIE at debug info offset {:#x} : {}",
-                        offset.0, error
-                    ))
-                });
-            }
-        }
-
-        Err(DebugError::Other(format!(
-            "DIE at debug info offset {:#010x} not found",
-            offset.0
-        )))
-    }
-
     /// Look up the DIE reference for the given attribute, if it exists.
     pub(crate) fn resolve_die_reference<'debug_info, 'unit_info>(
         &'debug_info self,
@@ -946,27 +899,56 @@ impl DebugInfo {
     where
         'unit_info: 'debug_info,
     {
-        let value = die.attr_value(attribute).ok().flatten()?;
+        let attr = die.attr(attribute).ok().flatten()?;
 
-        match value {
-            gimli::AttributeValue::UnitRef(unit_ref) => unit_info.unit.entry(unit_ref).ok(),
-            gimli::AttributeValue::DebugInfoRef(debug_info_ref) => {
-                self.get_die_at_offset(debug_info_ref).ok()
-            }
-            other_value => {
-                tracing::warn!(
-                    "Unsupported {:?} value: {other_value:?}",
-                    attribute.static_string(),
-                );
-                None
-            }
-        }
+        self.resolve_die_reference_with_unit(&attr, unit_info)
+            .ok()
+            .map(|(_, die)| die)
     }
 
     /// The program binary's (and core's) endianness.
     pub fn endianness(&self) -> RunTimeEndian {
         self.endianness
     }
+
+    /// Returns the UnitInfo and DIE for the given attribute.
+    pub(crate) fn resolve_die_reference_with_unit<'debug_info, 'unit_info>(
+        &'debug_info self,
+        attr: &gimli::Attribute<GimliReader>,
+        unit_info: &'unit_info UnitInfo,
+    ) -> Result<(&'debug_info UnitInfo, Die<'debug_info, 'debug_info>), DebugError>
+    where
+        'unit_info: 'debug_info,
+    {
+        match attr.value() {
+            gimli::AttributeValue::UnitRef(unit_ref) => {
+                Ok((unit_info, unit_info.unit.entry(unit_ref)?))
+            }
+            gimli::AttributeValue::DebugInfoRef(offset) => {
+                for unit_info in &self.unit_infos {
+                    let Some(unit_offset) = offset.to_unit_offset(&unit_info.unit.header) else {
+                        continue;
+                    };
+
+                    let entry = unit_info.unit.entry(unit_offset).map_err(|error| {
+                        DebugError::Other(format!(
+                            "Error reading DIE at debug info offset {:#x} : {}",
+                            offset.0, error
+                        ))
+                    })?;
+                    return Ok((unit_info, entry));
+                }
+
+                Err(DebugError::Other(format!(
+                    "Unable to find unit info for debug info offset {:#x}",
+                    offset.0
+                )))
+            }
+            other_attribute_value => Err(DebugError::Other(format!(
+                "Unimplemented attribute value {other_attribute_value:?}"
+            ))),
+        }
+    }
 }
 
 /// Uses the [`TypedPathBuf::normalize`] function to normalize both paths before comparing them
diff --git a/probe-rs-debug/src/unit_info.rs b/probe-rs-debug/src/unit_info.rs
index 31c60287..17434319 100644
--- a/probe-rs-debug/src/unit_info.rs
+++ b/probe-rs-debug/src/unit_info.rs
@@ -429,23 +429,21 @@ impl UnitInfo {
         frame_info: StackFrameInfo<'_>,
         cache: &mut VariableCache,
     ) -> Result<(), DebugError> {
-        match attr.value() {
-            gimli::AttributeValue::UnitRef(unit_ref) => {
-                // Reference to a type, or an entry to another type or a type modifier which will point to another type.
-                // Before we resolve that type tree, we need to resolve the current node's memory location.
-                // This is because the memory location of the type nodes and child variables often inherit this value.
-                self.process_memory_location(
-                    debug_info,
-                    attributes_entry,
-                    parent_variable,
-                    child_variable,
-                    memory,
-                    frame_info,
-                )?;
+        // Reference to a type, or an entry to another type or a type modifier which will point to another type.
+        // Before we resolve that type tree, we need to resolve the current node's memory location.
+        // This is because the memory location of the type nodes and child variables often inherit this value.
+        self.process_memory_location(
+            debug_info,
+            attributes_entry,
+            parent_variable,
+            child_variable,
+            memory,
+            frame_info,
+        )?;
 
-                // Now resolve the referenced tree node for the type.
-                let referenced_type_tree_node = self.unit.entry(unit_ref)?;
-                self.extract_type(
+        match debug_info.resolve_die_reference_with_unit(attr, self) {
+            Ok((unit_info, referenced_type_tree_node)) => {
+                unit_info.extract_type(
                     debug_info,
                     &referenced_type_tree_node,
                     parent_variable,
@@ -455,9 +453,9 @@ impl UnitInfo {
                     frame_info,
                 )?;
             }
-            other_attribute_value => {
+            Err(error) => {
                 child_variable.set_value(VariableValue::Error(format!(
-                    "Unimplemented: Attribute Value for DW_AT_type {other_attribute_value:?}"
+                    "Failed to process DW_AT_type: {error:?}"
                 )));
             }
         }


Test patch:
diff --git a/probe-rs-debug/src/snapshots/probe_rs_debug__debug_info__test__esp32s3_esp_hal_panic.snap b/probe-rs-debug/src/snapshots/probe_rs_debug__debug_info__test__esp32s3_esp_hal_panic.snap
index b2c0a959..02cd5c0d 100644
--- a/probe-rs-debug/src/snapshots/probe_rs_debug__debug_info__test__esp32s3_esp_hal_panic.snap
+++ b/probe-rs-debug/src/snapshots/probe_rs_debug__debug_info__test__esp32s3_esp_hal_panic.snap
@@ -5975,12 +5975,56 @@ expression: stack_frames
                   children:
                     - name:
                         Named: inner
-                      type_name: Unknown
-                      value: "< Unimplemented: Attribute Value for DW_AT_type DebugInfoRef(DebugInfoOffset(5879)) >"
-                - name:
-                    Named: not_send
-                  type_name: Unknown
-                  value: "< Unimplemented: Attribute Value for DW_AT_type DebugInfoRef(DebugInfoOffset(6875)) >"
+                      type_name:
+                        Struct: Executor
+                      value: Executor @ 0x00000000
+                      children:
+                        - name:
+                            Named: inner
+                          type_name:
+                            Struct: SyncExecutor
+                          value: SyncExecutor @ 0x00000000
+                          children:
+                            - name:
+                                Named: run_queue
+                              type_name:
+                                Struct: RunQueue
+                              value: RunQueue @ 0x00000000
+                              children:
+                                - name:
+                                    Named: head
+                                  type_name:
+                                    Struct: "AtomicPtr<embassy_executor::raw::TaskHeader>"
+                                  value: "AtomicPtr<embassy_executor::raw::TaskHeader> @ 0x00000000"
+                                  children:
+                                    - name:
+                                        Named: p
+                                      type_name:
+                                        Struct: "UnsafeCell<*mut embassy_executor::raw::TaskHeader>"
+                                      value: "UnsafeCell<*mut embassy_executor::raw::TaskHeader> @ 0x00000000"
+                                      children:
+                                        - name:
+                                            Named: value
+                                          type_name:
+                                            Pointer: "*mut embassy_executor::raw::TaskHeader"
+                                          value: "*mut embassy_executor::raw::TaskHeader @ 0x00000000"
+                                          children:
+                                            - name:
+                                                Named: "*value"
+                                              type_name:
+                                                Struct: TaskHeader
+                                              value: TaskHeader
+                            - name:
+                                Named: pender
+                              type_name:
+                                Struct: Pender
+                              value: Pender @ 0x00000004
+                              children:
+                                - name:
+                                    Named: __0
+                                  type_name:
+                                    Pointer: "*mut ()"
+                                  value: "*mut () @ 0x00000004"
         - name:
             Named: init
           type_name: