Repository: probe-rs/probe-rs
Pull request #2844 (2024-10-03 20:48:54)

Various debugger improvements
- Handle the `DW_AT_specification` attributes
- Store the linkage name (I'm using this in some tests in another branch of mine, to handle some variables with duplicate names 


Patch:
diff --git a/probe-rs/src/debug/unit_info.rs b/probe-rs/src/debug/unit_info.rs
index 9ee2bef0..60e1e1ce 100644
--- a/probe-rs/src/debug/unit_info.rs
+++ b/probe-rs/src/debug/unit_info.rs
@@ -14,6 +14,7 @@ use gimli::{
 };
 
 /// The result of `UnitInfo::evaluate_expression()` can be the value of a variable, or a memory location.
+#[derive(Debug)]
 pub(crate) enum ExpressionResult {
     Value(VariableValue),
     Location(VariableLocation),
@@ -198,6 +199,41 @@ impl UnitInfo {
             Some(tree_node)
         };
 
+        let specification_entry;
+
+        // We need to determine if we are working with a variable definition which refers to a declaration,
+        // and use that node for the attributes we need
+        let attributes_entry = if let Ok(Some(specification)) =
+            tree_node.attr(gimli::DW_AT_specification)
+        {
+            match specification.value() {
+                gimli::AttributeValue::UnitRef(unit_ref) => {
+                    // The abstract origin is a reference to another DIE, so we need to resolve that,
+                    // but first we need to process the (optional) memory location using the current DIE.
+                    self.process_memory_location(
+                        debug_info,
+                        tree_node,
+                        parent_variable,
+                        child_variable,
+                        memory,
+                        frame_info,
+                    )?;
+
+                    specification_entry = self.unit.entry(unit_ref)?;
+
+                    Some(&specification_entry)
+                }
+                other_attribute_value => {
+                    child_variable.set_value(VariableValue::Error(format!(
+                        "Unimplemented: Attribute Value for DW_AT_specification {other_attribute_value:?}"
+                    )));
+                    None
+                }
+            }
+        } else {
+            attributes_entry
+        };
+
         // For variable attribute resolution, we need to resolve a few attributes in advance of looping through all the other ones.
         // Try to exact the name first, for easier debugging
         if let Some(entry) = attributes_entry.as_ref() {
@@ -334,6 +370,14 @@ impl UnitInfo {
                             )));
                         }
                     },
+                    gimli::DW_AT_linkage_name => {
+                        let value = attr.value();
+                        let raw_str = debug_info.dwarf.attr_string(&self.unit, value).ok();
+
+                        let linkage_name = raw_str.and_then(|r| String::from_utf8(r.to_vec()).ok());
+
+                        child_variable.linkage_name = linkage_name;
+                    }
                     gimli::DW_AT_accessibility => {
                         // Silently ignore these for now.
                         // TODO: Add flag for public/private/protected for `Variable`, once we have a use case.
@@ -356,9 +400,6 @@ impl UnitInfo {
                     gimli::DW_AT_abstract_origin => {
                         // Processed before looping through all attributes
                     }
-                    gimli::DW_AT_linkage_name => {
-                        // Unused attribute of, for example, inlined DW_TAG_subroutine
-                    }
                     gimli::DW_AT_address_class => {
                         // Processed by `extract_type()`
                     }
@@ -1602,10 +1643,11 @@ impl UnitInfo {
                 }
 
                 ExpressionResult::Location(
-                    VariableLocation::Error(error_message)
-                    | VariableLocation::Unsupported(error_message),
+                    ref location @ VariableLocation::Error(ref error_message)
+                    | ref location @ VariableLocation::Unsupported(ref error_message),
                 ) => {
                     child_variable.set_value(VariableValue::Error(error_message.clone()));
+                    child_variable.memory_location = location.clone();
                 }
 
                 ExpressionResult::Location(location_from_expression) => {
@@ -2177,10 +2219,8 @@ fn extract_name(
     debug_info: &DebugInfo,
     entry: &gimli::DebuggingInformationEntry<GimliReader>,
 ) -> Result<Option<String>, gimli::Error> {
-    let attr = match entry.attr(gimli::DW_AT_name) {
-        Ok(Some(attr)) => attr.value(),
-        Ok(None) => return Ok(None),
-        Err(error) => return Err(error),
+    let Some(attr) = entry.attr_value(gimli::DW_AT_name)? else {
+        return Ok(None);
     };
 
     let name = match attr {
diff --git a/probe-rs/src/debug/variable.rs b/probe-rs/src/debug/variable.rs
index faf9b0d6..d0a60b72 100644
--- a/probe-rs/src/debug/variable.rs
+++ b/probe-rs/src/debug/variable.rs
@@ -466,6 +466,11 @@ pub struct Variable {
     pub parent_key: ObjectRef,
     /// The variable name refers to the name of any of the types of values described in the [VariableCache]
     pub name: VariableName,
+
+    /// Linkage name of the variable. Multiple variables with the same name could exist,
+    /// this is used to distinguish between them.
+    pub(crate) linkage_name: Option<String>,
+
     /// Use `Variable::set_value()` and `Variable::get_value()` to correctly process this `value`
     pub(super) value: VariableValue,
     /// The source location of the declaration of this variable, if available.
@@ -502,6 +507,7 @@ impl Variable {
             variable_key: Default::default(),
             parent_key: Default::default(),
             name: Default::default(),
+            linkage_name: None,
             value: Default::default(),
             source_location: Default::default(),
             type_name: Default::default(),
@@ -537,7 +543,8 @@ impl Variable {
 
             // If the value is invalid, then make sure we don't propagate invalid memory location
             // values.
-            self.memory_location = VariableLocation::Unavailable;
+            self.memory_location =
+                VariableLocation::Error("Failed to resolve variable value".to_string());
         }
     }
 


Test patch:
diff --git a/probe-rs/src/debug/snapshots/probe_rs__debug__debug_info__test__atsamd51p19a_static_variables.snap b/probe-rs/src/debug/snapshots/probe_rs__debug__debug_info__test__atsamd51p19a_static_variables.snap
index f009390e..00aba28c 100644
--- a/probe-rs/src/debug/snapshots/probe_rs__debug__debug_info__test__atsamd51p19a_static_variables.snap
+++ b/probe-rs/src/debug/snapshots/probe_rs__debug__debug_info__test__atsamd51p19a_static_variables.snap
@@ -1,11 +1,12 @@
 ---
 source: probe-rs/src/debug/debug_info.rs
+assertion_line: 2036
 expression: static_variables
 ---
 Child Variables:
   name: StaticScopeRoot
   type_name: Unknown
-  value: "<unknown> {\n\texception_table: <unknown> = < The value of this variable may have been optimized out of the debug info, by the compiler. >,\n\t_aTerminalId: <unknown> = < The value of this variable may have been optimized out of the debug info, by the compiler. >,<unknown>,\n\tchar[1024] = [\n\t\tH,\n\t\te,\n\t\tl,\n\t\tl,\n\t\to,\n\t\t ,\n\t\tH,\n\t\te,\n\t\tl,\n\t\tl,\n\n\t\t... and 1014 more\n\t],\n\tchar[16] = [\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\n\t\t... and 6 more\n\t],\n\t_ActiveTerminal: <unknown> = < The value of this variable may have been optimized out of the debug info, by the compiler. >,\n\tfoo: Foo_t = Foo_t @ 0x20000000,\n\tconst Foo_t[2] = [\n\t\tFoo_t @ 0x00001684,\n\t\tFoo_t @ 0x0000168C\n\t],\n\tuint8_t[9][6] = [\n\t\tuint8_t[9] = [\n\t\t\t0,\n\t\t\t1,\n\t\t\t2,\n\t\t\t3,\n\t\t\t4,\n\t\t\t5,\n\t\t\t6,\n\t\t\t7,\n\t\t\t8\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t10,\n\t\t\t11,\n\t\t\t12,\n\t\t\t13,\n\t\t\t14,\n\t\t\t15,\n\t\t\t16,\n\t\t\t17,\n\t\t\t18\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t20,\n\t\t\t21,\n\t\t\t22,\n\t\t\t23,\n\t\t\t24,\n\t\t\t25,\n\t\t\t26,\n\t\t\t27,\n\t\t\t28\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t30,\n\t\t\t31,\n\t\t\t32,\n\t\t\t33,\n\t\t\t34,\n\t\t\t35,\n\t\t\t36,\n\t\t\t37,\n\t\t\t38\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t40,\n\t\t\t41,\n\t\t\t42,\n\t\t\t43,\n\t\t\t44,\n\t\t\t45,\n\t\t\t46,\n\t\t\t47,\n\t\t\t48\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t50,\n\t\t\t51,\n\t\t\t52,\n\t\t\t53,\n\t\t\t54,\n\t\t\t55,\n\t\t\t56,\n\t\t\t57,\n\t\t\t58\n\t\t]\n\t],\n\tuint16_t[3][2] = [\n\t\tuint16_t[3] = [\n\t\t\t0,\n\t\t\t1,\n\t\t\t2\n\t\t],\n\t\tuint16_t[3] = [\n\t\t\t3,\n\t\t\t4,\n\t\t\t5\n\t\t]\n\t]}"
+  value: "<unknown> {\n\texception_table: <unknown> = < The value of this variable may have been optimized out of the debug info, by the compiler. >,\n\t_aTerminalId: <unknown> = < The value of this variable may have been optimized out of the debug info, by the compiler. >,\n\t_SEGGER_RTT: SEGGER_RTT_CB = SEGGER_RTT_CB @ 0x20002000,\n\tchar[1024] = [\n\t\tH,\n\t\te,\n\t\tl,\n\t\tl,\n\t\to,\n\t\t ,\n\t\tH,\n\t\te,\n\t\tl,\n\t\tl,\n\n\t\t... and 1014 more\n\t],\n\tchar[16] = [\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\t\t\u0000,\n\n\t\t... and 6 more\n\t],\n\t_ActiveTerminal: <unknown> = < The value of this variable may have been optimized out of the debug info, by the compiler. >,\n\tfoo: Foo_t = Foo_t @ 0x20000000,\n\tconst Foo_t[2] = [\n\t\tFoo_t @ 0x00001684,\n\t\tFoo_t @ 0x0000168C\n\t],\n\tuint8_t[9][6] = [\n\t\tuint8_t[9] = [\n\t\t\t0,\n\t\t\t1,\n\t\t\t2,\n\t\t\t3,\n\t\t\t4,\n\t\t\t5,\n\t\t\t6,\n\t\t\t7,\n\t\t\t8\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t10,\n\t\t\t11,\n\t\t\t12,\n\t\t\t13,\n\t\t\t14,\n\t\t\t15,\n\t\t\t16,\n\t\t\t17,\n\t\t\t18\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t20,\n\t\t\t21,\n\t\t\t22,\n\t\t\t23,\n\t\t\t24,\n\t\t\t25,\n\t\t\t26,\n\t\t\t27,\n\t\t\t28\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t30,\n\t\t\t31,\n\t\t\t32,\n\t\t\t33,\n\t\t\t34,\n\t\t\t35,\n\t\t\t36,\n\t\t\t37,\n\t\t\t38\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t40,\n\t\t\t41,\n\t\t\t42,\n\t\t\t43,\n\t\t\t44,\n\t\t\t45,\n\t\t\t46,\n\t\t\t47,\n\t\t\t48\n\t\t],\n\t\tuint8_t[9] = [\n\t\t\t50,\n\t\t\t51,\n\t\t\t52,\n\t\t\t53,\n\t\t\t54,\n\t\t\t55,\n\t\t\t56,\n\t\t\t57,\n\t\t\t58\n\t\t]\n\t],\n\tuint16_t[3][2] = [\n\t\tuint16_t[3] = [\n\t\t\t0,\n\t\t\t1,\n\t\t\t2\n\t\t],\n\t\tuint16_t[3] = [\n\t\t\t3,\n\t\t\t4,\n\t\t\t5\n\t\t]\n\t]}"
   children:
     - name:
         Named: exception_table
@@ -15,9 +16,243 @@ Child Variables:
         Named: _aTerminalId
       type_name: Unknown
       value: "< The value of this variable may have been optimized out of the debug info, by the compiler. >"
-    - name: Unknown
-      type_name: Unknown
-      value: "Error: This is a bug! Attempted to evaluate a Variable with no type or no memory location"
+    - name:
+        Named: _SEGGER_RTT
+      type_name:
+        Modified:
+          - Typedef: SEGGER_RTT_CB
+          - Struct: "<unnamed struct>"
+      value: SEGGER_RTT_CB @ 0x20002000
+      children:
+        - name:
+            Named: acID
+          type_name:
+            Array:
+              item_type_name:
+                Base: char
+              count: 16
+          value: "char[16] = [\n\tS,\n\tE,\n\tG,\n\tG,\n\tE,\n\tR,\n\t ,\n\tR,\n\tT,\n\tT,\n\t... and 6 more]"
+          children:
+            - name:
+                Named: __0
+              type_name:
+                Base: char
+              value: S
+            - name:
+                Named: __1
+              type_name:
+                Base: char
+              value: E
+            - name:
+                Named: __2
+              type_name:
+                Base: char
+              value: G
+            - name:
+                Named: __3
+              type_name:
+                Base: char
+              value: G
+            - name:
+                Named: __4
+              type_name:
+                Base: char
+              value: E
+            - name:
+                Named: __5
+              type_name:
+                Base: char
+              value: R
+            - name:
+                Named: __6
+              type_name:
+                Base: char
+              value: " "
+            - name:
+                Named: __7
+              type_name:
+                Base: char
+              value: R
+            - name:
+                Named: __8
+              type_name:
+                Base: char
+              value: T
+            - name:
+                Named: __9
+              type_name:
+                Base: char
+              value: T
+            - name:
+                Named: __10
+              type_name:
+                Base: char
+              value: "\u0000"
+            - name:
+                Named: __11
+              type_name:
+                Base: char
+              value: "\u0000"
+            - name:
+                Named: __12
+              type_name:
+                Base: char
+              value: "\u0000"
+            - name:
+                Named: __13
+              type_name:
+                Base: char
+              value: "\u0000"
+            - name:
+                Named: __14
+              type_name:
+                Base: char
+              value: "\u0000"
+            - name:
+                Named: __15
+              type_name:
+                Base: char
+              value: "\u0000"
+        - name:
+            Named: MaxNumUpBuffers
+          type_name:
+            Base: int
+          value: "1"
+        - name:
+            Named: MaxNumDownBuffers
+          type_name:
+            Base: int
+          value: "1"
+        - name:
+            Named: aUp
+          type_name:
+            Array:
+              item_type_name:
+                Modified:
+                  - Typedef: SEGGER_RTT_BUFFER_UP
+                  - Struct: "<unnamed struct>"
+              count: 1
+          value: "SEGGER_RTT_BUFFER_UP[1] = [\n\tSEGGER_RTT_BUFFER_UP @ 0x20002018]"
+          children:
+            - name:
+                Named: __0
+              type_name:
+                Modified:
+                  - Typedef: SEGGER_RTT_BUFFER_UP
+                  - Struct: "<unnamed struct>"
+              value: SEGGER_RTT_BUFFER_UP @ 0x20002018
+              children:
+                - name:
+                    Named: sName
+                  type_name:
+                    Pointer: char
+                  value: char* @ 0x20002018
+                  children:
+                    - name:
+                        Named: "*sName"
+                      type_name:
+                        Modified:
+                          - Const
+                          - Base: char
+                      value: T
+                - name:
+                    Named: pBuffer
+                  type_name:
+                    Pointer: char
+                  value: char* @ 0x2000201C
+                  children:
+                    - name:
+                        Named: "*pBuffer"
+                      type_name:
+                        Base: char
+                      value: H
+                - name:
+                    Named: SizeOfBuffer
+                  type_name:
+                    Base: unsigned int
+                  value: "1024"
+                - name:
+                    Named: WrOff
+                  type_name:
+                    Base: unsigned int
+                  value: "113"
+                - name:
+                    Named: RdOff
+                  type_name:
+                    Modified:
+                      - Volatile
+                      - Base: unsigned int
+                  value: "113"
+                - name:
+                    Named: Flags
+                  type_name:
+                    Base: unsigned int
+                  value: "0"
+        - name:
+            Named: aDown
+          type_name:
+            Array:
+              item_type_name:
+                Modified:
+                  - Typedef: SEGGER_RTT_BUFFER_DOWN
+                  - Struct: "<unnamed struct>"
+              count: 1
+          value: "SEGGER_RTT_BUFFER_DOWN[1] = [\n\tSEGGER_RTT_BUFFER_DOWN @ 0x20002030]"
+          children:
+            - name:
+                Named: __0
+              type_name:
+                Modified:
+                  - Typedef: SEGGER_RTT_BUFFER_DOWN
+                  - Struct: "<unnamed struct>"
+              value: SEGGER_RTT_BUFFER_DOWN @ 0x20002030
+              children:
+                - name:
+                    Named: sName
+                  type_name:
+                    Pointer: char
+                  value: char* @ 0x20002030
+                  children:
+                    - name:
+                        Named: "*sName"
+                      type_name:
+                        Modified:
+                          - Const
+                          - Base: char
+                      value: T
+                - name:
+                    Named: pBuffer
+                  type_name:
+                    Pointer: char
+                  value: char* @ 0x20002034
+                  children:
+                    - name:
+                        Named: "*pBuffer"
+                      type_name:
+                        Base: char
+                      value: "\u0000"
+                - name:
+                    Named: SizeOfBuffer
+                  type_name:
+                    Base: unsigned int
+                  value: "16"
+                - name:
+                    Named: WrOff
+                  type_name:
+                    Modified:
+                      - Volatile
+                      - Base: unsigned int
+                  value: "0"
+                - name:
+                    Named: RdOff
+                  type_name:
+                    Base: unsigned int
+                  value: "0"
+                - name:
+                    Named: Flags
+                  type_name:
+                    Base: unsigned int
+                  value: "0"
     - name:
         Named: _acUpBuffer
       type_name: