probe_rs_debug/variable.rs
1use crate::{language::ProgrammingLanguage, unit_info::UnitInfo};
2
3use super::*;
4use gimli::{DebugInfoOffset, DwLang, UnitOffset};
5use itertools::Itertools;
6use probe_rs::RegisterValue;
7use std::ops::Range;
8
9/// Define the role that a variable plays in a Variant relationship. See section '5.7.10 Variant
10/// Entries' of the DWARF 5 specification
11#[derive(Debug, Clone, Eq, PartialEq, Default)]
12pub enum VariantRole {
13 /// A (parent) Variable that can have any number of Variant's as its value
14 VariantPart(u64),
15 /// A (child) Variable that defines one of many possible types to hold the current value of a
16 /// VariantPart.
17 Variant(u64),
18 /// This variable doesn't play a role in a Variant relationship
19 #[default]
20 NonVariant,
21}
22
23/// A [Variable] will have either a valid value, or some reason why a value could not be constructed.
24/// - If we encounter expected errors, they will be displayed to the user as defined below.
25/// - If we encounter unexpected errors, they will be treated as proper errors and will propagated
26/// to the calling process as an `Err()`
27#[derive(Clone, Debug, PartialEq, Eq, Default)]
28pub enum VariableValue {
29 /// A valid value of this variable
30 Valid(String),
31 /// Notify the user that we encountered a problem correctly resolving the variable.
32 /// - The variable will be visible to the user, as will the other field of the variable.
33 /// - The contained warning message will be displayed to the user.
34 /// - The debugger will not attempt to resolve additional fields or children of this variable.
35 Error(String),
36 /// The value has not been set. This could be because ...
37 /// - It is too early in the process to have discovered its value, or ...
38 /// - The variable cannot have a stored value, e.g. a `struct`. In this case, please use
39 /// `Variable::get_value` to infer a human readable value from the value of the struct's fields.
40 #[default]
41 Empty,
42}
43
44impl std::fmt::Display for VariableValue {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 VariableValue::Valid(value) => value.fmt(f),
48 VariableValue::Error(error) => write!(f, "< {error} >"),
49 VariableValue::Empty => write!(
50 f,
51 "Value not set. Please use Variable::get_value() to infer a human readable variable value"
52 ),
53 }
54 }
55}
56
57impl VariableValue {
58 /// Returns `true` if the variable resolver did not encounter an error, `false` otherwise.
59 pub fn is_valid(&self) -> bool {
60 !matches!(self, VariableValue::Error(_))
61 }
62
63 /// Returns `true` if no value or error is present, `false` otherwise.
64 pub fn is_empty(&self) -> bool {
65 matches!(self, VariableValue::Empty)
66 }
67}
68
69/// The type of variable we have at hand.
70#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize)]
71pub enum VariableName {
72 /// Top-level variable for static variables, child of a stack frame variable,
73 /// and holds all the static scoped variables which are directly visible to the
74 /// compile unit of the frame.
75 StaticScopeRoot,
76 /// Top-level variable for registers, child of a stack frame variable.
77 RegistersRoot,
78 /// Top-level variable for local scoped variables, child of a stack frame variable.
79 LocalScopeRoot,
80 /// Artificial variable, without a name (e.g. enum discriminant)
81 Artifical,
82 /// Anonymous namespace
83 AnonymousNamespace,
84 /// A Namespace with a specific name
85 Namespace(String),
86 /// Variable with a specific name
87 Named(String),
88 /// Entry of an array or similar
89 Indexed(u64),
90 /// Variable with an unknown name
91 #[default]
92 Unknown,
93}
94
95impl std::fmt::Display for VariableName {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 VariableName::StaticScopeRoot => write!(f, "Static Variable"),
99 VariableName::RegistersRoot => write!(f, "Platform Register"),
100 VariableName::LocalScopeRoot => write!(f, "Function Variable"),
101 VariableName::Artifical => write!(f, "<artifical>"),
102 VariableName::AnonymousNamespace => write!(f, "<anonymous_namespace>"),
103 VariableName::Namespace(name) => name.fmt(f),
104 VariableName::Named(name) => name.fmt(f),
105 VariableName::Indexed(index) => write!(f, "__{index}"),
106 VariableName::Unknown => write!(f, "<unknown>"),
107 }
108 }
109}
110
111/// Encode the nature of the Debug Information Entry in a way that we can resolve child nodes of a
112/// [Variable].
113///
114/// The rules for 'lazy loading'/deferred recursion of [Variable] children are described under each
115/// of the enum values.
116#[derive(Debug, PartialEq, Eq, Clone, Default)]
117pub enum VariableNodeType {
118 /// Use the `header_offset` and `type_offset` as direct references for recursing the variable
119 /// children. With the current implementation, the `type_offset` will point to a DIE with a tag
120 /// of `DW_TAG_structure_type`.
121 /// - Rule: For structured variables, we WILL NOT automatically expand their children, but we
122 /// have enough information to expand it on demand. Except if they fall into one of the
123 /// special cases handled by [VariableNodeType::RecurseToBaseType]
124 TypeOffset(DebugInfoOffset, UnitOffset),
125 /// Use the `header_offset` and `entries_offset` as direct references for recursing the variable
126 /// children.
127 /// - Rule: All top level variables in a [StackFrame] are automatically deferred, i.e
128 /// [VariableName::LocalScopeRoot], [VariableName::RegistersRoot].
129 DirectLookup(DebugInfoOffset, UnitOffset),
130 /// Look up information from all compilation units. This is used to resolve static variables, so
131 /// when [`VariableName::StaticScopeRoot`] is used.
132 UnitsLookup,
133 /// Sometimes it doesn't make sense to recurse the children of a specific node type
134 /// - Rule: Pointers to `unit` datatypes WILL NOT BE resolved, because it doesn't make sense.
135 /// - Rule: Once we determine that a variable can not be recursed further, we update the
136 /// variable_node_type to indicate that no further recursion is possible/required. This
137 /// can be because the variable is a 'base' data type, or because there was some kind of
138 /// error in processing the current node, so we don't want to incur cascading errors.
139 // TODO: Find code instances where we use magic values (e.g. u32::MAX) and replace with DoNotRecurse logic if appropriate.
140 DoNotRecurse,
141 /// Unless otherwise specified, always recurse the children of every node until we get to the
142 /// base data type.
143 /// - Rule: (Default) Unless it is prevented by any of the other rules, we always recurse the
144 /// children of these variables.
145 /// - Rule: Certain structured variables (e.g. `&str`, `Some`, `Ok`, `Err`, etc.) are set to
146 /// [VariableNodeType::RecurseToBaseType] to improve the debugger UX.
147 /// - Rule: Pointers to `const` variables WILL ALWAYS BE recursed, because they provide
148 /// essential information, for example about the length of strings, or the size of
149 /// arrays.
150 /// - Rule: Enumerated types WILL ALWAYS BE recursed, because we only ever want to see the
151 /// 'active' child as the value.
152 /// - Rule: For now, Array types WILL ALWAYS BE recursed. TODO: Evaluate if it is beneficial to
153 /// defer these.
154 /// - Rule: For now, Union types WILL ALWAYS BE recursed. TODO: Evaluate if it is beneficial to
155 /// defer these.
156 #[default]
157 RecurseToBaseType,
158}
159
160impl VariableNodeType {
161 /// Will return `true` if the `variable_node_type` value implies that the variable will be
162 /// 'lazy' resolved.
163 pub fn is_deferred(&self) -> bool {
164 match self {
165 VariableNodeType::TypeOffset(_, _)
166 | VariableNodeType::DirectLookup(_, _)
167 | VariableNodeType::UnitsLookup => true,
168 VariableNodeType::DoNotRecurse | VariableNodeType::RecurseToBaseType => false,
169 }
170 }
171}
172
173/// The starting bit (and direction) of a bit field type.
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
175pub enum BitOffset {
176 /// The bit offset is from the least significant bit.
177 FromLsb(u64),
178 /// The bit offset is from the most significant bit.
179 FromMsb(u64),
180}
181
182/// Bitfield information for a variable.
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
184pub struct Bitfield {
185 /// The starting bit (and direction) of a bit field type.
186 pub offset: BitOffset,
187 /// The length of the bit field.
188 pub length: u64,
189}
190
191impl Default for Bitfield {
192 fn default() -> Self {
193 Bitfield {
194 offset: BitOffset::FromLsb(0),
195 length: 0,
196 }
197 }
198}
199
200impl Bitfield {
201 /// Returns a Bitfield that has a FromLsb offset.
202 pub(crate) fn normalize(&self, byte_size: u64) -> Self {
203 let offset = self.offset(byte_size);
204 Bitfield {
205 offset: BitOffset::FromLsb(offset),
206 length: self.length,
207 }
208 }
209
210 pub(crate) fn offset(&self, byte_size: u64) -> u64 {
211 match self.offset {
212 BitOffset::FromLsb(offset) => offset,
213 BitOffset::FromMsb(offset) => byte_size * 8 - offset - self.length,
214 }
215 }
216
217 pub(crate) fn normalized_offset(&self) -> u64 {
218 match self.offset {
219 BitOffset::FromLsb(offset) => offset,
220 BitOffset::FromMsb(_) => unreachable!("Bitfield should have been normalized first"),
221 }
222 }
223
224 pub(crate) fn length(&self) -> u64 {
225 self.length
226 }
227
228 pub(crate) fn mask(&self) -> u128 {
229 (1 << self.length) - 1
230 }
231
232 pub(crate) fn extract(&self, value: u128) -> u128 {
233 let offset = self.normalized_offset();
234 let mask = self.mask();
235
236 (value >> offset) & mask
237 }
238
239 pub(crate) fn insert(&self, value: u128, new_value: u128) -> u128 {
240 let offset = self.normalized_offset();
241 let mask = self.mask();
242
243 let shifted_mask = mask << offset;
244 let new_value = (new_value & mask) << offset;
245 (value & !shifted_mask) | new_value
246 }
247}
248
249/// A modifier to a variable type. Currently only used to format the type name.
250#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
251pub enum Modifier {
252 /// The type is declared as `volatile`.
253 Volatile,
254
255 /// The type is declared as `const`.
256 Const,
257
258 /// The type is declared as `restrict`.
259 Restrict,
260
261 /// The type is declared as `atomic`.
262 Atomic,
263
264 /// The type is an alias with the given name.
265 Typedef(String),
266}
267
268/// The variants of VariableType allows us to streamline the conditional logic that requires
269/// specific handling depending on the nature of the variable.
270#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
271pub enum VariableType {
272 /// A variable with a Rust base datatype.
273 Base(String),
274 /// The variable is a range of bits in a wider (integer) type.
275 Bitfield(Bitfield, Box<VariableType>),
276 /// A Rust struct.
277 Struct(String),
278 /// A Rust enum.
279 Enum(String),
280 /// Namespace refers to the path that qualifies a variable. e.g. "std::string" is the namespace
281 /// for the struct "String"
282 Namespace,
283 /// A Pointer is a variable that contains a reference to another variable, and the type of the
284 /// referenced variable may not be known until the reference has been resolved.
285 Pointer(Option<String>),
286 /// A Rust array.
287 Array {
288 /// The type name of the variable.
289 item_type_name: Box<VariableType>,
290 /// The number of entries in the array.
291 count: usize,
292 },
293 /// A type alias.
294 Modified(Modifier, Box<VariableType>),
295 /// When we are unable to determine the name of a variable.
296 #[default]
297 Unknown,
298 /// For infrequently used categories of variables that does not fall into any of the other
299 /// `VariableType` variants.
300 Other(String),
301}
302
303impl VariableType {
304 /// Get the inner type of a modified type.
305 pub fn inner(&self) -> &Self {
306 if let Self::Modified(_, ty) = self {
307 ty.inner()
308 } else {
309 self
310 }
311 }
312
313 /// Get the inner type of a modified type, stopping at typedef aliases.
314 fn skip_modifiers(&self) -> &Self {
315 match self {
316 Self::Modified(Modifier::Typedef(_), _) => self,
317 Self::Modified(_, ty) => ty.skip_modifiers(),
318 _ => self,
319 }
320 }
321
322 /// Is this variable of a Rust PhantomData marker type?
323 pub fn is_phantom_data(&self) -> bool {
324 match self {
325 VariableType::Struct(name) => name.starts_with("PhantomData"),
326 _ => false,
327 }
328 }
329
330 /// Is this variable an array?
331 pub fn is_array(&self) -> bool {
332 matches!(self, VariableType::Array { .. })
333 }
334
335 /// Returns the string representation of the variable type's kind.
336 pub fn kind(&self) -> &str {
337 match self {
338 VariableType::Base(_) => "base",
339 VariableType::Bitfield(..) => "bitfield",
340 VariableType::Struct(_) => "struct",
341 VariableType::Enum(_) => "enum",
342 VariableType::Namespace => "namespace",
343 VariableType::Pointer(_) => "pointer",
344 VariableType::Array { .. } => "array",
345 VariableType::Unknown => "unknown",
346 VariableType::Other(_) => "other",
347 VariableType::Modified(_, inner) => inner.kind(),
348 }
349 }
350
351 pub(crate) fn display_name(&self, language: &dyn ProgrammingLanguage) -> String {
352 match self {
353 VariableType::Modified(Modifier::Typedef(name), _) => name.clone(),
354 VariableType::Modified(modifier, ty) => {
355 language.modified_type_name(modifier, &ty.display_name(language))
356 }
357
358 VariableType::Array {
359 item_type_name,
360 count,
361 } => language.format_array_type(
362 // In case the compiler points at a modified item type (e.g. const), skip the
363 // modifier.
364 &item_type_name.skip_modifiers().display_name(language),
365 *count,
366 ),
367
368 VariableType::Bitfield(bitfield, name) => {
369 language.format_bitfield_type(&name.display_name(language), *bitfield)
370 }
371
372 _ => self.type_name(language),
373 }
374 }
375
376 /// Returns the type name after resolving aliases.
377 pub(crate) fn type_name(&self, language: &dyn ProgrammingLanguage) -> String {
378 let type_name = match self {
379 VariableType::Base(name)
380 | VariableType::Struct(name)
381 | VariableType::Enum(name)
382 | VariableType::Other(name) => Some(name.as_str()),
383
384 VariableType::Namespace => Some("namespace"),
385 VariableType::Unknown => None,
386
387 VariableType::Pointer(pointee) => {
388 // TODO: we should also carry the constness
389 return language.format_pointer_type(pointee.as_deref());
390 }
391
392 VariableType::Array {
393 item_type_name,
394 count,
395 } => return language.format_array_type(&item_type_name.type_name(language), *count),
396
397 VariableType::Bitfield(_, ty) | VariableType::Modified(_, ty) => {
398 return ty.type_name(language);
399 }
400 };
401
402 type_name.unwrap_or("<unknown>").to_string()
403 }
404}
405
406/// Location of a variable
407#[derive(Debug, Clone, PartialEq, Default)]
408pub enum VariableLocation {
409 /// Location of the variable is not known. This means that it has not been evaluated yet.
410 #[default]
411 Unknown,
412 /// The variable does not have a location currently, probably due to optimisations.
413 Unavailable,
414 /// The variable can be found in memory, at this address.
415 Address(u64),
416 /// The value of the variable is directly available.
417 Value,
418 /// The variable is stored in a register, and the value is read from there.
419 RegisterValue(RegisterValue),
420 /// There was an error evaluating the variable location.
421 Error(String),
422 /// Support for handling the location of this variable is not (yet) implemented.
423 Unsupported(String),
424}
425
426impl VariableLocation {
427 /// Return the memory address, if available. Otherwise an error is returned.
428 pub fn memory_address(&self) -> Result<u64, DebugError> {
429 match self {
430 VariableLocation::Address(address) => Ok(*address),
431 VariableLocation::RegisterValue(address) => match TryInto::<u64>::try_into(*address) {
432 Ok(address) => Ok(address),
433 Err(_) => Err(DebugError::WarnAndContinue {
434 message: "Register value is not a valid address".to_string(),
435 }),
436 },
437 VariableLocation::Error(error) => Err(DebugError::WarnAndContinue {
438 message: error.clone(),
439 }),
440 other => Err(DebugError::WarnAndContinue {
441 message: format!("Variable does not have a memory location: location={other:?}"),
442 }),
443 }
444 }
445
446 /// Check if the location is valid, ie. not an error, unsupported, or unavailable.
447 pub fn valid(&self) -> bool {
448 match self {
449 VariableLocation::Address(_)
450 | VariableLocation::RegisterValue(_)
451 | VariableLocation::Value
452 | VariableLocation::Unknown => true,
453 _other => false,
454 }
455 }
456}
457
458impl std::fmt::Display for VariableLocation {
459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460 match self {
461 VariableLocation::Unknown => "<unknown value>".fmt(f),
462 VariableLocation::Unavailable => "<value not available>".fmt(f),
463 VariableLocation::Address(address) => {
464 write!(f, "{address:#010X}")
465 }
466 VariableLocation::RegisterValue(address) => match address {
467 RegisterValue::U32(value) => write!(f, "{value:#010X}"),
468 RegisterValue::U64(value) => write!(f, "{value:#018X}"),
469 RegisterValue::U128(value) => write!(f, "{value:#034X}"),
470 },
471 VariableLocation::Value => "<not applicable - statically stored value>".fmt(f),
472 VariableLocation::Error(error) => error.fmt(f),
473 VariableLocation::Unsupported(reason) => reason.fmt(f),
474 }
475 }
476}
477
478/// The `Variable` struct is used in conjunction with `VariableCache` to cache data about variables.
479///
480/// Any modifications to the `Variable` value will be transient (lost when it goes out of scope),
481/// unless it is updated through one of the available methods on `VariableCache`.
482#[derive(Debug, Clone, PartialEq)]
483pub struct Variable {
484 /// Every variable must have a unique key value assigned to it.
485 /// The value will be zero until it is stored in VariableCache, at which time its value will be
486 /// set to the same as the VariableCache::variable_cache_key
487 pub(super) variable_key: ObjectRef,
488 /// The offset to the variable's type information.
489 pub(crate) type_node_offset: Option<UnitOffset>,
490 /// Every variable must have a unique parent assigned to it when stored in the VariableCache.
491 pub parent_key: ObjectRef,
492 /// The variable name refers to the name of any of the types of values described in the [VariableCache]
493 pub name: VariableName,
494
495 /// Linkage name of the variable. Multiple variables with the same name could exist,
496 /// this is used to distinguish between them.
497 pub(crate) linkage_name: Option<String>,
498
499 /// Use `Variable::set_value()` and `Variable::get_value()` to correctly process this `value`
500 pub(super) value: VariableValue,
501 /// The source location of the declaration of this variable, if available.
502 pub source_location: Option<SourceLocation>,
503 /// Programming language of the defining compilation unit.
504 pub language: DwLang,
505
506 /// The name of the type of this variable.
507 pub type_name: VariableType,
508 /// For 'lazy loading' of certain variable types we have to determine if the variable recursion
509 /// should be deferred, and if so, how to resolve it when the request for further recursion
510 /// happens.
511 /// See [VariableNodeType] for more information.
512 pub variable_node_type: VariableNodeType,
513 /// The starting location/address in memory where this Variable's value is stored.
514 pub memory_location: VariableLocation,
515 /// The size of this variable in bytes.
516 pub byte_size: Option<u64>,
517 /// The role of this variable.
518 pub role: VariantRole,
519}
520
521impl Variable {
522 /// In most cases, Variables will be initialized with their ELF references so that we resolve
523 /// their data types and values on demand.
524 pub fn new(unit_info: Option<&UnitInfo>) -> Variable {
525 Variable {
526 language: unit_info
527 .map(|info| info.get_language())
528 .unwrap_or(gimli::DW_LANG_Rust),
529 type_node_offset: None,
530 variable_key: Default::default(),
531 parent_key: Default::default(),
532 name: Default::default(),
533 linkage_name: None,
534 value: Default::default(),
535 source_location: None,
536 type_name: Default::default(),
537 variable_node_type: Default::default(),
538 memory_location: Default::default(),
539 byte_size: None,
540 role: Default::default(),
541 }
542 }
543
544 /// Returns the readable name of the variable type.
545 pub fn type_name(&self) -> String {
546 self.type_name
547 .display_name(language::from_dwarf(self.language).as_ref())
548 }
549
550 /// Get a unique key for this variable.
551 pub fn variable_key(&self) -> ObjectRef {
552 self.variable_key
553 }
554
555 /// This ensures debug frontends can see the errors, but doesn't fail because of a single
556 /// variable not being able to decode correctly.
557 pub fn set_value(&mut self, new_value: VariableValue) {
558 // Allow some block when logic requires it.
559 if new_value.is_valid() || self.value.is_valid() {
560 // Simply overwrite existing value with a new valid one.
561 self.value = new_value;
562 } else {
563 // Concatenate the error messages ...
564 self.value = VariableValue::Error(format!("{} : {}", self.value, new_value));
565
566 // If the value is invalid, then make sure we don't propagate invalid memory location
567 // values.
568 self.memory_location =
569 VariableLocation::Error("Failed to resolve variable value".to_string());
570 }
571 }
572
573 /// Convert the [String] value into the appropriate memory format and update the target memory
574 /// with the new value.
575 /// Currently this only works for base data types. There is no provision in the MS DAP API to
576 /// catch this client side, so we can only respond with a 'gentle' error message if the user
577 /// attempts unsupported data types.
578 pub fn update_value(
579 &self,
580 memory: &mut impl MemoryInterface,
581 variable_cache: &mut VariableCache,
582 new_value: String,
583 ) -> Result<(), DebugError> {
584 let valid_value = self.is_valid();
585 let valid_type = self.type_name != VariableType::Unknown;
586 let valid_memory = self.memory_location.valid();
587 if !valid_value || !valid_type || !valid_memory {
588 // Insufficient data available.
589 Err(DebugError::Other(format!(
590 "Cannot update variable: {:?}, with supplied information (value={:?}, type={:?}, memory location={:#010x?}).",
591 self.name, self.value, self.type_name, self.memory_location
592 )))
593 } else {
594 // We have everything we need to update the variable value.
595 language::from_dwarf(self.language)
596 .update_variable(self, memory, &new_value)
597 .map_err(|error| DebugError::WarnAndContinue {
598 message: format!("Invalid data value={new_value:?}: {error}"),
599 })?;
600
601 // Now update the cache with the new value for this variable.
602 let mut cache_variable = self.clone();
603 cache_variable.value = VariableValue::Valid(new_value);
604 cache_variable.extract_value(memory, variable_cache);
605 variable_cache.update_variable(&cache_variable)?;
606 Ok(())
607 }
608 }
609
610 /// Implementing get_value(), because Variable.value has to be private (a requirement of
611 /// updating the value without overriding earlier values ... see set_value()).
612 pub fn to_string(&self, variable_cache: &VariableCache) -> String {
613 // Allow for chained `if let` without complaining
614 if !self.value.is_empty() {
615 // The `value` for this `Variable` is non empty because either
616 // - It is base data type for which a value was determined based on the core runtime
617 // - We encountered an error somewhere, so report it to the user
618 return format!("{}", self.value);
619 }
620
621 if matches!(
622 self.name,
623 VariableName::AnonymousNamespace | VariableName::Namespace(_)
624 ) {
625 // Namespaces do not have values
626 return String::new();
627 }
628
629 // We need to construct a 'human readable' value using `fmt::Display` to represent the
630 // values of complex types and pointers.
631 if variable_cache.has_children(self) {
632 self.formatted_variable_value(variable_cache, 0, false)
633 .unwrap_or_default()
634 } else if self.type_name == VariableType::Unknown || !self.memory_location.valid() {
635 if self.variable_node_type.is_deferred() {
636 // When we will do a lazy-load of variable children, and they have not yet been
637 // requested by the user, just display the type_name as the value
638 self.type_name()
639 } else if let VariableLocation::Error(ref error) = self.memory_location {
640 error.clone()
641 } else {
642 // This condition should only be true for intermediate nodes
643 // from DWARF. These should not show up in the final
644 // `VariableCache`. If a user sees this error, then there is
645 // a logic problem in the stack unwind
646 "Error: This is a bug! Attempted to evaluate a Variable with no type or no memory location".to_string()
647 }
648 } else if matches!(self.type_name, VariableType::Struct(ref name) if name == "None") {
649 "None".to_string()
650 } else if matches!(self.type_name, VariableType::Array { count: 0, .. }) {
651 self.formatted_variable_value(variable_cache, 0, false)
652 .unwrap_or_default()
653 } else {
654 format!(
655 "Unimplemented: Get value of type {:?} of ({:?} bytes) at location {}",
656 self.type_name, self.byte_size, self.memory_location
657 )
658 }
659 }
660
661 /// Evaluate the variable's result if possible and set self.value, or else set self.value as the error String.
662 pub fn extract_value(
663 &mut self,
664 memory: &mut dyn MemoryInterface,
665 variable_cache: &VariableCache,
666 ) {
667 if let VariableValue::Error(_) = self.value {
668 // Nothing more to do ...
669 return;
670 }
671
672 let empty = self.value.is_empty();
673 // The value was set explicitly, so just leave it as is, or it was an error, so don't attempt
674 // anything else
675 let valid = self.memory_location.valid();
676 // This may just be that we are early on in the process of `Variable` evaluation
677 let unknown = self.type_name.inner() == &VariableType::Unknown;
678
679 if !empty || !valid || unknown {
680 return;
681 }
682
683 if self.variable_node_type.is_deferred()
684 || matches!(self.type_name, VariableType::Pointer(_))
685 {
686 // And we have not previously assigned the value, then assign the type and address as
687 // the value.
688 self.value =
689 VariableValue::Valid(format!("{} @ {}", self.type_name(), self.memory_location));
690 return;
691 }
692
693 tracing::trace!(
694 "Extracting value for {:?}, type={:?}",
695 self.name,
696 self.type_name
697 );
698
699 self.value =
700 language::from_dwarf(self.language).read_variable_value(self, memory, variable_cache);
701 }
702
703 /// The variable is considered to be an 'indexed' variable if the name starts with two
704 /// underscores followed by a number. e.g. "__1".
705 // TODO: Consider replacing this logic with `std::str::pattern::Pattern` when that API stabilizes
706 pub fn is_indexed(&self) -> bool {
707 match &self.name {
708 VariableName::Named(name) => {
709 name.starts_with("__")
710 && name
711 .find(char::is_numeric)
712 .is_some_and(|zero_based_position| zero_based_position == 2)
713 }
714 // Other kind of variables are never indexed
715 _ => false,
716 }
717 }
718
719 /// Returns `true` if the variable has a name, `false` otherwise.
720 pub fn is_named(&self) -> bool {
721 matches!(&self.name, VariableName::Named(_))
722 }
723
724 /// `true` if the Variable has a valid value, or an empty value.
725 /// `false` if the Variable has a VariableValue::Error(_) value
726 pub fn is_valid(&self) -> bool {
727 self.value.is_valid()
728 }
729
730 /// Format the variable.
731 fn formatted_variable_value(
732 &self,
733 variable_cache: &VariableCache,
734 indentation: usize,
735 show_name: bool,
736 ) -> Option<String> {
737 let type_name = self.type_name();
738
739 if !self.value.is_empty() {
740 // This is the end of the recursion where we already have a scalar
741 // value for a variable and we can just move it up.
742 let line_start = line_indent_string(indentation);
743 return Some(if show_name {
744 format!("{line_start}{}: {} = {}", self.name, type_name, self.value)
745 } else {
746 format!("{line_start}{}", self.value)
747 });
748 } else if matches!(
749 self.name,
750 VariableName::AnonymousNamespace | VariableName::Namespace(_)
751 ) {
752 // Namespaces do not have values, so we report no value up.
753 // This will alow us to filter it out when we concatenate children.
754 return None;
755 }
756
757 // Infer a human readable value using the available children of this variable.
758 let children = &mut variable_cache.get_children(self.variable_key);
759 let first_child = children.clone().next();
760
761 // Make sure we can safely unwrap() children.
762 Some(match self.type_name.inner() {
763 VariableType::Pointer(_) => {
764 format_pointer_value(variable_cache, indentation, first_child)
765 }
766 VariableType::Array { .. } => {
767 format_array_value(variable_cache, indentation, children, &type_name)
768 }
769 VariableType::Struct(name) if name == "Some" || name == "Ok" || name == "Err" => {
770 format_struct_value(variable_cache, indentation, children, &type_name)
771 }
772 _ if first_child.is_none() => {
773 // This is a struct with no children, so just print the type name.
774 // This is for example the None value of an Option or the empty type ().
775 type_name
776 }
777 _ if matches!(
778 self.name,
779 VariableName::StaticScopeRoot
780 | VariableName::LocalScopeRoot
781 | VariableName::RegistersRoot
782 ) =>
783 {
784 format_root_value(variable_cache, indentation, children, &type_name)
785 }
786 _ => format_default_value(
787 variable_cache,
788 indentation,
789 &self.name,
790 children,
791 &type_name,
792 show_name,
793 ),
794 })
795 }
796
797 /// Calculate the memory range that contains the value of this variable.
798 ///
799 /// If the location and/or byte size is not known, then return None.
800 /// Note: We don't do any validation of the memory range here and leave it
801 /// up to the caller to validate the memory ranges before attempting to read
802 /// them.
803 pub fn memory_range(&self) -> Option<Range<u64>> {
804 let VariableLocation::Address(address) = self.memory_location else {
805 return None;
806 };
807
808 self.byte_size.map(|byte_size| {
809 if byte_size == 0 {
810 address..address + 4
811 } else {
812 address..(address + byte_size)
813 }
814 })
815 }
816}
817
818/// Format a pointer value
819///
820/// Formats the pointed to value and potential subsequent children as well.
821fn format_pointer_value(
822 variable_cache: &VariableCache,
823 indentation: usize,
824 first_child: Option<&Variable>,
825) -> String {
826 let line_start = line_indent_string(indentation);
827
828 let value = if let Some(first_child) = first_child {
829 first_child
830 .formatted_variable_value(variable_cache, indentation + 1, true)
831 .expect("a child. This is a bug. Please report it.")
832 } else {
833 "Unable to resolve referenced variable value".to_string()
834 };
835
836 format!("{line_start}{value}")
837}
838
839/// Format any array like value.
840///
841/// Recursively formats all child values.
842fn format_array_value<'a>(
843 variable_cache: &VariableCache,
844 indentation: usize,
845 children: &mut (impl Iterator<Item = &'a Variable> + Clone),
846 type_name: &str,
847) -> String {
848 let line_start = line_indent_string(indentation);
849
850 // Limit arrays to 10 elements
851 const ARRAY_MAX_LENGTH: usize = 10;
852
853 // If we at least ARRAY_MAX_LENGTH + 2 items in the iterator, cap at ARRAY_MAX_LENGTH.
854 // If we have less, cap at the actual number of items.
855 // This helps us to never write "and 1 more" with the reasoning that the space used for this
856 // text, can be used for printing that one item.
857 let count = children.clone().count();
858 let take = if count > ARRAY_MAX_LENGTH + 1 {
859 ARRAY_MAX_LENGTH
860 } else {
861 count
862 };
863
864 let children_values = children
865 .by_ref()
866 .take(take)
867 .filter_map(|child| child.formatted_variable_value(variable_cache, indentation + 1, false))
868 .join(",");
869
870 let remainder = if count > ARRAY_MAX_LENGTH + 1 {
871 format!(",\n{line_start}\t... and {} more", count - take)
872 } else {
873 String::new()
874 };
875
876 format!("{line_start}{type_name} = [{children_values}{remainder}{line_start}]")
877}
878
879/// Format any struct like value .
880///
881/// Recursively formats all child values.
882fn format_struct_value<'a>(
883 variable_cache: &VariableCache,
884 indentation: usize,
885 children: &mut (impl Iterator<Item = &'a Variable> + Clone),
886 type_name: &str,
887) -> String {
888 let line_start = line_indent_string(indentation);
889
890 // FIXME: this is not hit by any of the unwind tests, which is weird because
891 // some of them contain `Some` structs.
892 // Handle special structure types like the variant values of `Option<>` and `Result<>`
893 let children_values = format_children_values(variable_cache, indentation, children, false);
894
895 format!("{line_start}{type_name} = ({children_values})")
896}
897
898/// Format any root value.
899///
900/// Recursively formats all child values.
901fn format_root_value<'a>(
902 variable_cache: &VariableCache,
903 indentation: usize,
904 children: &mut (impl Iterator<Item = &'a Variable> + Clone),
905 type_name: &str,
906) -> String {
907 let line_start = line_indent_string(indentation);
908
909 let children_values = format_children_values(variable_cache, indentation, children, true);
910 format!("{line_start}{type_name} {{{children_values}{line_start}}}")
911}
912
913/// Format any value that has no type that requires special handling.
914///
915/// Recursively formats all child values.
916fn format_default_value<'a>(
917 variable_cache: &VariableCache,
918 indentation: usize,
919 name: &VariableName,
920 children: &mut (impl Iterator<Item = &'a Variable> + Clone),
921 type_name: &String,
922 show_name: bool,
923) -> String {
924 let line_start = line_indent_string(indentation);
925
926 // Find the first child of the structure if it exists.
927 let child = children.clone().find(|v| v.is_named());
928
929 // If we do not have children, exit early because we cannot print more specifics (children)
930 // of this variable type. We instead print the empty type symbol.
931 let Some(child) = child else {
932 return "()".to_string();
933 };
934
935 let child_type_name = child.type_name();
936 if child.is_indexed() {
937 // Treat this structure as a tuple
938 let children_values = format_children_values(variable_cache, indentation, children, false);
939 let name = if show_name {
940 format!("{name}: {type_name}({child_type_name}) = ")
941 } else {
942 String::new()
943 };
944 format!("{line_start}{name}{type_name}({children_values}{line_start})")
945 } else {
946 // Treat this structure as a `struct`
947 let children_values = format_children_values(variable_cache, indentation, children, true);
948 let name = if show_name {
949 format!("{name}: {type_name} = ")
950 } else {
951 String::new()
952 };
953 format!("{line_start}{name}{type_name} {{{children_values}{line_start}}}")
954 }
955}
956
957/// Concatenate all children values with a comma.
958fn format_children_values<'a>(
959 variable_cache: &VariableCache,
960 indentation: usize,
961 children: &mut (impl Iterator<Item = &'a Variable> + Clone),
962 show_name: bool,
963) -> String {
964 children
965 .filter_map(|child| {
966 child.formatted_variable_value(variable_cache, indentation + 1, show_name)
967 })
968 .join(",")
969}
970
971/// Genarate a string that indents the line exactly the right amount.
972/// Includes a newline at the start if the indentation is bigger than 0.
973fn line_indent_string(indentation: usize) -> String {
974 let line_feed = if indentation == 0 { "" } else { "\n" };
975 format!("{line_feed}{:\t<indentation$}", "")
976}