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