1use super::{
2 DebugError, DebugRegisters, StackFrame, VariableCache,
3 exception_handling::ExceptionInterface,
4 function_die::{Die, FunctionDie},
5 get_object_reference,
6 unit_info::UnitInfo,
7 variable::*,
8};
9use crate::{SourceLocation, VerifiedBreakpoint, stack_frame::StackFrameInfo, unit_info::RangeExt};
10use gimli::{
11 BaseAddresses, DebugFrame, RunTimeEndian, UnwindContext, UnwindSection, UnwindTableRow,
12};
13use object::read::{Object, ObjectSection};
14use probe_rs::{Error, MemoryInterface, RegisterDataType, RegisterRole, RegisterValue, UnwindRule};
15use probe_rs_target::InstructionSet;
16use std::{
17 borrow, cmp::Ordering, num::NonZeroU64, ops::ControlFlow, path::Path, rc::Rc, str::from_utf8,
18};
19use typed_path::{TypedPath, TypedPathBuf};
20
21pub(crate) type GimliReader = gimli::EndianReader<RunTimeEndian, std::rc::Rc<[u8]>>;
22pub(crate) type GimliReaderOffset =
23 <gimli::EndianReader<RunTimeEndian, Rc<[u8]>> as gimli::Reader>::Offset;
24
25pub(crate) type GimliAttribute = gimli::Attribute<GimliReader>;
26
27pub(crate) type DwarfReader = gimli::read::EndianRcSlice<RunTimeEndian>;
28
29pub struct DebugInfo {
31 pub(crate) dwarf: gimli::Dwarf<DwarfReader>,
32 pub(crate) frame_section: gimli::DebugFrame<DwarfReader>,
33 pub(crate) locations_section: gimli::LocationLists<DwarfReader>,
34 pub(crate) address_section: gimli::DebugAddr<DwarfReader>,
35 pub(crate) debug_line_section: gimli::DebugLine<DwarfReader>,
36
37 pub(crate) unit_infos: Vec<UnitInfo>,
38 pub(crate) endianness: gimli::RunTimeEndian,
39
40 pub(crate) addr2line: Option<addr2line::Loader>,
41}
42
43impl DebugInfo {
44 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<DebugInfo, DebugError> {
46 let data = std::fs::read(path.as_ref())?;
47
48 let mut this = DebugInfo::from_raw(&data)?;
49 this.addr2line = addr2line::Loader::new(path).ok();
50 Ok(this)
51 }
52
53 pub fn from_raw(data: &[u8]) -> Result<Self, DebugError> {
55 let object = object::File::parse(data)?;
56
57 let endianness = if object.is_little_endian() {
58 RunTimeEndian::Little
59 } else {
60 RunTimeEndian::Big
61 };
62
63 let load_section = |id: gimli::SectionId| -> Result<DwarfReader, gimli::Error> {
65 let data = object
66 .section_by_name(id.name())
67 .and_then(|section| section.uncompressed_data().ok())
68 .unwrap_or_else(|| borrow::Cow::Borrowed(&[][..]));
69
70 Ok(gimli::read::EndianRcSlice::new(
71 Rc::from(&*data),
72 endianness,
73 ))
74 };
75
76 let dwarf_cow = gimli::Dwarf::load(&load_section)?;
78
79 use gimli::Section;
80 let mut frame_section = gimli::DebugFrame::load(load_section)?;
81 let address_section = gimli::DebugAddr::load(load_section)?;
82 let debug_loc = gimli::DebugLoc::load(load_section)?;
83 let debug_loc_lists = gimli::DebugLocLists::load(load_section)?;
84 let locations_section = gimli::LocationLists::new(debug_loc, debug_loc_lists);
85 let debug_line_section = gimli::DebugLine::load(load_section)?;
86
87 let mut unit_infos = Vec::new();
88
89 let mut iter = dwarf_cow.units();
90
91 while let Ok(Some(header)) = iter.next() {
92 if let Ok(unit) = dwarf_cow.unit(header) {
93 frame_section.set_address_size(unit.encoding().address_size);
100
101 unit_infos.push(UnitInfo::new(unit));
102 };
103 }
104
105 Ok(DebugInfo {
106 dwarf: dwarf_cow,
107 frame_section,
108 locations_section,
109 address_section,
110 debug_line_section,
111 unit_infos,
112 endianness,
113 addr2line: None,
114 })
115 }
116
117 pub fn get_source_location(&self, address: u64) -> Option<SourceLocation> {
119 for unit_info in &self.unit_infos {
120 let unit = &unit_info.unit;
121
122 let mut ranges = match self.dwarf.unit_ranges(unit) {
123 Ok(ranges) => ranges,
124 Err(error) => {
125 tracing::warn!(
126 "No valid source code ranges found for unit {:?}: {:?}",
127 unit.dwo_name(),
128 error
129 );
130 continue;
131 }
132 };
133
134 while let Ok(Some(range)) = ranges.next() {
135 if !(range.begin <= address && address < range.end) {
136 continue;
137 }
138 let ilnp = unit.line_program.as_ref()?.clone();
140
141 let (program, sequences) = match ilnp.sequences() {
142 Ok(value) => value,
143 Err(error) => {
144 tracing::warn!(
145 "No valid source code ranges found for address {}: {:?}",
146 address,
147 error
148 );
149 continue;
150 }
151 };
152
153 let mut target_seq = None;
155
156 for seq in sequences {
157 if seq.start <= address && address < seq.end {
158 target_seq = Some(seq);
159 break;
160 }
161 }
162
163 let Some(target_seq) = target_seq.as_ref() else {
164 continue;
165 };
166
167 let mut previous_row: Option<gimli::LineRow> = None;
168
169 let mut rows = program.resume_from(target_seq);
170
171 while let Ok(Some((_, row))) = rows.next_row() {
172 match row.address().cmp(&address) {
173 Ordering::Greater => {
174 if let Some(previous_row) = previous_row {
179 if let Some(path) =
180 self.find_file_and_directory(unit, previous_row.file_index())
181 {
182 tracing::debug!("{:#010x} - {:?}", address, previous_row.isa());
183 return Some(SourceLocation {
184 line: previous_row.line().map(NonZeroU64::get),
185 column: Some(previous_row.column().into()),
186 path,
187 address: Some(previous_row.address()),
188 });
189 }
190 }
191 }
192 Ordering::Less => {}
193 Ordering::Equal => {
194 if let Some(path) = self.find_file_and_directory(unit, row.file_index())
195 {
196 tracing::debug!("{:#010x} - {:?}", address, row.isa());
197
198 return Some(SourceLocation {
199 line: row.line().map(NonZeroU64::get),
200 column: Some(row.column().into()),
201 path,
202 address: Some(row.address()),
203 });
204 }
205 }
206 }
207 previous_row = Some(*row);
208 }
209 }
210 }
211 None
212 }
213
214 pub fn create_static_scope_cache(&self) -> VariableCache {
220 VariableCache::new_static_cache()
221 }
222
223 pub(crate) fn create_function_scope_cache(
225 &self,
226 die_cursor_state: &FunctionDie,
227 unit_info: &UnitInfo,
228 ) -> Result<VariableCache, DebugError> {
229 let function_variable_cache = VariableCache::new_dwarf_cache(
230 die_cursor_state.function_die.offset(),
231 VariableName::LocalScopeRoot,
232 unit_info,
233 )?;
234
235 Ok(function_variable_cache)
236 }
237
238 #[tracing::instrument(level = "trace", skip_all, fields(parent_variable = ?parent_variable.variable_key()))]
240 pub fn cache_deferred_variables(
241 &self,
242 cache: &mut VariableCache,
243 memory: &mut dyn MemoryInterface,
244 parent_variable: &mut Variable,
245 frame_info: StackFrameInfo<'_>,
246 ) -> Result<(), DebugError> {
247 if !parent_variable.is_valid() {
248 return Ok(());
250 }
251
252 if cache.has_children(parent_variable) {
254 return Ok(());
255 }
256
257 match parent_variable.variable_node_type {
258 VariableNodeType::TypeOffset(header_offset, unit_offset)
259 | VariableNodeType::DirectLookup(header_offset, unit_offset) => {
260 let Some(unit_info) = self
261 .unit_infos
262 .iter()
263 .find(|unit_info| unit_info.unit.header.offset() == header_offset.into())
264 else {
265 return Err(DebugError::Other(
266 "Failed to find unit info for offset lookup.".to_string(),
267 ));
268 };
269
270 let mut type_tree = unit_info.unit.entries_tree(Some(unit_offset))?;
272 let parent_node = type_tree.root()?;
273
274 unit_info.process_tree(
275 self,
276 parent_node,
277 parent_variable,
278 memory,
279 cache,
280 frame_info,
281 )?;
282 }
283 VariableNodeType::UnitsLookup => {
284 if self.unit_infos.is_empty() {
285 return Err(DebugError::Other("Missing unit infos".to_string()));
287 }
288
289 for unit_info in self.unit_infos.iter() {
291 let mut entries = unit_info.unit.entries();
292
293 let (_, unit_node) = entries.next_dfs()?.unwrap();
296 let unit_offset = unit_node.offset();
297
298 let mut type_tree = unit_info.unit.entries_tree(Some(unit_offset))?;
299 let parent_node = type_tree.root()?;
300
301 unit_info.process_tree(
302 self,
303 parent_node,
304 parent_variable,
305 memory,
306 cache,
307 frame_info,
308 )?;
309 }
310 }
311 _ => {
312 }
314 }
315 Ok(())
316 }
317
318 fn get_stackframe_from_symbols(
320 &self,
321 address: u64,
322 unwind_registers: &DebugRegisters,
323 ) -> Result<Vec<StackFrame>, DebugError> {
324 let Some(ref addr2line) = self.addr2line else {
325 return Ok(vec![]);
326 };
327 let Some(fn_name) = addr2line.find_symbol(address) else {
328 return Ok(vec![]);
329 };
330
331 let mut fn_name = fn_name.to_string();
332 for lang in [
333 gimli::DW_LANG_Rust,
334 gimli::DW_LANG_C_plus_plus,
335 gimli::DW_LANG_C_plus_plus_03,
336 gimli::DW_LANG_C_plus_plus_11,
337 gimli::DW_LANG_C_plus_plus_14,
338 ] {
339 if let Some(demangle) = addr2line::demangle(&fn_name, lang) {
340 fn_name = demangle;
341 break;
342 }
343 }
344
345 Ok(vec![StackFrame {
346 id: get_object_reference(),
347 function_name: format!(
348 "{fn_name} @ {address:#0width$x}>",
349 width = (unwind_registers.get_address_size_bytes() * 2 + 2)
350 ),
351 source_location: None,
352 registers: unwind_registers.clone(),
353 pc: RegisterValue::from(address),
354 frame_base: None,
355 is_inlined: false,
356 local_variables: None,
357 canonical_frame_address: None,
358 }])
359 }
360
361 pub(crate) fn get_stackframe_info(
365 &self,
366 memory: &mut impl MemoryInterface,
367 address: u64,
368 cfa: Option<u64>,
369 unwind_registers: &DebugRegisters,
370 ) -> Result<Vec<StackFrame>, DebugError> {
371 let unknown_function = || {
374 format!(
375 "<unknown function @ {address:#0width$x}>",
376 width = (unwind_registers.get_address_size_bytes() * 2 + 2)
377 )
378 };
379
380 let Ok((unit_info, functions)) = self.get_function_dies(address) else {
381 return self.get_stackframe_from_symbols(address, unwind_registers);
383 };
384 if functions.is_empty() {
385 return self.get_stackframe_from_symbols(address, unwind_registers);
387 }
388
389 let frame_base = functions[0].frame_base(
392 self,
393 memory,
394 StackFrameInfo {
395 registers: unwind_registers,
396 frame_base: None,
397 canonical_frame_address: cfa,
398 },
399 )?;
400
401 let mut frames = Vec::new();
402
403 for function_pair in functions.windows(2) {
406 let function_die = &function_pair[0];
407 let next_function = &function_pair[1];
408
409 let function_name = function_die
410 .function_name(self)
411 .unwrap_or_else(unknown_function);
412
413 tracing::debug!("UNWIND: Function name: {}", function_name);
414
415 assert!(next_function.is_inline());
416
417 let address_size = unit_info.unit.header.address_size() as u64;
419
420 let Some(next_function_low_pc) = next_function.low_pc() else {
421 tracing::warn!(
422 "UNWIND: Unknown starting address for inlined function {}.",
423 function_name
424 );
425 continue;
426 };
427
428 if !(next_function_low_pc > address_size && next_function_low_pc < u32::MAX as u64) {
429 tracing::warn!("UNWIND: Unknown call site for inlined function {function_name}.");
430 continue;
431 }
432
433 let inlined_call_site = RegisterValue::from(next_function_low_pc);
435
436 tracing::debug!(
437 "UNWIND: Callsite for inlined function {:?}",
438 next_function.function_name(self)
439 );
440
441 let inlined_caller_source_location = next_function.inline_call_location(self);
442
443 tracing::debug!("UNWIND: Call site: {inlined_caller_source_location:?}");
444
445 let local_variables = self
449 .create_function_scope_cache(function_die, unit_info)
450 .inspect_err(|error| {
451 tracing::error!("Could not resolve function variables. {error}. Continuing...");
452 })
453 .ok();
454
455 frames.push(StackFrame {
456 id: get_object_reference(),
457 function_name,
458 source_location: inlined_caller_source_location,
459 registers: unwind_registers.clone(),
460 pc: inlined_call_site,
461 frame_base,
462 is_inlined: function_die.is_inline(),
463 local_variables,
464 canonical_frame_address: cfa,
465 });
466 }
467
468 #[allow(clippy::unwrap_used)]
471 let last_function = functions.last().unwrap();
472
473 let function_name = last_function
474 .function_name(self)
475 .unwrap_or_else(unknown_function);
476
477 let function_location = self.get_source_location(address);
478
479 let local_variables =
482 self.create_function_scope_cache(last_function, unit_info)
483 .map_or_else(
484 |error| {
485 tracing::error!(
486 "Could not resolve function variables. {error}. Continuing...",
487 );
488 None
489 },
490 Some,
491 );
492
493 frames.push(StackFrame {
494 id: get_object_reference(),
495 function_name,
496 source_location: function_location,
497 registers: unwind_registers.clone(),
498 pc: match unwind_registers.get_address_size_bytes() {
499 4 => RegisterValue::U32(address as u32),
500 8 => RegisterValue::U64(address),
501 _ => RegisterValue::from(address),
502 },
503 frame_base,
504 is_inlined: last_function.is_inline(),
505 local_variables,
506 canonical_frame_address: cfa,
507 });
508
509 Ok(frames)
510 }
511
512 pub fn unwind(
540 &self,
541 core: &mut impl MemoryInterface,
542 initial_registers: DebugRegisters,
543 exception_handler: &dyn ExceptionInterface,
544 instruction_set: Option<InstructionSet>,
545 ) -> Result<Vec<StackFrame>, probe_rs::Error> {
546 self.unwind_impl(initial_registers, core, exception_handler, instruction_set)
547 }
548
549 pub(crate) fn unwind_impl(
550 &self,
551 initial_registers: DebugRegisters,
552 memory: &mut impl MemoryInterface,
553 exception_handler: &dyn ExceptionInterface,
554 instruction_set: Option<InstructionSet>,
555 ) -> Result<Vec<StackFrame>, probe_rs::Error> {
556 let mut stack_frames = Vec::<StackFrame>::new();
557
558 let mut unwind_context = Box::new(gimli::UnwindContext::new());
559
560 let mut unwind_registers = initial_registers;
561
562 'unwind: while let Some(frame_pc_register_value) =
564 unwind_registers.get_program_counter().and_then(|pc| {
565 if pc.is_zero() | pc.is_max_value() {
566 None
567 } else {
568 pc.value
569 }
570 })
571 {
572 let frame_pc = frame_pc_register_value.try_into().map_err(|error| {
573 let message = format!("Cannot convert register value for program counter to a 64-bit integer value: {error:?}");
574 probe_rs::Error::Register(message)
575 })?;
576
577 tracing::trace!(
581 "UNWIND: Will generate `StackFrame` for function at address (PC) {frame_pc_register_value:#}"
582 );
583 let unwind_info = get_unwind_info(&mut unwind_context, &self.frame_section, frame_pc);
584
585 let cfa = unwind_info
587 .as_ref()
588 .ok()
589 .and_then(|unwind_info| determine_cfa(&unwind_registers, unwind_info).ok())
590 .flatten();
591
592 let cached_stack_frames =
594 match self.get_stackframe_info(memory, frame_pc, cfa, &unwind_registers) {
595 Ok(cached_stack_frames) => cached_stack_frames,
596 Err(e) => {
597 tracing::error!("UNWIND: Unable to complete `StackFrame` information: {e}");
598 break;
600 }
601 };
602
603 if !cached_stack_frames.is_empty() {
607 for frame in cached_stack_frames.into_iter().rev() {
608 if frame.is_inlined {
609 tracing::trace!(
610 "UNWIND: Found inlined function - name={}, pc={}",
611 frame.function_name,
612 frame.pc
613 );
614 }
615 stack_frames.push(frame);
616 }
617 } else {
618 stack_frames.push(StackFrame {
621 id: get_object_reference(),
622 function_name: format!(
623 "<unknown function @ {:#0width$x}>",
624 frame_pc,
625 width = (unwind_registers.get_address_size_bytes() * 2 + 2)
626 ),
627 source_location: self.get_source_location(frame_pc),
628 registers: unwind_registers.clone(),
629 pc: frame_pc_register_value,
630 frame_base: None,
631 is_inlined: false,
632 local_variables: None,
633 canonical_frame_address: None,
634 });
635 };
636
637 tracing::trace!("UNWIND - Preparing to unwind the registers for the previous frame.");
639
640 let callee_frame_registers = unwind_registers.clone();
643
644 let unwind_info = match unwind_info {
647 Ok(unwind_info) => {
648 tracing::trace!("UNWIND: Found unwind info for address {frame_pc:#010x}");
649 unwind_info
650 }
651 Err(err) => {
652 tracing::trace!(
653 "UNWIND: Unable to find unwind info for address {frame_pc:#010x}: {err}"
654 );
655 if let ControlFlow::Break(error) = exception_handler.unwind_without_debuginfo(
656 &mut unwind_registers,
657 frame_pc,
658 &stack_frames,
659 instruction_set,
660 memory,
661 ) {
662 if let Some(error) = error {
663 tracing::error!("{:?}", &error);
665 if let Some(first_frame) = stack_frames.first_mut() {
666 first_frame.function_name =
667 format!("{} : ERROR : {error}", first_frame.function_name);
668 };
669 }
670 break 'unwind;
671 }
672
673 if callee_frame_registers == unwind_registers {
674 tracing::debug!("No change, preventing infinite loop");
675 break;
676 }
677 continue 'unwind;
678 }
679 };
680
681 for debug_register in unwind_registers.0.iter_mut() {
683 if debug_register
685 .core_register
686 .register_has_role(RegisterRole::ProgramCounter)
687 {
688 continue;
689 }
690
691 match unwind_register(
692 debug_register,
693 &callee_frame_registers,
694 unwind_info,
695 cfa,
696 memory,
697 ) {
698 Err(error) => {
699 tracing::error!("{:?}", &error);
700 if let Some(first_frame) = stack_frames.last_mut() {
701 first_frame.function_name =
702 format!("{} : ERROR: {error}", first_frame.function_name);
703 };
704 break 'unwind;
705 }
706 Ok(val) => {
707 debug_register.value = val;
708 }
709 };
710 }
711
712 if unwind_registers
720 .get_return_address()
721 .is_some_and(|ra| ra.value.is_some())
722 {
723 match exception_handler.exception_details(memory, &unwind_registers, self) {
724 Ok(Some(exception_info)) => {
725 tracing::trace!(
726 "UNWIND: Stack unwind reached an exception handler {}",
727 exception_info.description
728 );
729 unwind_registers = exception_info.handler_frame.registers.clone();
730 stack_frames.push(exception_info.handler_frame);
731 continue 'unwind;
733 }
734 Ok(None) => {
735 tracing::trace!(
736 "UNWIND: No exception context found. Stack unwind will continue."
737 );
738 }
739 Err(e) => {
740 let message = format!(
741 "UNWIND: Error while checking for exception context. The stack trace will not include the calling frames. : {e:?}"
742 );
743 tracing::warn!("{message}");
744 stack_frames.push(StackFrame {
745 id: get_object_reference(),
746 function_name: message,
747 source_location: None,
748 registers: unwind_registers.clone(),
749 pc: frame_pc_register_value,
750 frame_base: None,
751 is_inlined: false,
752 local_variables: None,
753 canonical_frame_address: None,
754 });
755 break 'unwind;
756 }
757 };
758 }
759
760 let unwound_return_address = unwind_registers
761 .get_register_by_role(&RegisterRole::ReturnAddress)
762 .ok()
763 .and_then(|reg| reg.value);
764
765 let program_counter = unwind_registers.get_program_counter_mut().unwrap();
766
767 let Ok(current_pc) =
768 callee_frame_registers.get_register_value_by_role(&RegisterRole::ProgramCounter)
769 else {
770 let error = probe_rs::Error::Other(
771 "UNWIND: Tried to unwind return address value where current program counter is unknown.".to_string()
772 );
773 tracing::error!("{:?}", &error);
774 if let Some(first_frame) = stack_frames.last_mut() {
775 first_frame.function_name =
776 format!("{} : ERROR: {error}", first_frame.function_name);
777 };
778 break 'unwind;
779 };
780 let mut register_rule_string = "PC=(unwound LR) (dwarf Undefined)".to_string();
783 program_counter.value = unwound_return_address.and_then(|return_address| {
784 unwind_program_counter_register(
785 return_address,
786 current_pc,
787 instruction_set,
788 &mut register_rule_string,
789 )
790 });
791
792 tracing::trace!(
793 "UNWIND - {:>10}: Caller: {}\tCallee: {}\tRule: {}",
794 program_counter.get_register_name(),
795 program_counter.value.unwrap_or_default(),
796 callee_frame_registers
797 .get_register(program_counter.core_register.id)
798 .and_then(|reg| reg.value)
799 .unwrap_or_default(),
800 register_rule_string,
801 );
802
803 if callee_frame_registers == unwind_registers {
804 tracing::debug!("No change, preventing infinite loop");
805 break;
806 }
807 }
808
809 Ok(stack_frames)
810 }
811
812 #[tracing::instrument(skip_all)]
816 pub fn get_breakpoint_location(
817 &self,
818 path: TypedPath,
819 line: u64,
820 column: Option<u64>,
821 ) -> Result<VerifiedBreakpoint, DebugError> {
822 tracing::debug!(
823 "Looking for breakpoint location for {}:{}:{}",
824 path.display(),
825 line,
826 column
827 .map(|c| c.to_string())
828 .unwrap_or_else(|| "-".to_owned())
829 );
830 VerifiedBreakpoint::for_source_location(self, path, line, column)
831 }
832
833 pub(crate) fn get_path(
836 &self,
837 unit: &gimli::read::Unit<DwarfReader>,
838 file_index: u64,
839 ) -> Option<TypedPathBuf> {
840 let line_program = unit.line_program.as_ref()?;
841 let header = line_program.header();
842 let Some(file_entry) = header.file(file_index) else {
843 tracing::warn!(
844 "Unable to extract file entry for file_index {:?}.",
845 file_index
846 );
847 return None;
848 };
849 let file_name_attr_string = self.dwarf.attr_string(unit, file_entry.path_name()).ok()?;
850 let name_path = from_utf8(&file_name_attr_string).ok()?;
851
852 let dir_name_attr_string = file_entry
853 .directory(header)
854 .and_then(|dir| self.dwarf.attr_string(unit, dir).ok());
855
856 let dir_path = dir_name_attr_string.and_then(|dir_name| {
857 from_utf8(&dir_name)
858 .ok()
859 .map(|p| TypedPath::derive(p).to_path_buf())
860 });
861
862 let mut combined_path = match dir_path {
863 Some(dir_path) => dir_path.join(name_path),
864 None => TypedPath::derive(name_path).to_path_buf(),
865 };
866
867 if combined_path.is_relative() {
868 let comp_dir = unit
869 .comp_dir
870 .as_ref()
871 .map(|dir| from_utf8(dir))
872 .transpose()
873 .ok()?
874 .map(TypedPath::derive);
875 if let Some(comp_dir) = comp_dir {
876 combined_path = comp_dir.join(&combined_path);
877 }
878 }
879
880 Some(combined_path)
881 }
882
883 pub(crate) fn find_file_and_directory(
884 &self,
885 unit: &gimli::read::Unit<DwarfReader>,
886 file_index: u64,
887 ) -> Option<TypedPathBuf> {
888 let combined_path = self.get_path(unit, file_index)?;
889
890 Some(combined_path)
891 }
892
893 pub(crate) fn compile_unit_info(
895 &self,
896 address: u64,
897 ) -> Result<&super::unit_info::UnitInfo, DebugError> {
898 for header in &self.unit_infos {
899 match self.dwarf.unit_ranges(&header.unit) {
900 Ok(mut ranges) => {
901 while let Ok(Some(range)) = ranges.next() {
902 if range.contains(address) {
903 return Ok(header);
904 }
905 }
906 }
907 Err(_) => continue,
908 };
909 }
910 Err(DebugError::WarnAndContinue {
911 message: format!(
912 "No debug information available for the instruction at {address:#010x}. Please consider using instruction level stepping."
913 ),
914 })
915 }
916
917 pub(crate) fn get_function_dies(
922 &self,
923 address: u64,
924 ) -> Result<(&UnitInfo, Vec<FunctionDie>), DebugError> {
925 for unit_info in &self.unit_infos {
926 let function_dies = unit_info.get_function_dies(self, address)?;
927
928 if !function_dies.is_empty() {
929 return Ok((unit_info, function_dies));
930 }
931 }
932 Err(DebugError::Other(format!(
933 "No function DIE's at address {address:#x}."
934 )))
935 }
936
937 pub(crate) fn resolve_die_reference<'debug_info, 'unit_info>(
939 &'debug_info self,
940 attribute: gimli::DwAt,
941 die: &Die,
942 unit_info: &'unit_info UnitInfo,
943 ) -> Option<Die<'debug_info, 'debug_info>>
944 where
945 'unit_info: 'debug_info,
946 {
947 let attr = die.attr(attribute).ok().flatten()?;
948
949 self.resolve_die_reference_with_unit(&attr, unit_info)
950 .ok()
951 .map(|(_, die)| die)
952 }
953
954 pub fn endianness(&self) -> RunTimeEndian {
956 self.endianness
957 }
958
959 pub(crate) fn resolve_die_reference_with_unit<'debug_info, 'unit_info>(
961 &'debug_info self,
962 attr: &gimli::Attribute<GimliReader>,
963 unit_info: &'unit_info UnitInfo,
964 ) -> Result<(&'debug_info UnitInfo, Die<'debug_info, 'debug_info>), DebugError>
965 where
966 'unit_info: 'debug_info,
967 {
968 match attr.value() {
969 gimli::AttributeValue::UnitRef(unit_ref) => {
970 Ok((unit_info, unit_info.unit.entry(unit_ref)?))
971 }
972 gimli::AttributeValue::DebugInfoRef(offset) => {
973 for unit_info in &self.unit_infos {
974 let Some(unit_offset) = offset.to_unit_offset(&unit_info.unit.header) else {
975 continue;
976 };
977
978 let entry = unit_info.unit.entry(unit_offset).map_err(|error| {
979 DebugError::Other(format!(
980 "Error reading DIE at debug info offset {:#x} : {}",
981 offset.0, error
982 ))
983 })?;
984 return Ok((unit_info, entry));
985 }
986
987 Err(DebugError::Other(format!(
988 "Unable to find unit info for debug info offset {:#x}",
989 offset.0
990 )))
991 }
992 other_attribute_value => Err(DebugError::Other(format!(
993 "Unimplemented attribute value {other_attribute_value:?}"
994 ))),
995 }
996 }
997}
998
999pub(crate) fn canonical_path_eq(primary_path: TypedPath, secondary_path: TypedPath) -> bool {
1001 primary_path.normalize() == secondary_path.normalize()
1002}
1003
1004pub fn get_unwind_info<'a>(
1006 unwind_context: &'a mut UnwindContext<GimliReaderOffset>,
1007 frame_section: &DebugFrame<DwarfReader>,
1008 frame_program_counter: u64,
1009) -> Result<&'a gimli::UnwindTableRow<GimliReaderOffset>, DebugError> {
1010 let transform_error = |error| {
1011 DebugError::Other(format!(
1012 "UNWIND: Error reading FrameDescriptorEntry at PC={:x} : {}",
1013 frame_program_counter, error
1014 ))
1015 };
1016
1017 let unwind_bases = BaseAddresses::default();
1018
1019 let frame_descriptor_entry = frame_section
1020 .fde_for_address(
1021 &unwind_bases,
1022 frame_program_counter,
1023 DebugFrame::cie_from_offset,
1024 )
1025 .map_err(transform_error)?;
1026
1027 frame_descriptor_entry
1028 .unwind_info_for_address(
1029 frame_section,
1030 &unwind_bases,
1031 unwind_context,
1032 frame_program_counter,
1033 )
1034 .map_err(transform_error)
1035}
1036
1037pub fn determine_cfa<R: gimli::ReaderOffset>(
1039 unwind_registers: &DebugRegisters,
1040 unwind_info: &UnwindTableRow<R>,
1041) -> Result<Option<u64>, probe_rs::Error> {
1042 let gimli::CfaRule::RegisterAndOffset { register, offset } = unwind_info.cfa() else {
1043 unimplemented!()
1044 };
1045
1046 let reg_val = unwind_registers
1047 .get_register_by_dwarf_id(register.0)
1048 .and_then(|register| register.value);
1049
1050 let cfa = match reg_val {
1051 None => {
1052 tracing::error!(
1053 "UNWIND: `StackFrameIterator` unable to determine the unwind CFA: Missing value of register {}",
1054 register.0
1055 );
1056 None
1057 }
1058
1059 Some(reg_val) if reg_val.is_zero() => {
1060 tracing::trace!(
1063 "UNWIND: Stack unwind complete - The FP register value unwound to a value of zero."
1064 );
1065 None
1066 }
1067
1068 Some(reg_val) => {
1069 let unwind_cfa = add_to_address(
1070 reg_val.try_into()?,
1071 *offset,
1072 unwind_registers.get_address_size_bytes(),
1073 );
1074 tracing::trace!(
1075 "UNWIND - CFA : {:#010x}\tRule: {:?}",
1076 unwind_cfa,
1077 unwind_info.cfa()
1078 );
1079 Some(unwind_cfa)
1080 }
1081 };
1082
1083 Ok(cfa)
1084}
1085
1086pub fn unwind_pc_without_debuginfo(
1088 unwind_registers: &mut DebugRegisters,
1089 _frame_pc: u64,
1090 instruction_set: Option<probe_rs::InstructionSet>,
1091) -> ControlFlow<Option<DebugError>> {
1092 let callee_frame_registers = unwind_registers.clone();
1099 let unwound_return_address: Option<RegisterValue> = unwind_registers
1100 .get_return_address()
1101 .and_then(|lr| lr.value);
1102
1103 if let Some(calling_pc) = unwind_registers.get_program_counter_mut() {
1105 let mut register_rule_string = "PC=(unwound LR) (dwarf Undefined)".to_string();
1106
1107 let Ok(current_pc) =
1108 callee_frame_registers.get_register_value_by_role(&RegisterRole::ProgramCounter)
1109 else {
1110 return ControlFlow::Break(
1111 Some(probe_rs::Error::Other(
1112 "UNWIND: Tried to unwind return address value where current program counter is unknown.".to_string()
1113 ).into())
1114 );
1115 };
1116 calling_pc.value = unwound_return_address.and_then(|return_address| {
1119 unwind_program_counter_register(
1120 return_address,
1121 current_pc,
1122 instruction_set,
1123 &mut register_rule_string,
1124 )
1125 });
1126 }
1127
1128 ControlFlow::Continue(())
1129}
1130
1131pub fn unwind_register(
1133 debug_register: &super::DebugRegister,
1134 callee_frame_registers: &DebugRegisters,
1136 unwind_info: &gimli::UnwindTableRow<GimliReaderOffset>,
1137 unwind_cfa: Option<u64>,
1138 memory: &mut dyn MemoryInterface,
1139) -> Result<Option<RegisterValue>, probe_rs::Error> {
1140 use gimli::read::RegisterRule;
1141
1142 let register_rule = debug_register
1144 .dwarf_id
1145 .map(|register_position| unwind_info.register(gimli::Register(register_position)))
1146 .unwrap_or(RegisterRule::Undefined);
1147
1148 unwind_register_using_rule(
1149 debug_register.core_register,
1150 callee_frame_registers,
1151 unwind_cfa,
1152 memory,
1153 register_rule,
1154 )
1155}
1156
1157fn unwind_register_using_rule(
1158 debug_register: &probe_rs::CoreRegister,
1159 callee_frame_registers: &DebugRegisters,
1160 unwind_cfa: Option<u64>,
1161 memory: &mut dyn MemoryInterface,
1162 register_rule: gimli::RegisterRule<usize>,
1163) -> Result<Option<RegisterValue>, probe_rs::Error> {
1164 use gimli::read::RegisterRule;
1165
1166 let mut register_rule_string = format!("{register_rule:?}");
1167
1168 let new_value = match register_rule {
1169 RegisterRule::Undefined => {
1170 match &debug_register {
1173 fp if fp.register_has_role(RegisterRole::FramePointer) => {
1174 register_rule_string = "FP=CFA (dwarf Undefined)".to_string();
1175 unwind_cfa.map(|unwind_cfa| {
1176 if fp.data_type == RegisterDataType::UnsignedInteger(32) {
1177 RegisterValue::U32(unwind_cfa as u32 & !0b11)
1178 } else {
1179 RegisterValue::U64(unwind_cfa & !0b11)
1180 }
1181 })
1182 }
1183 sp if sp.register_has_role(RegisterRole::StackPointer) => {
1184 register_rule_string = "SP=CFA (dwarf Undefined)".to_string();
1187 unwind_cfa.map(|unwind_cfa| {
1188 if sp.data_type == RegisterDataType::UnsignedInteger(32) {
1189 RegisterValue::U32(unwind_cfa as u32 & !0b11)
1190 } else {
1191 RegisterValue::U64(unwind_cfa & !0b11)
1192 }
1193 })
1194 }
1195 lr if lr.register_has_role(RegisterRole::ReturnAddress) => {
1196 let Ok(current_pc) = callee_frame_registers
1197 .get_register_value_by_role(&RegisterRole::ProgramCounter)
1198 else {
1199 return Err(
1200 probe_rs::Error::Other(
1201 "UNWIND: Tried to unwind return address value where current program counter is unknown.".to_string()
1202 )
1203 );
1204 };
1205 let Some(current_lr) = callee_frame_registers
1206 .get_register_by_role(&RegisterRole::ReturnAddress)
1207 .ok()
1208 .and_then(|lr| lr.value)
1209 else {
1210 return Err(
1211 probe_rs::Error::Other(
1212 "UNWIND: Tried to unwind return address value where current return address is unknown.".to_string()
1213 )
1214 );
1215 };
1216
1217 let current_lr_value: u64 = current_lr.try_into()?;
1218
1219 if current_pc == current_lr_value & !0b1 {
1220 register_rule_string = "LR=Undefined (dwarf Undefined)".to_string();
1223 None
1224 } else {
1225 register_rule_string = "LR=Current LR (dwarf Undefined)".to_string();
1227 Some(current_lr)
1228 }
1229 }
1230 pc if pc.register_has_role(RegisterRole::ProgramCounter) => {
1231 unreachable!("The program counter is handled separately")
1232 }
1233 other_register => {
1234 match other_register.unwind_rule {
1237 UnwindRule::Preserve => {
1238 register_rule_string = "Preserve".to_string();
1239 callee_frame_registers
1240 .get_register(other_register.id)
1241 .and_then(|reg| reg.value)
1242 }
1243 UnwindRule::Clear => {
1244 register_rule_string = "Clear".to_string();
1245 None
1246 }
1247 UnwindRule::SpecialRule => {
1248 register_rule_string = "Clear (no unwind rules specified)".to_string();
1252 None
1253 }
1254 }
1255 }
1256 }
1257 }
1258
1259 RegisterRule::SameValue => callee_frame_registers
1260 .get_register(debug_register.id)
1261 .and_then(|reg| reg.value),
1262
1263 RegisterRule::Offset(address_offset) => {
1264 let Some(unwind_cfa) = unwind_cfa else {
1266 return Err(probe_rs::Error::Other(
1267 "UNWIND: Tried to unwind `RegisterRule` at CFA = None.".to_string(),
1268 ));
1269 };
1270 let address_size = callee_frame_registers.get_address_size_bytes();
1271 let previous_frame_register_address =
1272 add_to_address(unwind_cfa, address_offset, address_size);
1273
1274 register_rule_string = format!("CFA {register_rule:?}");
1275
1276 let result = match address_size {
1278 4 => {
1279 let mut buff = [0u8; 4];
1280 memory
1281 .read(previous_frame_register_address, &mut buff)
1282 .map(|_| RegisterValue::U32(u32::from_le_bytes(buff)))
1283 }
1284 8 => {
1285 let mut buff = [0u8; 8];
1286 memory
1287 .read(previous_frame_register_address, &mut buff)
1288 .map(|_| RegisterValue::U64(u64::from_le_bytes(buff)))
1289 }
1290 _ => {
1291 return Err(Error::Other(format!(
1292 "UNWIND: Address size {} not supported.",
1293 address_size
1294 )));
1295 }
1296 };
1297
1298 match result {
1299 Ok(register_value) => Some(register_value),
1300 Err(error) => {
1301 tracing::error!(
1302 "UNWIND: Rule: Offset {} from address {:#010x}",
1303 address_offset,
1304 unwind_cfa
1305 );
1306
1307 return Err(Error::Other(format!(
1308 "UNWIND: Failed to read value for register {} from address {} ({} bytes): {}",
1309 debug_register,
1310 RegisterValue::from(previous_frame_register_address),
1311 4,
1312 error
1313 )));
1314 }
1315 }
1316 }
1317 _ => unimplemented!(),
1319 };
1320
1321 tracing::trace!(
1322 "UNWIND - {:>10}: Caller: {}\tCallee: {}\tRule: {}",
1323 debug_register,
1324 new_value.unwrap_or_default(),
1325 callee_frame_registers
1326 .get_register(debug_register.id)
1327 .and_then(|reg| reg.value)
1328 .unwrap_or_default(),
1329 register_rule_string,
1330 );
1331 Ok(new_value)
1332}
1333
1334fn unwind_program_counter_register(
1336 return_address: RegisterValue,
1337 current_pc: u64,
1338 instruction_set: Option<InstructionSet>,
1339 register_rule_string: &mut String,
1340) -> Option<RegisterValue> {
1341 if return_address.is_max_value() || return_address.is_zero() {
1342 tracing::warn!(
1343 "No reliable return address is available, so we cannot determine the program counter to unwind the previous frame."
1344 );
1345 return None;
1346 }
1347
1348 match return_address {
1349 RegisterValue::U32(return_address) => {
1350 match instruction_set {
1351 Some(InstructionSet::Thumb2) => {
1352 *register_rule_string = "PC=(unwound LR & !0b1) (dwarf Undefined)".to_string();
1357 Some(RegisterValue::U32(return_address & !0b1))
1358 }
1359 Some(InstructionSet::RV32C) => {
1360 *register_rule_string = "PC=(unwound x1 - 2) (dwarf Undefined)".to_string();
1361 Some(RegisterValue::U32(return_address - 2))
1362 }
1363 Some(InstructionSet::RV32) => {
1364 *register_rule_string = "PC=(unwound x1 - 4) (dwarf Undefined)".to_string();
1365 Some(RegisterValue::U32(return_address - 4))
1366 }
1367 Some(InstructionSet::Xtensa) => {
1368 let upper_bits = (current_pc as u32) & 0xC000_0000;
1369 *register_rule_string = "PC=(unwound x0 - 3) (dwarf Undefined)".to_string();
1370 Some(RegisterValue::U32(
1371 (return_address & 0x3FFF_FFFF | upper_bits) - 3,
1372 ))
1373 }
1374 _ => Some(RegisterValue::U32(return_address)),
1375 }
1376 }
1377 RegisterValue::U64(return_address) => Some(RegisterValue::U64(return_address)),
1378 RegisterValue::U128(_) => {
1379 tracing::warn!("128 bit address space not supported");
1380 None
1381 }
1382 }
1383}
1384
1385fn add_to_address(address: u64, offset: i64, address_size_in_bytes: usize) -> u64 {
1393 match address_size_in_bytes {
1394 4 => {
1395 if offset >= 0 {
1396 (address as u32)
1397 .checked_add(offset as u32)
1398 .map(u64::from)
1399 .unwrap_or(0x0)
1400 } else {
1401 (address as u32).saturating_sub(offset.unsigned_abs() as u32) as u64
1402 }
1403 }
1404 8 => {
1405 if offset >= 0 {
1406 address.checked_add(offset as u64).unwrap_or(0x0)
1407 } else {
1408 address.saturating_sub(offset.unsigned_abs())
1409 }
1410 }
1411 _ => {
1412 panic!(
1413 "UNWIND: Address size {} not supported. Please report this as a bug.",
1414 address_size_in_bytes
1415 );
1416 }
1417 }
1418}
1419
1420#[cfg(test)]
1421mod test {
1422 use crate::{
1423 DebugInfo, DebugRegister, DebugRegisters,
1424 exception_handling::{
1425 armv6m::ArmV6MExceptionHandler, armv7m::ArmV7MExceptionHandler,
1426 exception_handler_for_core,
1427 },
1428 stack_frame::{StackFrameInfo, TestFormatter},
1429 test::debug_registers,
1430 };
1431
1432 use gimli::RegisterRule;
1433 use probe_rs::{
1434 CoreDump, RegisterValue,
1435 architecture::arm::core::registers::cortex_m::{self, CORTEX_M_CORE_REGISTERS},
1436 test::MockMemory,
1437 };
1438 use std::path::{Path, PathBuf};
1439 use test_case::test_case;
1440
1441 use super::unwind_register_using_rule;
1442
1443 fn get_path_for_test_files(relative_file: &str) -> PathBuf {
1445 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1446 path.push("tests");
1447 path.push(relative_file);
1448 path
1449 }
1450
1451 fn load_test_elf_as_debug_info(elf_file: &str) -> DebugInfo {
1454 let path = get_path_for_test_files(elf_file);
1455 DebugInfo::from_file(&path).unwrap_or_else(|err: crate::DebugError| {
1456 panic!("Failed to open file {}: {:?}", path.display(), err)
1457 })
1458 }
1459
1460 #[test]
1461 fn unwinding_first_instruction_after_exception() {
1462 let debug_info = load_test_elf_as_debug_info("exceptions");
1463
1464 let values: Vec<_> = [
1488 0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000, 0x00000000, 0x00000000, 0x2001fff0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2001ffd0, 0xfffffff9, 0x00000182, 0x2001ffd0, 0x00000000, 0x2100000b, ]
1508 .into_iter()
1509 .enumerate()
1510 .map(|(id, r)| DebugRegister {
1511 dwarf_id: Some(id as u16),
1512 core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1513 value: Some(RegisterValue::U32(r)),
1514 })
1515 .collect();
1516
1517 let regs = DebugRegisters(values);
1518
1519 let expected_regs = regs.clone();
1520
1521 let mut mocked_mem = MockMemory::new();
1522
1523 mocked_mem.add_word_range(
1538 0x2001_ffd0,
1539 &[
1540 0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000, 0x0000017f, 0x00000180,
1541 0x21000000, 0x2001fff8, 0x00000161, 0x00000000, 0x0000013d,
1542 ],
1543 );
1544
1545 let exception_handler = Box::new(ArmV6MExceptionHandler {});
1546
1547 let frames = debug_info
1548 .unwind_impl(
1549 regs,
1550 &mut mocked_mem,
1551 exception_handler.as_ref(),
1552 Some(probe_rs_target::InstructionSet::Thumb2),
1553 )
1554 .unwrap();
1555
1556 let first_frame = &frames[0];
1557
1558 assert_eq!(first_frame.pc, RegisterValue::U32(0x00000182));
1559
1560 assert_eq!(
1561 first_frame.function_name,
1562 "__cortex_m_rt_SVCall_trampoline".to_string()
1563 );
1564
1565 assert_eq!(first_frame.registers, expected_regs);
1566
1567 let next_frame = &frames[1];
1568 assert_eq!(next_frame.function_name, "SVC");
1569 assert_eq!(next_frame.pc, RegisterValue::U32(0x0000017f));
1570
1571 }
1609
1610 #[test]
1611 fn unwinding_in_exception_handler() {
1612 let debug_info = load_test_elf_as_debug_info("exceptions");
1613
1614 let values: Vec<_> = [
1637 0x00000001, 0x2001ff9f, 0x20000047, 0x20000047, 0x00000000, 0x00000000, 0x00000000, 0x2001ffc0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2001ffc0, 0x0000042f, 0x000001a4, 0x2001ffc0, 0x00000000, 0x2100000b, ]
1657 .into_iter()
1658 .enumerate()
1659 .map(|(id, r)| DebugRegister {
1660 dwarf_id: Some(id as u16),
1661 core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1662 value: Some(RegisterValue::U32(r)),
1663 })
1664 .collect();
1665
1666 let regs = DebugRegisters(values);
1667
1668 let mut dummy_mem = MockMemory::new();
1669
1670 dummy_mem.add_word_range(
1689 0x2001_ffc0,
1690 &[
1691 0x2001ffc8, 0x0000018b, 0x2001fff0, 0xfffffff9, 0x00000001, 0x2001ffcf, 0x20000044,
1692 0x20000044, 0x00000000, 0x0000017f, 0x00000180, 0x21000000, 0x2001fff8, 0x00000161,
1693 0x00000000, 0x0000013d,
1694 ],
1695 );
1696
1697 let exception_handler = Box::new(ArmV6MExceptionHandler {});
1698
1699 let frames = debug_info
1700 .unwind_impl(
1701 regs,
1702 &mut dummy_mem,
1703 exception_handler.as_ref(),
1704 Some(probe_rs_target::InstructionSet::Thumb2),
1705 )
1706 .unwrap();
1707
1708 assert_eq!(frames[0].pc, RegisterValue::U32(0x000001a4));
1709
1710 assert_eq!(
1711 frames[1].function_name,
1712 "__cortex_m_rt_SVCall_trampoline".to_string()
1713 );
1714
1715 assert_eq!(frames[1].pc, RegisterValue::U32(0x0000018A)); assert_eq!(
1723 frames[1]
1724 .registers
1725 .get_register(probe_rs::RegisterId(7))
1726 .and_then(|r| r.value),
1727 Some(RegisterValue::U32(0x2001ffc8))
1728 );
1729
1730 let printed_backtrace = frames
1731 .into_iter()
1732 .map(|f| TestFormatter(&f).to_string())
1733 .collect::<Vec<String>>()
1734 .join("");
1735
1736 insta::assert_snapshot!(printed_backtrace);
1737 }
1738
1739 #[test]
1740 fn unwinding_in_exception_trampoline() {
1741 let debug_info = load_test_elf_as_debug_info("exceptions");
1742
1743 let values: Vec<_> = [
1766 0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000, 0x00000000, 0x00000000, 0x2001ffc8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2001ffc8, 0x0000018B, 0x0000018A, 0x2001ffc8, 0x00000000, 0x2100000b, ]
1786 .into_iter()
1787 .enumerate()
1788 .map(|(id, r)| DebugRegister {
1789 dwarf_id: Some(id as u16),
1790 core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1791 value: Some(RegisterValue::U32(r)),
1792 })
1793 .collect();
1794
1795 let regs = DebugRegisters(values);
1796
1797 let mut dummy_mem = MockMemory::new();
1798
1799 dummy_mem.add_word_range(
1816 0x2001_ffc8,
1817 &[
1818 0x2001fff0, 0xfffffff9, 0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000,
1819 0x0000017f, 0x00000180, 0x21000000, 0x2001fff8, 0x00000161, 0x00000000, 0x0000013d,
1820 ],
1821 );
1822
1823 let exception_handler = Box::new(ArmV6MExceptionHandler {});
1824
1825 let frames = debug_info
1826 .unwind_impl(
1827 regs,
1828 &mut dummy_mem,
1829 exception_handler.as_ref(),
1830 Some(probe_rs_target::InstructionSet::Thumb2),
1831 )
1832 .unwrap();
1833
1834 let printed_backtrace = frames
1835 .into_iter()
1836 .map(|f| TestFormatter(&f).to_string())
1837 .collect::<Vec<String>>()
1838 .join("");
1839
1840 insta::assert_snapshot!(printed_backtrace);
1841 }
1842
1843 #[test]
1844 fn unwinding_inlined() {
1845 let debug_info = load_test_elf_as_debug_info("inlined-functions");
1846
1847 let values: Vec<_> = [
1871 0xfffffecc, 0x00000001, 0x00000000, 0x40008140, 0x000f4240, 0xfffffec0, 0x00000000, 0x20003ff0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5000050c, 0x20003ff0, 0x00200000, 0x000002e4, 0x20003ff0, 0x00000000, 0x61000000, ]
1891 .into_iter()
1892 .enumerate()
1893 .map(|(id, r)| DebugRegister {
1894 dwarf_id: Some(id as u16),
1895 core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1896 value: Some(RegisterValue::U32(r)),
1897 })
1898 .collect();
1899
1900 let regs = DebugRegisters(values);
1901
1902 let mut dummy_mem = MockMemory::new();
1903
1904 dummy_mem.add_word_range(
1911 0x2000_3ff0,
1912 &[0x20003ff8, 0x00000161, 0x00000000, 0x0000013d],
1913 );
1914
1915 let exception_handler = Box::new(ArmV7MExceptionHandler);
1916
1917 let frames = debug_info
1918 .unwind_impl(
1919 regs,
1920 &mut dummy_mem,
1921 exception_handler.as_ref(),
1922 Some(probe_rs_target::InstructionSet::Thumb2),
1923 )
1924 .unwrap();
1925
1926 let printed_backtrace = frames
1927 .into_iter()
1928 .map(|f| TestFormatter(&f).to_string())
1929 .collect::<Vec<String>>()
1930 .join("");
1931
1932 insta::assert_snapshot!(printed_backtrace);
1933 }
1934
1935 #[test]
1936 fn test_print_stacktrace() {
1937 let elf = Path::new("./tests/gpio-hal-blinky/elf");
1938 let coredump = include_bytes!("../tests/gpio-hal-blinky/coredump");
1939
1940 let mut adapter = CoreDump::load_raw(coredump).unwrap();
1941 let debug_info = DebugInfo::from_file(elf).unwrap();
1942
1943 let initial_registers = debug_registers(&adapter);
1944 let exception_handler = exception_handler_for_core(adapter.core_type());
1945 let instruction_set = adapter.instruction_set();
1946
1947 let stack_frames = debug_info
1948 .unwind(
1949 &mut adapter,
1950 initial_registers,
1951 exception_handler.as_ref(),
1952 Some(instruction_set),
1953 )
1954 .unwrap();
1955
1956 let printed_backtrace = stack_frames
1957 .into_iter()
1958 .map(|f| TestFormatter(&f).to_string())
1959 .collect::<Vec<String>>()
1960 .join("");
1961
1962 insta::assert_snapshot!(printed_backtrace);
1963 }
1964
1965 #[test_case("RP2040_full_unwind"; "full_unwind Armv6-m using RP2040")]
1966 #[test_case("RP2040_svcall"; "svcall Armv6-m using RP2040")]
1967 #[test_case("RP2040_systick"; "systick Armv6-m using RP2040")]
1968 #[test_case("nRF52833_xxAA_full_unwind"; "full_unwind Armv7-m using nRF52833_xxAA")]
1969 #[test_case("nRF52833_xxAA_svcall"; "svcall Armv7-m using nRF52833_xxAA")]
1970 #[test_case("nRF52833_xxAA_systick"; "systick Armv7-m using nRF52833_xxAA")]
1971 #[test_case("nRF52833_xxAA_hardfault_from_usagefault"; "hardfault_from_usagefault Armv7-m using nRF52833_xxAA")]
1972 #[test_case("nRF52833_xxAA_hardfault_from_busfault"; "hardfault_from_busfault Armv7-m using nRF52833_xxAA")]
1973 #[test_case("nRF52833_xxAA_hardfault_in_systick"; "hardfault_in_systick Armv7-m using nRF52833_xxAA")]
1974 #[test_case("atsamd51p19a"; "Armv7-em from C source code")]
1975 #[test_case("esp32c3_full_unwind"; "full_unwind RISC-V32E using esp32c3")]
1976 #[test_case("esp32s3_esp_hal_panic"; "Xtensa unwinding on an esp32s3 in a panic handler")]
1977 #[test_case("esp32c6_coredump_elf"; "Unwind using a RISC-V coredump in ELF format")]
1978 #[test_case("esp32s3_coredump_elf"; "Unwind using an Xtensa coredump in ELF format")]
1979 fn full_unwind(test_name: &str) {
1980 let debug_info =
1981 load_test_elf_as_debug_info(format!("debug-unwind-tests/{test_name}.elf").as_str());
1982
1983 let coredump_path = coredump_path(format!("debug-unwind-tests/{test_name}"));
1984 let mut adapter = CoreDump::load(&coredump_path).unwrap();
1985
1986 let snapshot_name = test_name.to_string();
1987
1988 let initial_registers = debug_registers(&adapter);
1989 let exception_handler = exception_handler_for_core(adapter.core_type());
1990 let instruction_set = adapter.instruction_set();
1991
1992 let mut stack_frames = debug_info
1993 .unwind(
1994 &mut adapter,
1995 initial_registers,
1996 exception_handler.as_ref(),
1997 Some(instruction_set),
1998 )
1999 .unwrap();
2000
2001 for frame in stack_frames.iter_mut() {
2003 let mut variable_caches = Vec::new();
2004 if let Some(local_variables) = &mut frame.local_variables {
2005 variable_caches.push(local_variables);
2006 }
2007 for variable_cache in variable_caches {
2008 variable_cache.recurse_deferred_variables(
2010 &debug_info,
2011 &mut adapter,
2012 10,
2013 StackFrameInfo {
2014 registers: &frame.registers,
2015 frame_base: frame.frame_base,
2016 canonical_frame_address: frame.canonical_frame_address,
2017 },
2018 );
2019 }
2020 }
2021
2022 insta::assert_yaml_snapshot!(snapshot_name, stack_frames);
2025 }
2026
2027 #[test_case("RP2040_full_unwind"; "Armv6-m using RP2040")]
2028 #[test_case("nRF52833_xxAA_full_unwind"; "Armv7-m using nRF52833_xxAA")]
2029 #[test_case("atsamd51p19a"; "Armv7-em from C source code")]
2030 fn static_variables(chip_name: &str) {
2032 let debug_info =
2035 load_test_elf_as_debug_info(format!("debug-unwind-tests/{chip_name}.elf").as_str());
2036
2037 let coredump_path = coredump_path(format!("debug-unwind-tests/{chip_name}"));
2038 let mut adapter = CoreDump::load(&coredump_path).unwrap();
2039
2040 let initial_registers = debug_registers(&adapter);
2041
2042 let snapshot_name = format!("{chip_name}_static_variables");
2043
2044 let mut static_variables = debug_info.create_static_scope_cache();
2045
2046 static_variables.recurse_deferred_variables(
2047 &debug_info,
2048 &mut adapter,
2049 10,
2050 StackFrameInfo {
2051 registers: &initial_registers,
2052 frame_base: None,
2053 canonical_frame_address: None,
2054 },
2055 );
2056 insta::assert_yaml_snapshot!(snapshot_name, static_variables);
2059 }
2060
2061 fn coredump_path(base: String) -> PathBuf {
2062 let possible_coredump_paths = [
2063 get_path_for_test_files(format!("{base}.coredump").as_str()),
2064 get_path_for_test_files(format!("{base}_coredump.elf").as_str()),
2065 ];
2066
2067 possible_coredump_paths
2068 .iter()
2069 .find(|path| path.exists())
2070 .unwrap_or_else(|| {
2071 panic!(
2072 "No coredump found for chip {base}. Expected one of: {:?}",
2073 possible_coredump_paths
2074 )
2075 })
2076 .clone()
2077 }
2078
2079 #[test]
2080 fn unwind_same_value() {
2081 let rule = gimli::RegisterRule::SameValue;
2082
2083 let mut callee_frame_registers = DebugRegisters::default();
2084 let debug_register = CORTEX_M_CORE_REGISTERS.core_registers().next().unwrap();
2085
2086 let expected_value = Some(RegisterValue::U32(0x1234));
2087
2088 callee_frame_registers.0.push(DebugRegister {
2089 core_register: debug_register,
2090 dwarf_id: Some(0),
2091 value: expected_value,
2092 });
2093
2094 let mut memory = MockMemory::new();
2095
2096 let value = unwind_register_using_rule(
2097 debug_register,
2098 &callee_frame_registers,
2099 None,
2100 &mut memory,
2101 rule,
2102 )
2103 .unwrap();
2104
2105 assert_eq!(value, expected_value);
2106 }
2107
2108 #[test]
2109 fn unwind_offset() {
2110 let cfa = 0x1000;
2111 let offset = 4;
2112 let rule = gimli::RegisterRule::Offset(offset as i64);
2113 let expected_value = 0xcafe;
2114
2115 let expected_register_value = Some(RegisterValue::U32(expected_value));
2116
2117 let mut memory = MockMemory::new();
2118 memory.add_word_range(cfa + offset, &[expected_value]);
2119
2120 let mut callee_frame_registers = DebugRegisters::default();
2121 let debug_register = CORTEX_M_CORE_REGISTERS.core_registers().next().unwrap();
2122
2123 callee_frame_registers.0.push(DebugRegister {
2124 core_register: debug_register,
2125 dwarf_id: Some(0),
2126 value: None,
2127 });
2128
2129 callee_frame_registers.0.push(DebugRegister {
2131 core_register: &cortex_m::PC,
2132 dwarf_id: Some(15),
2133 value: Some(RegisterValue::U32(0x0)),
2134 });
2135
2136 let value = unwind_register_using_rule(
2137 debug_register,
2138 &callee_frame_registers,
2139 Some(cfa),
2140 &mut memory,
2141 rule,
2142 )
2143 .unwrap();
2144
2145 assert_eq!(value, expected_register_value);
2146 }
2147
2148 #[test]
2149 fn unwind_undefined_for_frame_pointer() {
2150 let mut callee_frame_registers = DebugRegisters::default();
2151 callee_frame_registers.0.push(DebugRegister {
2152 core_register: &cortex_m::FP,
2153 dwarf_id: Some(7),
2154 value: Some(RegisterValue::U32(0x100)),
2155 });
2156
2157 callee_frame_registers.0.push(DebugRegister {
2159 core_register: &cortex_m::PC,
2160 dwarf_id: Some(15),
2161 value: Some(RegisterValue::U32(0x0)),
2162 });
2163
2164 let cfa = 0x200;
2165
2166 let mut memory = MockMemory::new();
2167
2168 let value = unwind_register_using_rule(
2169 &cortex_m::FP,
2170 &callee_frame_registers,
2171 Some(cfa),
2172 &mut memory,
2173 RegisterRule::Undefined,
2174 )
2175 .unwrap();
2176
2177 assert_eq!(value, Some(RegisterValue::U32(0x200)));
2180 }
2181}