stackdump_trace/variables/
mod.rs

1//! A module containing functions for finding and reading the variables of frames.
2//!
3//! These are (almost) all pure functions that get all of their context through the parameters.
4//! This was decided because the datastructures that are involved are pretty complex and I didn't
5//! want to add complexity.
6//! All functions can be reasoned with on the function level.
7//!
8
9use crate::{
10    error::TraceError,
11    get_entry_type_reference_tree_recursive,
12    gimli_extensions::{AttributeExt, DebuggingInformationEntryExt},
13    type_value_tree::{
14        value::{StringFormat, Value},
15        variable_type::{Archetype, VariableType},
16        TypeValue, TypeValueNode, TypeValueTree, VariableDataError,
17    },
18    DefaultReader, Location, Variable, VariableKind, VariableLocationResult,
19};
20use bitvec::prelude::*;
21use gimli::{
22    Abbreviations, Attribute, AttributeValue, DebugInfoOffset, DebuggingInformationEntry, Dwarf,
23    EntriesTree, Evaluation, EvaluationResult, Piece, Reader, Unit, UnitHeader, UnitOffset,
24};
25use stackdump_core::device_memory::DeviceMemory;
26use std::{collections::HashMap, pin::Pin};
27
28mod type_value_tree_building;
29
30fn div_ceil(lhs: u64, rhs: u64) -> u64 {
31    let d = lhs / rhs;
32    let r = lhs % rhs;
33    if r > 0 && rhs > 0 {
34        d + 1
35    } else {
36        d
37    }
38}
39/// Gets the string value from the `DW_AT_name` attribute of the given entry
40fn get_entry_name(
41    dwarf: &Dwarf<DefaultReader>,
42    unit: &Unit<DefaultReader, usize>,
43    entry: &DebuggingInformationEntry<DefaultReader, usize>,
44) -> Result<String, TraceError> {
45    if entry.tag() == gimli::constants::DW_TAG_volatile_type
46        || entry.tag() == gimli::constants::DW_TAG_const_type
47    {
48        // These tags don't have a name of their own,
49        // so we must follow the the DW_AT_type attribute that points to another entry
50
51        let abbreviations = dwarf.abbreviations(&unit.header)?;
52
53        get_entry_type_reference_tree_recursive!(
54            variable_type_value_tree = (dwarf, unit, &abbreviations, entry)
55        );
56
57        get_entry_name(dwarf, unit, variable_type_value_tree?.root()?.entry())
58    } else {
59        // Find the attribute
60        let name_attr = entry.required_attr(&unit.header, gimli::constants::DW_AT_name);
61
62        match name_attr {
63            Ok(name_attr) => {
64                // Read as a string type
65                let attr_string = dwarf.attr_string(unit, name_attr.value())?;
66                // Convert to String
67                Ok(attr_string.to_string()?.into())
68            }
69            Err(_) if entry.tag() == gimli::constants::DW_TAG_array_type => {
70                // Arrays can be anonymous
71                Ok("array".into())
72            }
73            Err(_) if entry.tag() == gimli::constants::DW_TAG_subroutine_type => {
74                // Subroutines can be anonymous
75                Ok("subroutine".into())
76            }
77            Err(e) => Err(e),
78        }
79    }
80}
81
82/// Gets the string value from the `DW_AT_linkage_name` attribute of the given entry if it's present
83fn get_entry_linkage_name(
84    dwarf: &Dwarf<DefaultReader>,
85    unit: &Unit<DefaultReader, usize>,
86    entry: &DebuggingInformationEntry<DefaultReader, usize>,
87) -> Result<Option<String>, TraceError> {
88    // Find the attribute
89    let Some(linkage_name_attr) = entry.attr(gimli::constants::DW_AT_linkage_name)? else {
90        return Ok(None);
91    };
92
93    let attr_string = dwarf.attr_string(unit, linkage_name_attr.value())?;
94    Ok(Some(attr_string.to_string()?.into()))
95}
96
97/// If available, get the EntriesTree of the `DW_AT_abstract_origin` attribute of the given entry
98fn get_entry_abstract_origin_reference_tree<'abbrev, 'unit>(
99    dwarf: &Dwarf<DefaultReader>,
100    unit_header: &'unit UnitHeader<DefaultReader>,
101    abbreviations: &'abbrev Abbreviations,
102    entry: &DebuggingInformationEntry<DefaultReader>,
103) -> Result<EntriesTree<'abbrev, 'unit, DefaultReader>, GetEntryTreeError> {
104    // Find the attribute
105    let abstract_origin_attr = entry
106        .required_attr(unit_header, gimli::constants::DW_AT_abstract_origin)
107        .map_err(GetEntryTreeError::TraceError)?;
108
109    // Check its offset
110    match abstract_origin_attr.value() {
111        AttributeValue::UnitRef(offset) => {
112            // Get the entries for the type
113            Ok(unit_header
114                .entries_tree(abbreviations, Some(offset))
115                .map_err(|e| GetEntryTreeError::TraceError(e.into()))?)
116        }
117        AttributeValue::DebugInfoRef(offset) => {
118            if let Some(offset) = offset.to_unit_offset(unit_header) {
119                return unit_header
120                    .entries_tree(abbreviations, Some(offset))
121                    .map_err(|e| GetEntryTreeError::TraceError(e.into()));
122            }
123
124            // The offset is not in our current compilation unit. Let's see if we can find the correct one
125            let mut units = dwarf.units();
126            while let Ok(Some(unit_header)) = units.next() {
127                if offset.to_unit_offset(&unit_header).is_some() {
128                    // Yes, we've found the unit. We return it so that the caller can recall us with the correct unit.
129                    // We can't use the correct unit ourselves, because the EntriesTree borrows the unit
130                    return Err(GetEntryTreeError::WrongUnit(unit_header));
131                }
132            }
133
134            Err(GetEntryTreeError::TraceError(
135                TraceError::DebugInfoOffsetUnitNotFound {
136                    debug_info_offset: offset.0,
137                },
138            ))
139        }
140        value => Err(GetEntryTreeError::TraceError(
141            TraceError::WrongAttributeValueType {
142                attribute_name: abstract_origin_attr.name().to_string(),
143                expected_type_name: "UnitRef or DebugInfoRef",
144                gotten_value: format!("{value:X?}"),
145            },
146        )),
147    }
148}
149
150/// Get the EntriesTree of the `DW_AT_type` attribute of the given entry
151fn get_entry_type_reference_tree<'abbrev, 'unit>(
152    dwarf: &Dwarf<DefaultReader>,
153    unit_header: &'unit UnitHeader<DefaultReader, usize>,
154    abbreviations: &'abbrev Abbreviations,
155    entry: &DebuggingInformationEntry<DefaultReader, usize>,
156) -> Result<EntriesTree<'abbrev, 'unit, DefaultReader>, GetEntryTreeError> {
157    // Find the attribute
158    let type_attr = entry
159        .required_attr(unit_header, gimli::constants::DW_AT_type)
160        .map_err(GetEntryTreeError::TraceError)?;
161
162    // Check its offset
163    match type_attr.value() {
164        AttributeValue::UnitRef(offset) => {
165            // Get the entries for the type
166            Ok(unit_header
167                .entries_tree(abbreviations, Some(offset))
168                .map_err(|e| GetEntryTreeError::TraceError(e.into()))?)
169        }
170        AttributeValue::DebugInfoRef(offset) => {
171            if let Some(offset) = offset.to_unit_offset(unit_header) {
172                return unit_header
173                    .entries_tree(abbreviations, Some(offset))
174                    .map_err(|e| GetEntryTreeError::TraceError(e.into()));
175            }
176
177            // The offset is not in our current compilation unit. Let's see if we can find the correct one
178            let mut units = dwarf.units();
179            while let Ok(Some(unit_header)) = units.next() {
180                if offset.to_unit_offset(&unit_header).is_some() {
181                    // Yes, we've found the unit. We return it so that the caller can recall us with the correct unit.
182                    // We can't use the correct unit ourselves, because the EntriesTree borrows the unit
183                    return Err(GetEntryTreeError::WrongUnit(unit_header));
184                }
185            }
186
187            Err(GetEntryTreeError::TraceError(
188                TraceError::DebugInfoOffsetUnitNotFound {
189                    debug_info_offset: offset.0,
190                },
191            ))
192        }
193        value => Err(GetEntryTreeError::TraceError(
194            TraceError::WrongAttributeValueType {
195                attribute_name: type_attr.name().to_string(),
196                expected_type_name: "UnitRef or DebugInfoRef",
197                gotten_value: format!("{value:X?}"),
198            },
199        )),
200    }
201}
202
203pub(crate) enum GetEntryTreeError {
204    WrongUnit(UnitHeader<DefaultReader>),
205    TraceError(TraceError),
206}
207
208impl GetEntryTreeError {
209    fn into_trace_error(self) -> TraceError {
210        match self {
211            Self::TraceError(e) => e,
212            Self::WrongUnit(_) => TraceError::UnitNotFoundAgain,
213        }
214    }
215}
216
217#[macro_export]
218macro_rules! get_entry_abstract_origin_reference_tree_recursive {
219    ($tree_name:ident = ($dwarf:expr, $unit:expr, $abbreviations:expr, $entry:expr)) => {
220        let mut __unit_header = $unit.header.clone();
221        #[allow(unused_mut)]
222        let mut $tree_name = match $crate::variables::get_entry_abstract_origin_reference_tree(
223            $dwarf,
224            &__unit_header,
225            $abbreviations,
226            $entry,
227        ) {
228            Err($crate::variables::GetEntryTreeError::WrongUnit(target_unit)) => {
229                __unit_header = target_unit;
230                $crate::variables::get_entry_abstract_origin_reference_tree(
231                    $dwarf,
232                    &__unit_header,
233                    $abbreviations,
234                    $entry,
235                )
236            }
237            value => value,
238        }
239        .map_err(|e| e.into_trace_error());
240    };
241}
242
243#[macro_export]
244macro_rules! get_entry_type_reference_tree_recursive {
245    ($tree_name:ident = ($dwarf:expr, $unit:expr, $abbreviations:expr, $entry:expr)) => {
246        let mut __unit_header = $unit.header.clone();
247        #[allow(unused_mut)]
248        let mut $tree_name = match $crate::variables::get_entry_type_reference_tree(
249            $dwarf,
250            &__unit_header,
251            $abbreviations,
252            $entry,
253        ) {
254            Err($crate::variables::GetEntryTreeError::WrongUnit(target_unit)) => {
255                __unit_header = target_unit;
256                $crate::variables::get_entry_type_reference_tree(
257                    $dwarf,
258                    &__unit_header,
259                    $abbreviations,
260                    $entry,
261                )
262            }
263            value => value,
264        }
265        .map_err(|e| e.into_trace_error());
266    };
267}
268
269fn try_read_frame_base<W: funty::Integral>(
270    dwarf: &Dwarf<DefaultReader>,
271    unit: &Unit<DefaultReader, usize>,
272    device_memory: &DeviceMemory<W>,
273    entry: &DebuggingInformationEntry<DefaultReader, usize>,
274) -> Result<Option<W>, TraceError>
275where
276    <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
277{
278    let frame_base_location = evaluate_location(
279        dwarf,
280        unit,
281        device_memory,
282        entry.attr(gimli::constants::DW_AT_frame_base)?,
283        None,
284    )?;
285    let frame_base_data = get_variable_data(
286        device_memory,
287        core::mem::size_of::<W>() as u64 * 8,
288        &frame_base_location,
289    );
290
291    Ok(frame_base_data.ok().map(|data| data.load_le()))
292}
293
294/// Finds the [Location] of the given entry.
295///
296/// This is done based on the `DW_AT_decl_file`, `DW_AT_decl_line` and `DW_AT_decl_column` attributes.
297/// These are normally present on variables and functions.
298fn find_entry_location(
299    dwarf: &Dwarf<DefaultReader>,
300    unit: &Unit<DefaultReader, usize>,
301    entry: &DebuggingInformationEntry<DefaultReader, usize>,
302) -> Result<Location, TraceError> {
303    // Get the attributes
304    let variable_decl_file = entry
305        .attr_value(gimli::constants::DW_AT_decl_file)?
306        .and_then(|f| match f {
307            AttributeValue::FileIndex(index) => Some(index),
308            _ => None,
309        });
310    let variable_decl_line = entry
311        .attr_value(gimli::constants::DW_AT_decl_line)?
312        .and_then(|l| l.udata_value());
313    let variable_decl_column = entry
314        .attr_value(gimli::constants::DW_AT_decl_column)?
315        .and_then(|c| c.udata_value());
316
317    fn path_push(path: &mut String, p: &str) {
318        /// Check if the path in the given string has a unix style root
319        fn has_unix_root(p: &str) -> bool {
320            p.starts_with('/')
321        }
322
323        /// Check if the path in the given string has a windows style root
324        fn has_windows_root(p: &str) -> bool {
325            p.starts_with('\\') || p.get(1..3) == Some(":\\")
326        }
327
328        if has_unix_root(p) || has_windows_root(p) {
329            *path = p.to_string();
330        } else {
331            let dir_separator = if has_windows_root(path.as_str()) {
332                '\\'
333            } else {
334                '/'
335            };
336
337            if !path.ends_with(dir_separator) {
338                path.push(dir_separator);
339            }
340            *path += p;
341        }
342    }
343
344    // The file is given as a number, so we need to search for the real file path
345    let variable_file = if let (Some(variable_decl_file), Some(line_program)) =
346        (variable_decl_file, unit.line_program.as_ref())
347    {
348        // The file paths are stored in the line_program
349        if let Some(file_entry) = line_program.header().file(variable_decl_file) {
350            let mut path = if let Some(comp_dir) = &unit.comp_dir {
351                comp_dir.to_string_lossy()?.into_owned()
352            } else {
353                String::new()
354            };
355
356            // The directory index 0 is defined to correspond to the compilation unit directory
357            if variable_decl_file != 0 {
358                if let Some(directory) = file_entry.directory(line_program.header()) {
359                    path_push(
360                        &mut path,
361                        &dwarf.attr_string(unit, directory)?.to_string_lossy()?,
362                    )
363                }
364            }
365
366            path_push(
367                &mut path,
368                &dwarf
369                    .attr_string(unit, file_entry.path_name())?
370                    .to_string()?,
371            );
372
373            Some(path)
374        } else {
375            None
376        }
377    } else {
378        None
379    };
380
381    Ok(Location {
382        file: variable_file,
383        line: variable_decl_line,
384        column: variable_decl_column,
385    })
386}
387
388/// Reads the DW_AT_data_member_location and returns the entry's bit offset
389fn read_data_member_location(
390    unit_header: &UnitHeader<DefaultReader, usize>,
391    entry: &DebuggingInformationEntry<DefaultReader, usize>,
392) -> Result<u64, TraceError> {
393    // TODO: Sometimes this is not a simple number, but a location expression.
394    // As of writing this has not come up, but I can imagine this is the case for C bitfields.
395    // It is the offset in bits from the base.
396    Ok(entry
397        .required_attr(unit_header, gimli::constants::DW_AT_data_member_location)?
398        .required_udata_value()?
399        * 8)
400}
401
402/// Decodes the type of an entry into a type value tree, however, the value is not yet filled in.
403///
404/// The given node should come from the [get_entry_type_reference_tree]
405/// and [get_entry_abstract_origin_reference_tree] functions.
406fn build_type_value_tree<W: funty::Integral>(
407    dwarf: &Dwarf<DefaultReader>,
408    unit: &Unit<DefaultReader, usize>,
409    abbreviations: &Abbreviations,
410    node: gimli::EntriesTreeNode<DefaultReader>,
411    type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
412) -> Result<TypeValueTree<W>, TraceError> {
413    // Get the root entry and its tag
414    let entry = node.entry();
415    let entry_die_offset = entry.offset().to_debug_info_offset(&unit.header).unwrap();
416
417    if let Some(existing_type) = type_cache.get(&entry_die_offset) {
418        log::trace!(
419            "Using cached type value tree for {:?} at {:X} (tag: {})",
420            get_entry_name(dwarf, unit, entry),
421            entry_die_offset.0,
422            entry.tag()
423        );
424
425        return (*existing_type).clone();
426    }
427
428    log::trace!(
429        "Building type value tree for {:?} at {:X} (tag: {})",
430        get_entry_name(dwarf, unit, entry),
431        entry_die_offset.0,
432        entry.tag()
433    );
434
435    // The tag tells us what the base type it
436    let result = match entry.tag() {
437        gimli::constants::DW_TAG_variant_part => type_value_tree_building::build_tagged_union(
438            dwarf,
439            unit,
440            abbreviations,
441            node,
442            type_cache,
443        ),
444        tag @ gimli::constants::DW_TAG_structure_type
445        | tag @ gimli::constants::DW_TAG_union_type
446        | tag @ gimli::constants::DW_TAG_class_type => type_value_tree_building::build_object(
447            dwarf,
448            unit,
449            abbreviations,
450            node,
451            type_cache,
452            tag,
453        ),
454        gimli::constants::DW_TAG_base_type => {
455            type_value_tree_building::build_base_type(dwarf, unit, node)
456        }
457        gimli::constants::DW_TAG_pointer_type => {
458            type_value_tree_building::build_pointer(dwarf, unit, abbreviations, node, type_cache)
459        }
460        gimli::constants::DW_TAG_array_type => {
461            type_value_tree_building::build_array(dwarf, unit, abbreviations, node, type_cache)
462        }
463        gimli::constants::DW_TAG_typedef => {
464            type_value_tree_building::build_typedef(dwarf, unit, abbreviations, node, type_cache)
465        }
466        gimli::constants::DW_TAG_enumeration_type => type_value_tree_building::build_enumeration(
467            dwarf,
468            unit,
469            abbreviations,
470            node,
471            type_cache,
472        ),
473        gimli::constants::DW_TAG_subroutine_type => {
474            let mut type_value_tree = TypeValueTree::new(TypeValue::default());
475            let mut type_value = type_value_tree.root_mut();
476
477            type_value.data_mut().variable_type.archetype = Archetype::Subroutine;
478            Ok(type_value_tree)
479        } // Ignore
480        gimli::constants::DW_TAG_volatile_type => type_value_tree_building::build_volatile_type(
481            dwarf,
482            unit,
483            abbreviations,
484            node,
485            type_cache,
486        ),
487        gimli::constants::DW_TAG_const_type => {
488            type_value_tree_building::build_const_type(dwarf, unit, abbreviations, node, type_cache)
489        }
490        tag => Err(TraceError::TagNotImplemented {
491            tag_name: tag.to_string(),
492            entry_debug_info_offset: entry.offset().to_debug_info_offset(&unit.header).unwrap().0,
493        }),
494    };
495
496    type_cache
497        .entry(entry_die_offset)
498        .or_insert_with(|| result.clone());
499
500    result
501}
502
503/// Runs the location evaluation of gimli.
504///
505/// - `location`: The `DW_AT_location` attribute value of the entry of the variable we want to get the location of.
506///   This may be a None if the variable has no location attribute.
507fn evaluate_location<W: funty::Integral>(
508    dwarf: &Dwarf<DefaultReader>,
509    unit: &Unit<DefaultReader, usize>,
510    device_memory: &DeviceMemory<W>,
511    location: Option<Attribute<DefaultReader>>,
512    frame_base: Option<W>,
513) -> Result<VariableLocationResult, TraceError>
514where
515    <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
516{
517    // First, we need to have the actual value
518    let location = match location {
519        Some(location) => location.value(),
520        None => return Ok(VariableLocationResult::NoLocationAttribute),
521    };
522
523    // Then we need to get the location expression. This expression can then later be evaluated by gimli.
524    let location_expression = match location {
525        AttributeValue::Block(ref data) => gimli::Expression(data.clone()),
526        AttributeValue::Exprloc(ref data) => data.clone(),
527        AttributeValue::LocationListsRef(l) => {
528            let mut locations = dwarf.locations(unit, l)?;
529            let mut location = None;
530
531            while let Ok(Some(maybe_location)) = locations.next() {
532                let check_pc = device_memory.register(gimli::Arm::PC)?;
533
534                if check_pc.as_u64() >= maybe_location.range.begin
535                    && check_pc.as_u64() < maybe_location.range.end
536                {
537                    location = Some(maybe_location);
538                    break;
539                }
540            }
541
542            if let Some(location) = location {
543                location.data
544            } else {
545                return Ok(VariableLocationResult::LocationListNotFound);
546            }
547        }
548        _ => unreachable!(),
549    };
550
551    // Turn the expression into an evaluation
552    let result = evaluate_expression(
553        unit,
554        device_memory,
555        frame_base,
556        location_expression.evaluation(unit.encoding()),
557    );
558
559    match result {
560        Err(TraceError::LocationEvaluationStepNotImplemented(step)) => {
561            Ok(VariableLocationResult::LocationEvaluationStepNotImplemented(step))
562        }
563        Err(TraceError::MissingMemory(_)) => Ok(VariableLocationResult::NoLocationFound),
564        Err(e) => Err(e),
565        Ok(pieces) if pieces.is_empty() => Ok(VariableLocationResult::NoLocationFound),
566        Ok(pieces) => Ok(VariableLocationResult::LocationsFound(pieces)),
567    }
568}
569
570fn evaluate_expression<W: funty::Integral>(
571    unit: &Unit<DefaultReader, usize>,
572    device_memory: &DeviceMemory<W>,
573    frame_base: Option<W>,
574    mut evaluation: Evaluation<DefaultReader>,
575) -> Result<Vec<Piece<DefaultReader, usize>>, TraceError>
576where
577    <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
578{
579    // Now we need to evaluate everything.
580    // DWARF has a stack based instruction set that needs to be executed.
581    // Luckily, gimli already implements the bulk of it.
582    // The evaluation stops when it requires some memory that we need to provide.
583    let mut result = evaluation.evaluate()?;
584    while result != EvaluationResult::Complete {
585        log::trace!("Location evaluation result: {result:?}");
586        match result {
587            EvaluationResult::RequiresRegister {
588                register,
589                base_type,
590            } => {
591                let value = device_memory.register(register)?;
592                let value = match base_type.0 {
593                    0 => gimli::Value::Generic(value.as_u64()),
594                    val => return Err(TraceError::OperationNotImplemented { operation: format!("Other types than generic haven't been implemented yet. base_type value: {val}"), file: file!(), line: line!() } ),
595                };
596                result = evaluation.resume_with_register(value)?;
597            }
598            EvaluationResult::RequiresFrameBase if frame_base.is_some() => {
599                result = evaluation.resume_with_frame_base(
600                    frame_base.ok_or(TraceError::UnknownFrameBase)?.as_u64(),
601                )?;
602            }
603            EvaluationResult::RequiresRelocatedAddress(address) => {
604                // We have no relocations of code
605                result = evaluation.resume_with_relocated_address(address)?;
606            }
607            EvaluationResult::RequiresEntryValue(ex) => {
608                let entry_pieces = evaluate_expression(
609                    unit,
610                    device_memory,
611                    frame_base,
612                    ex.evaluation(unit.encoding()),
613                )?;
614
615                let entry_data = get_variable_data(
616                    device_memory,
617                    W::BITS as u64,
618                    &VariableLocationResult::LocationsFound(entry_pieces),
619                )?;
620
621                result = evaluation.resume_with_entry_value(gimli::Value::Generic(
622                    entry_data.load_le::<W>().as_u64(), // TODO: What should be the endianness of this? Our device or the target device?
623                ))?;
624            }
625            EvaluationResult::RequiresMemory {
626                address,
627                size,
628                space: None,
629                base_type: UnitOffset(0),
630            } => {
631                // This arm only accepts the generic base_type, so size should always be equal to the size of W
632                // assert_eq!(size as u32 * 8, W::BITS);
633
634                let mut data = device_memory
635                    .read_slice(address..address + size as u64)?
636                    .ok_or(TraceError::MissingMemory(address))?;
637
638                data.extend(
639                    std::iter::once(0)
640                        .cycle()
641                        .take((W::BITS / 8) as usize - size as usize),
642                );
643
644                let value = gimli::Value::Generic(data.as_bits::<Lsb0>().load_le::<W>().as_u64());
645                result = evaluation.resume_with_memory(value)?;
646            }
647            r => {
648                return Err(TraceError::LocationEvaluationStepNotImplemented(
649                    std::rc::Rc::new(r),
650                ))
651            }
652        }
653    }
654
655    Ok(evaluation.result())
656}
657
658/// Reads the data of a piece of memory
659///
660/// The [Piece] is an indirect result of the [evaluate_location] function.
661///
662/// - `device_memory`: The captured memory of the device
663/// - `piece`: The piece of memory location that tells us which data needs to be read
664/// - `variable_size`: The size of the variable in bytes
665fn get_piece_data<W: funty::Integral>(
666    device_memory: &DeviceMemory<W>,
667    piece: &Piece<DefaultReader, usize>,
668    variable_size: u64,
669) -> Result<Option<bitvec::vec::BitVec<u8, Lsb0>>, VariableDataError>
670where
671    <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
672{
673    let mut data = match piece.location.clone() {
674        gimli::Location::Empty => return Err(VariableDataError::OptimizedAway),
675        gimli::Location::Register { register } => Some(
676            device_memory
677                .register(register)
678                .map(|r| r.to_ne_bytes().view_bits().to_bitvec()) // TODO: Is this correct? Shouldn't this be the endianness of the target device?
679                .map_err(|e| VariableDataError::NoDataAvailableAt(e.to_string()))?,
680        ),
681        gimli::Location::Address { address } => device_memory
682            .read_slice(address..(address + variable_size))?
683            .map(|b| b.view_bits().to_bitvec()),
684        gimli::Location::Value { value } => {
685            let mut data = BitVec::new();
686
687            match value {
688                gimli::Value::Generic(v) => data.extend(v.view_bits::<Lsb0>()),
689                gimli::Value::I8(v) => data.extend((v as u8).view_bits::<Lsb0>()),
690                gimli::Value::U8(v) => data.extend(v.view_bits::<Lsb0>()),
691                gimli::Value::I16(v) => data.extend((v as u16).view_bits::<Lsb0>()),
692                gimli::Value::U16(v) => data.extend(v.view_bits::<Lsb0>()),
693                gimli::Value::I32(v) => data.extend((v as u32).view_bits::<Lsb0>()),
694                gimli::Value::U32(v) => data.extend(v.view_bits::<Lsb0>()),
695                gimli::Value::I64(v) => data.extend((v as u64).view_bits::<Lsb0>()),
696                gimli::Value::U64(v) => data.extend(v.view_bits::<Lsb0>()),
697                gimli::Value::F32(v) => data.extend(v.to_bits().view_bits::<Lsb0>()),
698                gimli::Value::F64(v) => data.extend(v.to_bits().view_bits::<Lsb0>()),
699            }
700
701            Some(data)
702        }
703        gimli::Location::Bytes { value } => value
704            .get(0..variable_size as usize)
705            .map(|b| b.view_bits().to_bitvec()),
706        gimli::Location::ImplicitPointer {
707            value: _,
708            byte_offset: _,
709        } => {
710            return Err(VariableDataError::OperationNotImplemented {
711                operation: "`ImplicitPointer` location not yet supported".into(),
712                file: file!(),
713                line: line!(),
714            })
715        }
716    };
717
718    // The piece can also specify offsets and a size, so adapt what we've just read to that
719    if let Some(data) = data.as_mut() {
720        if let Some(offset) = piece.bit_offset {
721            data.drain(0..offset as usize);
722        }
723        if let Some(length) = piece.size_in_bits {
724            data.truncate(length as usize);
725        }
726    }
727
728    Ok(data)
729}
730
731/// Get all of the available variable data based on the [VariableLocationResult] of the [evaluate_location] function.
732///
733/// - `device_memory`: All the captured memory of the device
734/// - `variable_size`: The size of the variable in bits
735/// - `variable_location`: The location of the variable
736fn get_variable_data<W: funty::Integral>(
737    device_memory: &DeviceMemory<W>,
738    variable_size: u64,
739    variable_location: &VariableLocationResult,
740) -> Result<BitVec<u8, Lsb0>, VariableDataError>
741where
742    <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
743{
744    match variable_location {
745        VariableLocationResult::NoLocationAttribute => Err(VariableDataError::OptimizedAway),
746        VariableLocationResult::LocationListNotFound => Err(VariableDataError::OptimizedAway),
747        VariableLocationResult::NoLocationFound => Err(VariableDataError::OptimizedAway),
748        VariableLocationResult::LocationsFound(pieces) => {
749            let mut data = BitVec::new();
750
751            // Ceil-div with 8 to get the bytes we need to read
752            let variable_size_bytes = div_ceil(variable_size, 8);
753
754            // Get all the data of the pieces
755            for piece in pieces {
756                let piece_data = get_piece_data(device_memory, piece, variable_size_bytes)?;
757
758                if let Some(mut piece_data) = piece_data {
759                    // TODO: Is this always in sequential order? We now assume that it is
760                    data.append(&mut piece_data);
761                } else {
762                    // Data is not on the stack
763                    return Err(VariableDataError::NoDataAvailableAt(format!(
764                        "{:X?}",
765                        piece.location
766                    )));
767                };
768            }
769
770            Ok(data)
771        }
772        VariableLocationResult::LocationEvaluationStepNotImplemented(step) => Err(
773            VariableDataError::UnimplementedLocationEvaluationStep(format!("{step:?}")),
774        ),
775    }
776}
777
778/// Returns the first found address in the location (if any)
779fn get_variable_address(variable_location: &VariableLocationResult) -> Option<u64> {
780    match variable_location {
781        VariableLocationResult::LocationsFound(pieces) => {
782            for piece in pieces {
783                if let gimli::Location::Address { address } = piece.location {
784                    return Some(address);
785                }
786            }
787            None
788        }
789        _ => None,
790    }
791}
792
793fn read_base_type<W: funty::Integral>(
794    encoding: gimli::DwAte,
795    data: &BitSlice<u8, Lsb0>,
796) -> Result<Value<W>, VariableDataError> {
797    match encoding {
798        gimli::constants::DW_ATE_unsigned | gimli::constants::DW_ATE_unsigned_char => {
799            match data.len() {
800                8 => Ok(Value::Uint(data.load_le::<u8>() as _)),
801                16 => Ok(Value::Uint(data.load_le::<u16>() as _)),
802                32 => Ok(Value::Uint(data.load_le::<u32>() as _)),
803                64 => Ok(Value::Uint(data.load_le::<u64>() as _)),
804                128 => Ok(Value::Uint(data.load_le::<u128>() as _)),
805                _ => Err(VariableDataError::InvalidSize { bits: data.len() }),
806            }
807        }
808        gimli::constants::DW_ATE_signed | gimli::constants::DW_ATE_signed_char => {
809            match data.len() {
810                8 => Ok(Value::Int(data.load_le::<u8>() as _)),
811                16 => Ok(Value::Int(data.load_le::<u16>() as _)),
812                32 => Ok(Value::Int(data.load_le::<u32>() as _)),
813                64 => Ok(Value::Int(data.load_le::<u64>() as _)),
814                128 => Ok(Value::Int(data.load_le::<u128>() as _)),
815                _ => Err(VariableDataError::InvalidSize { bits: data.len() }),
816            }
817        }
818        gimli::constants::DW_ATE_float => match data.len() {
819            32 => Ok(Value::Float(f32::from_bits(data.load_le::<u32>()) as _)),
820            64 => Ok(Value::Float(f64::from_bits(data.load_le::<u64>()) as _)),
821            _ => Err(VariableDataError::InvalidSize { bits: data.len() }),
822        },
823        gimli::constants::DW_ATE_boolean => Ok(Value::Bool(data.iter().any(|v| *v))),
824        gimli::constants::DW_ATE_address => match data.len() {
825            8 => Ok(Value::Address(
826                data.load_le::<u8>().try_into().ok().unwrap(),
827            )),
828            16 => Ok(Value::Address(
829                data.load_le::<u16>().try_into().ok().unwrap(),
830            )),
831            32 => Ok(Value::Address(
832                data.load_le::<u32>().try_into().ok().unwrap(),
833            )),
834            64 => Ok(Value::Address(
835                data.load_le::<u64>().try_into().ok().unwrap(),
836            )),
837            _ => Err(VariableDataError::InvalidSize { bits: data.len() }),
838        },
839        t => Err(VariableDataError::UnsupportedBaseType {
840            base_type: t,
841            data: data.to_bitvec(),
842        }),
843    }
844}
845
846/// Read some bit data into the value of the give variable. If there is an error, that error will be placed in the value field as well
847fn read_variable_data<W: funty::Integral>(
848    mut variable: Pin<&mut TypeValueNode<W>>,
849    data: &BitSlice<u8, Lsb0>,
850    device_memory: &DeviceMemory<W>,
851    type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
852) {
853    // We may not have enough data in some cases
854    // I don't know why that is, so let's just print a warning
855    if variable.data().bit_length() > data.len() as u64 {
856        log::warn!(
857            "Variable of type {} claims to take up {} bits, but only {} bits are available",
858            variable.data().variable_type.name,
859            variable.data().bit_range.end,
860            data.len()
861        );
862    }
863
864    match variable.data().variable_type.archetype {
865        Archetype::TaggedUnion => {
866            // The first child must be the descriminator and not one of the variants
867            assert!(variable.front_mut().unwrap().data().name == "discriminant");
868
869            // We have to read the discriminator, then select the active variant and then read that
870            read_variable_data(
871                variable.front_mut().unwrap(),
872                data,
873                device_memory,
874                type_cache,
875            );
876
877            let discriminator_value = match &variable.front().unwrap().data().variable_value {
878                Ok(value) => value.clone(),
879                _ => {
880                    return;
881                }
882            };
883
884            // We know the discriminator value, so now we need to hunt for the active variant.
885            // There may not be one though
886            let active_variant = variable
887                .iter_mut()
888                .skip(1)
889                .find(|variant| variant.data().variable_value.as_ref() == Ok(&discriminator_value));
890
891            if let Some(active_variant) = active_variant {
892                read_variable_data(active_variant, data, device_memory, type_cache);
893            } else if let Some(default_variant) = variable
894                .iter_mut()
895                .skip(1)
896                .find(|variant| variant.data().variable_value.is_err())
897            {
898                // There is no active variant, so we need to go for the default
899                read_variable_data(default_variant, data, device_memory, type_cache);
900            }
901        }
902        Archetype::TaggedUnionVariant => {
903            read_variable_data(
904                variable.front_mut().unwrap(),
905                data,
906                device_memory,
907                type_cache,
908            );
909        }
910        Archetype::Structure
911        | Archetype::Union
912        | Archetype::Class
913        | Archetype::ObjectMemberPointer => {
914            // Every member of this object is a child in the tree.
915            // We simply need to read every child.
916
917            for mut child in variable.iter_mut() {
918                let child_data = match data.get(child.data().bit_range_usize()) {
919                    Some(child_data) => child_data,
920                    None => BitSlice::empty(),
921                };
922
923                let range = child.data().bit_range.clone();
924                child.data_mut().bit_range = 0..range.end - range.start;
925                read_variable_data(child, child_data, device_memory, type_cache);
926            }
927
928            if &variable.data().variable_type.name == "&str" {
929                // This is a string
930                let pointer = &variable
931                    .iter()
932                    .find(|field| field.data().name == "data_ptr")
933                    .ok_or(())
934                    .map(|node| &node.data().variable_value);
935                let length = &variable
936                    .iter()
937                    .find(|field| field.data().name == "length")
938                    .ok_or(())
939                    .map(|node| &node.data().variable_value);
940
941                match (pointer, length) {
942                    (Ok(Ok(Value::Address(pointer))), Ok(Ok(Value::Uint(length))))
943                        if *length < 64 * 1024 =>
944                    {
945                        // We can read the data. This works because the length field denotes the byte size, not the char size
946                        let data = device_memory
947                            .read_slice(pointer.as_u64()..pointer.as_u64() + *length as u64);
948                        if let Ok(Some(data)) = data {
949                            variable.data_mut().variable_value =
950                                Ok(Value::String(data, StringFormat::Utf8));
951                        } else {
952                            // There's something wrong. Fall back to treating the string as an object
953                            variable.data_mut().variable_value = Ok(Value::Object);
954                        }
955                    }
956                    (Ok(Ok(Value::Address(_))), Ok(Ok(Value::Uint(length))))
957                        if *length >= 64 * 1024 =>
958                    {
959                        log::warn!(
960                            "We started decoding the string {}, but it is {length} bytes long",
961                            variable.data().name
962                        );
963                        // There's something wrong. Fall back to treating the string as an object
964                        variable.data_mut().variable_value = Ok(Value::Object);
965                    }
966                    _ => {
967                        log::error!(
968                            "We started decoding the string {}, but found an error",
969                            variable.data().name
970                        );
971                        // There's something wrong. Fall back to treating the string as an object
972                        variable.data_mut().variable_value = Ok(Value::Object);
973                    }
974                }
975            } else {
976                // This is a normal object
977                variable.data_mut().variable_value = Ok(Value::Object);
978            }
979        }
980        Archetype::BaseType(encoding) => {
981            if variable.data().bit_length() == 0 && variable.data().variable_type.name == "()" {
982                variable.data_mut().variable_value = Ok(Value::Unit);
983            } else {
984                variable.data_mut().variable_value =
985                    match data.get(variable.data().bit_range_usize()) {
986                        Some(data) => read_base_type(encoding, data),
987                        None => Err(VariableDataError::NoDataAvailable),
988                    };
989            }
990        }
991        Archetype::Pointer(die_offset) => {
992            // The variable is a number that is the address of the pointee.
993            // The pointee is not part of this tree yet and has to be looked up through the type_cache.
994            // This is done so that we cannot get an infinite recursive type due to e.g. linked lists.
995
996            variable.data_mut().variable_value = match data.get(variable.data().bit_range_usize()) {
997                Some(data) => read_base_type(gimli::constants::DW_ATE_address, data),
998                None => Err(VariableDataError::NoDataAvailable),
999            };
1000
1001            let address = match variable.data().variable_value {
1002                Ok(Value::Address(addr)) => Ok(addr),
1003                _ => Err(VariableDataError::InvalidPointerData),
1004            };
1005
1006            let pointee_tree_clone = match type_cache
1007                .get(&die_offset)
1008                .expect("Pointers must have their pointee type cached")
1009                .clone()
1010            {
1011                Ok(pointee_tree_clone) => pointee_tree_clone,
1012                Err(_) => TypeValueTree::new(TypeValue {
1013                    name: "Pointee".into(),
1014                    variable_type: VariableType {
1015                        archetype: Archetype::Unknown,
1016                        ..Default::default()
1017                    },
1018                    bit_range: 0..0,
1019                    variable_value: Err(VariableDataError::Unknown),
1020                }),
1021            };
1022            variable.push_back(pointee_tree_clone);
1023            let mut pointee = variable.back_mut().unwrap();
1024
1025            match address {
1026                Ok(address) if address == W::ZERO => {
1027                    pointee.data_mut().variable_value = Err(VariableDataError::NullPointer)
1028                }
1029                Ok(address) => {
1030                    let pointee_data = device_memory.read_slice(
1031                        address.as_u64()
1032                            ..address.as_u64() + div_ceil(pointee.data().bit_range.end, 8),
1033                    );
1034
1035                    match pointee_data {
1036                        Ok(Some(pointee_data)) => {
1037                            read_variable_data(
1038                                pointee,
1039                                pointee_data.view_bits(),
1040                                device_memory,
1041                                type_cache,
1042                            );
1043                        }
1044                        Ok(None) => {
1045                            pointee.data_mut().variable_value =
1046                                Err(VariableDataError::NoDataAvailable);
1047                        }
1048                        Err(e) => {
1049                            pointee.data_mut().variable_value = Err(e.into());
1050                        }
1051                    }
1052                }
1053                Err(e) => pointee.data_mut().variable_value = Err(e),
1054            }
1055        }
1056        Archetype::Array => {
1057            variable.data_mut().variable_value = Ok(Value::Array);
1058            // The tree has all children that we have to read. These are the elements of the array
1059            for mut element in variable.iter_mut() {
1060                match data.get(element.data().bit_range_usize()) {
1061                    Some(data) => {
1062                        let element_bitrange = element.data().bit_range.clone();
1063                        element.data_mut().bit_range =
1064                            0..element_bitrange.end - element_bitrange.start;
1065                        read_variable_data(element, data, device_memory, type_cache)
1066                    }
1067                    None => {
1068                        element.data_mut().variable_value = Err(VariableDataError::NoDataAvailable)
1069                    }
1070                }
1071            }
1072        }
1073        Archetype::Enumeration => {
1074            variable.data_mut().variable_value = Ok(Value::Enumeration);
1075
1076            // The first child of the enumeration is the base integer. We only have to read that one.
1077            read_variable_data(
1078                variable.front_mut().expect("Enumerations have a child"),
1079                data,
1080                device_memory,
1081                type_cache,
1082            );
1083        }
1084        Archetype::Typedef => {
1085            variable.data_mut().variable_value = Ok(Value::Typedef);
1086
1087            // The first child of the enumeration is the base integer. We only have to read that one.
1088            read_variable_data(
1089                variable.front_mut().expect("Typedefs have a child"),
1090                data,
1091                device_memory,
1092                type_cache,
1093            );
1094        }
1095        Archetype::Enumerator => {
1096            // Ignore, we don't have to do anything
1097        }
1098        Archetype::Subroutine => {
1099            variable.data_mut().variable_value = Ok(Value::Object);
1100            // Ignore, there's nothing to do
1101        }
1102        Archetype::Unknown => {
1103            // Ignore, we don't know what to do
1104        }
1105    }
1106}
1107
1108fn read_variable_entry<W: funty::Integral>(
1109    dwarf: &Dwarf<DefaultReader>,
1110    unit: &Unit<DefaultReader, usize>,
1111    abbreviations: &Abbreviations,
1112    device_memory: &DeviceMemory<W>,
1113    frame_base: Option<W>,
1114    entry: &DebuggingInformationEntry<DefaultReader, usize>,
1115    type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1116) -> Result<Option<Variable<W>>, TraceError>
1117where
1118    <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1119{
1120    get_entry_abstract_origin_reference_tree_recursive!(
1121        abstract_origin_tree = (dwarf, unit, abbreviations, entry)
1122    );
1123    let mut abstract_origin_tree = abstract_origin_tree.ok();
1124
1125    let abstract_origin_node = abstract_origin_tree
1126        .as_mut()
1127        .and_then(|tree| tree.root().ok());
1128    let abstract_origin_entry = abstract_origin_node.as_ref().map(|node| node.entry());
1129
1130    // Get the name of the variable
1131    let variable_name = get_entry_name(dwarf, unit, entry);
1132
1133    // Alternatively, get the name from the abstract origin
1134    let mut variable_name = match (variable_name, abstract_origin_entry) {
1135        (Err(_), Some(entry)) => get_entry_name(dwarf, unit, entry),
1136        (variable_name, _) => variable_name,
1137    };
1138
1139    if entry.tag() == gimli::constants::DW_TAG_formal_parameter && variable_name.is_err() {
1140        log::trace!("Formal parameter does not have a name, renaming it to 'param'");
1141        variable_name = Ok("param".into());
1142    }
1143
1144    // Get the name of the variable
1145    let variable_linkage_name = get_entry_linkage_name(dwarf, unit, entry)?;
1146
1147    // Get the type of the variable or its abstract origin
1148    get_entry_type_reference_tree_recursive!(
1149        variable_type_tree = (dwarf, unit, abbreviations, entry)
1150    );
1151
1152    get_entry_abstract_origin_reference_tree_recursive!(
1153        abstract_origin_tree = (dwarf, unit, abbreviations, entry)
1154    );
1155
1156    let variable_type_value_tree = (|| match (variable_type_tree, abstract_origin_tree) {
1157        (Ok(mut variable_type_tree), _) => {
1158            let type_root = variable_type_tree.root()?;
1159            build_type_value_tree(dwarf, unit, abbreviations, type_root, type_cache)
1160        }
1161        (_, Ok(mut abstract_origin_tree)) => {
1162            let abstract_entry = abstract_origin_tree.root()?.entry().clone();
1163            get_entry_type_reference_tree_recursive!(
1164                abstract_variable_type_tree = (dwarf, unit, abbreviations, &abstract_entry)
1165            );
1166
1167            match abstract_variable_type_tree {
1168                Ok(mut abstract_variable_type_tree) => {
1169                    let type_root = abstract_variable_type_tree.root()?;
1170                    build_type_value_tree(dwarf, unit, abbreviations, type_root, type_cache)
1171                }
1172                Err(e) => Err(e),
1173            }
1174        }
1175        (Err(e), _) => Err(e),
1176    })();
1177
1178    let variable_kind = VariableKind {
1179        zero_sized: variable_type_value_tree
1180            .as_ref()
1181            .map(|vt| vt.data().bit_length() == 0)
1182            .unwrap_or_default(),
1183        inlined: abstract_origin_entry.is_some(),
1184        parameter: entry.tag() == gimli::constants::DW_TAG_formal_parameter,
1185    };
1186
1187    // Get the (file) location of the variable
1188    let mut variable_file_location = find_entry_location(dwarf, unit, entry)?;
1189    if let (None, Some(abstract_origin_entry)) =
1190        (&variable_file_location.file, abstract_origin_entry)
1191    {
1192        variable_file_location = find_entry_location(dwarf, unit, abstract_origin_entry)?;
1193    }
1194
1195    match (variable_name, variable_type_value_tree) {
1196        (Ok(variable_name), Ok(variable_type_value_tree)) if variable_kind.zero_sized => {
1197            Ok(Some(Variable {
1198                name: variable_name,
1199                kind: variable_kind,
1200                type_value: variable_type_value_tree,
1201                location: variable_file_location,
1202                linkage_name: variable_linkage_name,
1203                address: None, // We don't care about the address of a ZST
1204            }))
1205        }
1206        (Ok(variable_name), Ok(mut variable_type_value_tree)) => {
1207            let location_attr = entry.attr(gimli::constants::DW_AT_location)?;
1208
1209            let location_attr = match (location_attr, abstract_origin_entry) {
1210                (None, Some(entry)) => entry.attr(gimli::constants::DW_AT_location)?,
1211                (location_attr, _) => location_attr,
1212            };
1213
1214            // Get the location of the variable
1215            let variable_location =
1216                evaluate_location(dwarf, unit, device_memory, location_attr, frame_base)?;
1217
1218            log::debug!(
1219                "Reading variable data for `{variable_name}` at {variable_location:X?} of {} bits",
1220                variable_type_value_tree.data().bit_length()
1221            );
1222            let variable_data = get_variable_data(
1223                device_memory,
1224                variable_type_value_tree.data().bit_length(),
1225                &variable_location,
1226            );
1227
1228            match variable_data {
1229                // We have the data so read the variable using it
1230                Ok(variable_data) => read_variable_data(
1231                    variable_type_value_tree.root_mut(),
1232                    &variable_data,
1233                    device_memory,
1234                    type_cache,
1235                ),
1236                // We couldn't get the data, so set the value to the error we got
1237                Err(e) => {
1238                    variable_type_value_tree
1239                        .root_mut()
1240                        .data_mut()
1241                        .variable_value = Err(e)
1242                }
1243            }
1244
1245            Ok(Some(Variable {
1246                name: variable_name,
1247                kind: variable_kind,
1248                type_value: variable_type_value_tree,
1249                location: variable_file_location,
1250                linkage_name: variable_linkage_name,
1251                address: get_variable_address(&variable_location),
1252            }))
1253        }
1254        (Ok(variable_name), Err(type_error)) => {
1255            log::info!(
1256                "Could not read the type of variable `{}` of entry {:X?}: {}",
1257                variable_name,
1258                entry.offset().to_debug_info_offset(&unit.header),
1259                type_error
1260            );
1261            Ok(None)
1262        }
1263        (Err(name_error), _) => {
1264            log::debug!(
1265                "Could not get the name of a variable of entry {:X?}: {}",
1266                entry.offset().to_debug_info_offset(&unit.header),
1267                name_error
1268            );
1269            Ok(None)
1270        }
1271    }
1272}
1273
1274pub fn find_variables_in_function<W: funty::Integral>(
1275    dwarf: &Dwarf<DefaultReader>,
1276    unit: &Unit<DefaultReader, usize>,
1277    abbreviations: &Abbreviations,
1278    device_memory: &DeviceMemory<W>,
1279    node: gimli::EntriesTreeNode<DefaultReader>,
1280    type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1281) -> Result<Vec<Variable<W>>, TraceError>
1282where
1283    <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1284{
1285    #[allow(clippy::too_many_arguments)]
1286    fn recursor<W: funty::Integral>(
1287        dwarf: &Dwarf<DefaultReader>,
1288        unit: &Unit<DefaultReader, usize>,
1289        abbreviations: &Abbreviations,
1290        device_memory: &DeviceMemory<W>,
1291        node: gimli::EntriesTreeNode<DefaultReader>,
1292        variables: &mut Vec<Variable<W>>,
1293        mut frame_base: Option<W>,
1294        type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1295    ) -> Result<(), TraceError>
1296    where
1297        <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1298    {
1299        let entry = node.entry();
1300
1301        log::trace!(
1302            "Checking out the entry @ .debug_info: {:X}",
1303            unit.header.offset().as_debug_info_offset().unwrap().0 + entry.offset().0
1304        );
1305
1306        if let Some(new_frame_base) = try_read_frame_base(dwarf, unit, device_memory, entry)? {
1307            frame_base = Some(new_frame_base);
1308        }
1309
1310        if entry.tag() == gimli::constants::DW_TAG_variable
1311            || entry.tag() == gimli::constants::DW_TAG_formal_parameter
1312        {
1313            if let Some(variable) = read_variable_entry(
1314                dwarf,
1315                unit,
1316                abbreviations,
1317                device_memory,
1318                frame_base,
1319                entry,
1320                type_cache,
1321            )? {
1322                variables.push(variable);
1323            }
1324        }
1325
1326        let mut children = node.children();
1327        while let Some(child) = children.next()? {
1328            recursor(
1329                dwarf,
1330                unit,
1331                abbreviations,
1332                device_memory,
1333                child,
1334                variables,
1335                frame_base,
1336                type_cache,
1337            )?;
1338        }
1339
1340        Ok(())
1341    }
1342
1343    let mut variables = Vec::new();
1344    recursor(
1345        dwarf,
1346        unit,
1347        abbreviations,
1348        device_memory,
1349        node,
1350        &mut variables,
1351        None,
1352        type_cache,
1353    )?;
1354    Ok(variables)
1355}
1356
1357pub fn find_static_variables<W: funty::Integral>(
1358    dwarf: &Dwarf<DefaultReader>,
1359    device_memory: &DeviceMemory<W>,
1360    type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1361) -> Result<Vec<Variable<W>>, TraceError>
1362where
1363    <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1364{
1365    fn recursor<W: funty::Integral>(
1366        dwarf: &Dwarf<DefaultReader>,
1367        unit: &Unit<DefaultReader, usize>,
1368        abbreviations: &Abbreviations,
1369        device_memory: &DeviceMemory<W>,
1370        node: gimli::EntriesTreeNode<DefaultReader>,
1371        variables: &mut Vec<Variable<W>>,
1372        type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1373    ) -> Result<(), TraceError>
1374    where
1375        <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1376    {
1377        let entry = node.entry();
1378
1379        match entry.tag() {
1380            gimli::constants::DW_TAG_compile_unit => {}
1381            gimli::constants::DW_TAG_namespace => {}
1382            gimli::constants::DW_TAG_structure_type
1383            | gimli::constants::DW_TAG_subprogram
1384            | gimli::constants::DW_TAG_enumeration_type
1385            | gimli::constants::DW_TAG_base_type
1386            | gimli::constants::DW_TAG_array_type
1387            | gimli::constants::DW_TAG_pointer_type
1388            | gimli::constants::DW_TAG_subroutine_type
1389            | gimli::constants::DW_TAG_typedef
1390            | gimli::constants::DW_TAG_restrict_type
1391            | gimli::constants::DW_TAG_const_type
1392            | gimli::constants::DW_TAG_union_type
1393            | gimli::constants::DW_TAG_volatile_type => return Ok(()),
1394            gimli::constants::DW_TAG_variable => {
1395                if let Some(variable) = read_variable_entry(
1396                    dwarf,
1397                    unit,
1398                    abbreviations,
1399                    device_memory,
1400                    None,
1401                    entry,
1402                    type_cache,
1403                )? {
1404                    variables.push(variable);
1405                }
1406            }
1407            tag => {
1408                log::error!(
1409                    "Unexpected tag in the search of static variables: {} at {:X?}",
1410                    tag,
1411                    entry.offset().to_debug_info_offset(&unit.header)
1412                );
1413                return Ok(());
1414            }
1415        }
1416
1417        let mut children = node.children();
1418        while let Some(child) = children.next()? {
1419            recursor(
1420                dwarf,
1421                unit,
1422                abbreviations,
1423                device_memory,
1424                child,
1425                variables,
1426                type_cache,
1427            )?;
1428        }
1429
1430        Ok(())
1431    }
1432
1433    let mut variables = Vec::new();
1434    let mut units = dwarf.units();
1435    while let Some(unit_header) = units.next()? {
1436        let abbreviations = dwarf.abbreviations(&unit_header)?;
1437        recursor(
1438            dwarf,
1439            &dwarf.unit(unit_header.clone())?,
1440            &abbreviations,
1441            device_memory,
1442            unit_header.entries_tree(&abbreviations, None)?.root()?,
1443            &mut variables,
1444            type_cache,
1445        )?;
1446    }
1447
1448    Ok(variables)
1449}