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 read::RegisterRule,
13};
14use object::read::{Object, ObjectSection};
15use probe_rs::{CoreRegister, Error, InstructionSet, MemoryInterface, RegisterRole, RegisterValue};
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, &dwarf_cow));
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.unit_infos.iter().find(|unit_info| {
260 unit_info
261 .unit
262 .header
263 .offset()
264 .to_debug_info_offset(&unit_info.unit)
265 == Some(header_offset)
266 }) else {
267 return Err(DebugError::Other(
268 "Failed to find unit info for offset lookup.".to_string(),
269 ));
270 };
271
272 let mut type_tree = unit_info.unit.entries_tree(Some(unit_offset))?;
274 let parent_node = type_tree.root()?;
275
276 unit_info.process_tree(
277 self,
278 parent_node,
279 parent_variable,
280 memory,
281 cache,
282 frame_info,
283 )?;
284 }
285 VariableNodeType::UnitsLookup => {
286 if self.unit_infos.is_empty() {
287 return Err(DebugError::Other("Missing unit infos".to_string()));
289 }
290
291 for unit_info in self.unit_infos.iter() {
293 let mut entries = unit_info.unit.entries();
294
295 let unit_node = entries.next_dfs()?.unwrap();
298 let unit_offset = unit_node.offset();
299
300 let mut type_tree = unit_info.unit.entries_tree(Some(unit_offset))?;
301 let parent_node = type_tree.root()?;
302
303 unit_info.process_tree(
304 self,
305 parent_node,
306 parent_variable,
307 memory,
308 cache,
309 frame_info,
310 )?;
311 }
312 }
313 _ => {
314 }
316 }
317 Ok(())
318 }
319
320 fn get_stackframe_from_symbols(
322 &self,
323 address: u64,
324 unwind_registers: &DebugRegisters,
325 ) -> Result<Vec<StackFrame>, DebugError> {
326 let Some(ref addr2line) = self.addr2line else {
327 return Ok(vec![]);
328 };
329 let Some(fn_name) = addr2line.find_symbol(address) else {
330 return Ok(vec![]);
331 };
332
333 let mut fn_name = fn_name.to_string();
334 for lang in [
335 gimli::DW_LANG_Rust,
336 gimli::DW_LANG_C_plus_plus,
337 gimli::DW_LANG_C_plus_plus_03,
338 gimli::DW_LANG_C_plus_plus_11,
339 gimli::DW_LANG_C_plus_plus_14,
340 ] {
341 if let Some(demangle) = addr2line::demangle(&fn_name, lang) {
342 fn_name = demangle;
343 break;
344 }
345 }
346
347 Ok(vec![StackFrame {
348 id: get_object_reference(),
349 function_name: fn_name,
350 source_location: None,
351 registers: unwind_registers.clone(),
352 pc: unwind_registers.address_to_register_value(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 =
434 unwind_registers.address_to_register_value(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 #[expect(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: unwind_registers.address_to_register_value(address),
499 frame_base,
500 is_inlined: last_function.is_inline(),
501 local_variables,
502 canonical_frame_address: cfa,
503 });
504
505 Ok(frames)
506 }
507
508 pub fn unwind(
536 &self,
537 core: &mut impl MemoryInterface,
538 initial_registers: DebugRegisters,
539 exception_handler: &dyn ExceptionInterface,
540 instruction_set: Option<InstructionSet>,
541 max_stack_frame_count: usize,
542 ) -> Result<Vec<StackFrame>, Error> {
543 self.unwind_impl(
544 initial_registers,
545 core,
546 exception_handler,
547 instruction_set,
548 max_stack_frame_count,
549 )
550 }
551
552 pub(crate) fn unwind_impl(
553 &self,
554 initial_registers: DebugRegisters,
555 memory: &mut impl MemoryInterface,
556 exception_handler: &dyn ExceptionInterface,
557 instruction_set: Option<InstructionSet>,
558 max_stack_frame_count: usize,
559 ) -> Result<Vec<StackFrame>, Error> {
560 let mut stack_frames = Vec::<StackFrame>::new();
561
562 let mut unwind_context = Box::new(gimli::UnwindContext::new());
563
564 let mut unwind_registers = initial_registers;
565
566 'unwind: while let Some(frame_pc_register_value) =
568 unwind_registers.get_program_counter().and_then(|pc| {
569 if pc.is_zero() | pc.is_max_value() {
570 None
571 } else {
572 pc.value
573 }
574 })
575 {
576 if stack_frames.len() >= max_stack_frame_count {
577 tracing::warn!("Stopped unwinding the stack after {max_stack_frame_count} frames");
578 break;
579 }
580 let frame_pc = frame_pc_register_value.try_into().map_err(|error| {
581 let message = format!("Cannot convert register value for program counter to a 64-bit integer value: {error:?}");
582 Error::Register(message)
583 })?;
584
585 tracing::trace!(
589 "UNWIND: Will generate `StackFrame` for function at address (PC) {frame_pc_register_value:#}"
590 );
591 let unwind_info = get_unwind_info(&mut unwind_context, &self.frame_section, frame_pc);
592
593 let cfa = unwind_info
595 .as_ref()
596 .ok()
597 .and_then(|unwind_info| determine_cfa(&unwind_registers, unwind_info).ok())
598 .flatten();
599
600 let cached_stack_frames =
602 match self.get_stackframe_info(memory, frame_pc, cfa, &unwind_registers) {
603 Ok(cached_stack_frames) => cached_stack_frames,
604 Err(e) => {
605 tracing::error!("UNWIND: Unable to complete `StackFrame` information: {e}");
606 break;
608 }
609 };
610
611 if !cached_stack_frames.is_empty() {
615 for frame in cached_stack_frames.into_iter().rev() {
616 if frame.is_inlined {
617 tracing::trace!(
618 "UNWIND: Found inlined function - name={}, pc={}",
619 frame.function_name,
620 frame.pc
621 );
622 }
623 stack_frames.push(frame);
624 }
625 } else {
626 stack_frames.push(StackFrame {
629 id: get_object_reference(),
630 function_name: format!(
631 "<unknown function @ {:#0width$x}>",
632 frame_pc,
633 width = (unwind_registers.get_address_size_bytes() * 2 + 2)
634 ),
635 source_location: self.get_source_location(frame_pc),
636 registers: unwind_registers.clone(),
637 pc: frame_pc_register_value,
638 frame_base: None,
639 is_inlined: false,
640 local_variables: None,
641 canonical_frame_address: None,
642 });
643 };
644
645 tracing::trace!("UNWIND - Preparing to unwind the registers for the previous frame.");
647
648 let callee_frame_registers = unwind_registers.clone();
651
652 let unwind_info = match unwind_info {
655 Ok(unwind_info) => {
656 tracing::trace!("UNWIND: Found unwind info for address {frame_pc:#010x}");
657 unwind_info
658 }
659 Err(err) => {
660 tracing::trace!(
661 "UNWIND: Unable to find unwind info for address {frame_pc:#010x}: {err}"
662 );
663 if let ControlFlow::Break(error) = exception_handler.unwind_without_debuginfo(
664 &mut unwind_registers,
665 frame_pc,
666 &stack_frames,
667 instruction_set,
668 memory,
669 ) {
670 if let Some(error) = error {
671 tracing::error!("{:?}", &error);
673 if let Some(first_frame) = stack_frames.first_mut() {
674 first_frame.function_name =
675 format!("{} : ERROR : {error}", first_frame.function_name);
676 };
677 }
678 break 'unwind;
679 }
680
681 if unwind_registers
686 .get_return_address()
687 .is_some_and(|ra| ra.value.is_some())
688 {
689 match exception_handler.exception_details(memory, &unwind_registers, self) {
690 Ok(Some(exception_info)) => {
691 tracing::trace!(
692 "UNWIND: Stack unwind reached an exception handler {} (no debug info path)",
693 exception_info.description
694 );
695 unwind_registers = exception_info.handler_frame.registers.clone();
696 stack_frames.push(exception_info.handler_frame);
697 continue 'unwind;
698 }
699 Ok(None) => {}
700 Err(e) => {
701 tracing::warn!(
702 "UNWIND: Error checking exception context (no debug info path): {e:?}"
703 );
704 }
705 }
706 }
707
708 if callee_frame_registers == unwind_registers {
709 tracing::debug!("No change, preventing infinite loop");
710 break;
711 }
712 continue 'unwind;
713 }
714 };
715
716 for debug_register in unwind_registers.0.iter_mut() {
718 if debug_register
720 .core_register
721 .register_has_role(RegisterRole::ProgramCounter)
722 {
723 continue;
724 }
725
726 match unwind_register(
727 debug_register,
728 &callee_frame_registers,
729 unwind_info,
730 cfa,
731 memory,
732 exception_handler,
733 ) {
734 Err(error) => {
735 tracing::error!("{:?}", &error);
736 if let Some(first_frame) = stack_frames.last_mut() {
737 first_frame.function_name =
738 format!("{} : ERROR: {error}", first_frame.function_name);
739 };
740 break 'unwind;
741 }
742 Ok(val) => {
743 debug_register.value = val;
744 }
745 };
746 }
747
748 if unwind_registers
756 .get_return_address()
757 .is_some_and(|ra| ra.value.is_some())
758 {
759 match exception_handler.exception_details(memory, &unwind_registers, self) {
760 Ok(Some(exception_info)) => {
761 tracing::trace!(
762 "UNWIND: Stack unwind reached an exception handler {}",
763 exception_info.description
764 );
765 unwind_registers = exception_info.handler_frame.registers.clone();
766 stack_frames.push(exception_info.handler_frame);
767 continue 'unwind;
769 }
770 Ok(None) => {
771 tracing::trace!(
772 "UNWIND: No exception context found. Stack unwind will continue."
773 );
774 }
775 Err(e) => {
776 let message = format!(
778 "UNWIND: Error while checking for exception context. The stack trace will not include the calling frames.\n{e:?}"
779 );
780 tracing::warn!("{message}");
781 stack_frames.push(StackFrame {
782 id: get_object_reference(),
783 function_name: message,
784 source_location: None,
785 registers: unwind_registers.clone(),
786 pc: frame_pc_register_value,
787 frame_base: None,
788 is_inlined: false,
789 local_variables: None,
790 canonical_frame_address: None,
791 });
792 break 'unwind;
793 }
794 };
795 }
796
797 let unwound_return_address = unwind_registers
798 .get_register_by_role(&RegisterRole::ReturnAddress)
799 .ok()
800 .and_then(|reg| reg.value);
801
802 let program_counter = unwind_registers.get_program_counter_mut().unwrap();
803
804 let Ok(current_pc) =
805 callee_frame_registers.get_register_value_by_role(&RegisterRole::ProgramCounter)
806 else {
807 let error = "UNWIND: Tried to unwind return address value where current program counter is unknown.";
808 tracing::error!("{error}");
809 if let Some(first_frame) = stack_frames.last_mut() {
810 first_frame.function_name =
811 format!("{} : ERROR: {error}", first_frame.function_name);
812 };
813 break 'unwind;
814 };
815 program_counter.value = unwound_return_address.and_then(|return_address| {
818 unwind_program_counter_register(return_address, current_pc, instruction_set)
819 });
820
821 if callee_frame_registers == unwind_registers {
822 tracing::debug!("No change, preventing infinite loop");
823 break;
824 }
825 }
826
827 Ok(stack_frames)
828 }
829
830 #[tracing::instrument(skip_all)]
834 pub fn get_breakpoint_location(
835 &self,
836 path: TypedPath,
837 line: u64,
838 column: Option<u64>,
839 ) -> Result<VerifiedBreakpoint, DebugError> {
840 tracing::debug!(
841 "Looking for breakpoint location for {}:{}:{}",
842 path.display(),
843 line,
844 column
845 .map(|c| c.to_string())
846 .unwrap_or_else(|| "-".to_owned())
847 );
848 VerifiedBreakpoint::for_source_location(self, path, line, column)
849 }
850
851 pub(crate) fn get_path(
854 &self,
855 unit: &gimli::read::Unit<DwarfReader>,
856 file_index: u64,
857 ) -> Option<TypedPathBuf> {
858 let line_program = unit.line_program.as_ref()?;
859 let header = line_program.header();
860 let Some(file_entry) = header.file(file_index) else {
861 tracing::warn!(
862 "Unable to extract file entry for file_index {:?}.",
863 file_index
864 );
865 return None;
866 };
867 let file_name_attr_string = self.dwarf.attr_string(unit, file_entry.path_name()).ok()?;
868 let name_path = from_utf8(&file_name_attr_string).ok()?;
869
870 let dir_name_attr_string = file_entry
871 .directory(header)
872 .and_then(|dir| self.dwarf.attr_string(unit, dir).ok());
873
874 let dir_path = dir_name_attr_string.and_then(|dir_name| {
875 from_utf8(&dir_name)
876 .ok()
877 .map(|p| TypedPath::derive(p).to_path_buf())
878 });
879
880 let mut combined_path = match dir_path {
881 Some(dir_path) => dir_path.join(name_path),
882 None => TypedPath::derive(name_path).to_path_buf(),
883 };
884
885 if combined_path.is_relative() {
886 let comp_dir = unit
887 .comp_dir
888 .as_ref()
889 .map(|dir| from_utf8(dir))
890 .transpose()
891 .ok()?
892 .map(TypedPath::derive);
893 if let Some(comp_dir) = comp_dir {
894 combined_path = comp_dir.join(&combined_path);
895 }
896 }
897
898 Some(combined_path)
899 }
900
901 pub(crate) fn find_file_and_directory(
902 &self,
903 unit: &gimli::read::Unit<DwarfReader>,
904 file_index: u64,
905 ) -> Option<TypedPathBuf> {
906 let combined_path = self.get_path(unit, file_index)?;
907
908 Some(combined_path)
909 }
910
911 pub(crate) fn compile_unit_info(
913 &self,
914 address: u64,
915 ) -> Result<&super::unit_info::UnitInfo, DebugError> {
916 for header in &self.unit_infos {
917 match self.dwarf.unit_ranges(&header.unit) {
918 Ok(mut ranges) => {
919 while let Ok(Some(range)) = ranges.next() {
920 if range.contains(address) {
921 return Ok(header);
922 }
923 }
924 }
925 Err(_) => continue,
926 };
927 }
928 Err(DebugError::WarnAndContinue {
929 message: format!(
930 "No debug information available for the instruction at {address:#010x}. Please consider using instruction level stepping."
931 ),
932 })
933 }
934
935 pub(crate) fn get_function_dies(
940 &self,
941 address: u64,
942 ) -> Result<(&UnitInfo, Vec<FunctionDie<'_>>), DebugError> {
943 for unit_info in &self.unit_infos {
944 let function_dies = unit_info.get_function_dies(self, address)?;
945
946 if !function_dies.is_empty() {
947 return Ok((unit_info, function_dies));
948 }
949 }
950 Err(DebugError::Other(format!(
951 "No function DIE's at address {address:#x}."
952 )))
953 }
954
955 pub(crate) fn resolve_die_reference<'debug_info, 'unit_info>(
957 &'debug_info self,
958 attribute: gimli::DwAt,
959 die: &Die,
960 unit_info: &'unit_info UnitInfo,
961 ) -> Option<Die>
962 where
963 'unit_info: 'debug_info,
964 {
965 self.resolve_die_reference_with_unit_info(attribute, die, unit_info)
966 .map(|(_, die)| die)
967 }
968
969 pub(crate) fn resolve_die_reference_with_unit_info<'debug_info, 'unit_info>(
977 &'debug_info self,
978 attribute: gimli::DwAt,
979 die: &Die,
980 unit_info: &'unit_info UnitInfo,
981 ) -> Option<(&'debug_info UnitInfo, Die)>
982 where
983 'unit_info: 'debug_info,
984 {
985 let attr = die.attr(attribute)?;
986
987 self.resolve_die_reference_with_unit(attr, unit_info).ok()
988 }
989
990 pub fn endianness(&self) -> RunTimeEndian {
992 self.endianness
993 }
994
995 pub(crate) fn resolve_die_reference_with_unit<'debug_info, 'unit_info>(
997 &'debug_info self,
998 attr: &gimli::Attribute<GimliReader>,
999 unit_info: &'unit_info UnitInfo,
1000 ) -> Result<(&'debug_info UnitInfo, Die), DebugError>
1001 where
1002 'unit_info: 'debug_info,
1003 {
1004 match attr.value() {
1005 gimli::AttributeValue::UnitRef(unit_ref) => {
1006 Ok((unit_info, unit_info.unit.entry(unit_ref)?))
1007 }
1008 gimli::AttributeValue::DebugInfoRef(offset) => {
1009 for unit_info in &self.unit_infos {
1010 let Some(unit_offset) = offset.to_unit_offset(&unit_info.unit.header) else {
1011 continue;
1012 };
1013
1014 let entry = unit_info.unit.entry(unit_offset).map_err(|error| {
1015 DebugError::Other(format!(
1016 "Error reading DIE at debug info offset {:#x} : {}",
1017 offset.0, error
1018 ))
1019 })?;
1020 return Ok((unit_info, entry));
1021 }
1022
1023 Err(DebugError::Other(format!(
1024 "Unable to find unit info for debug info offset {:#x}",
1025 offset.0
1026 )))
1027 }
1028 other_attribute_value => Err(DebugError::Other(format!(
1029 "Unimplemented attribute value {other_attribute_value:?}"
1030 ))),
1031 }
1032 }
1033}
1034
1035pub(crate) fn canonical_path_eq(primary_path: TypedPath, secondary_path: TypedPath) -> bool {
1037 primary_path.normalize() == secondary_path.normalize()
1038}
1039
1040pub(crate) fn path_matches(full_path: TypedPath, partial_path: TypedPath) -> bool {
1050 if canonical_path_eq(full_path, partial_path) {
1051 return true;
1052 }
1053 if partial_path.is_relative() {
1054 let full_buf = full_path.normalize();
1055 let full_str = full_buf.to_string_lossy().replace('\\', "/");
1056 let partial_buf = partial_path.normalize();
1057 let partial_str = partial_buf.to_string_lossy().replace('\\', "/");
1058 full_str.ends_with(&*partial_str)
1059 && (full_str.len() == partial_str.len()
1060 || full_str[..full_str.len() - partial_str.len()].ends_with('/'))
1061 } else {
1062 false
1063 }
1064}
1065
1066pub fn get_unwind_info<'a>(
1068 unwind_context: &'a mut UnwindContext<GimliReaderOffset>,
1069 frame_section: &DebugFrame<DwarfReader>,
1070 frame_program_counter: u64,
1071) -> Result<&'a gimli::UnwindTableRow<GimliReaderOffset>, DebugError> {
1072 let transform_error = |error| {
1073 DebugError::Other(format!(
1074 "UNWIND: Error reading FrameDescriptorEntry at PC={frame_program_counter:x} : {error}"
1075 ))
1076 };
1077
1078 let unwind_bases = BaseAddresses::default();
1079
1080 let frame_descriptor_entry = frame_section
1081 .fde_for_address(
1082 &unwind_bases,
1083 frame_program_counter,
1084 DebugFrame::cie_from_offset,
1085 )
1086 .map_err(transform_error)?;
1087
1088 frame_descriptor_entry
1089 .unwind_info_for_address(
1090 frame_section,
1091 &unwind_bases,
1092 unwind_context,
1093 frame_program_counter,
1094 )
1095 .map_err(transform_error)
1096}
1097
1098pub fn determine_cfa<R: gimli::ReaderOffset>(
1100 unwind_registers: &DebugRegisters,
1101 unwind_info: &UnwindTableRow<R>,
1102) -> Result<Option<u64>, Error> {
1103 let gimli::CfaRule::RegisterAndOffset { register, offset } = unwind_info.cfa() else {
1104 unimplemented!()
1105 };
1106
1107 let reg_val = unwind_registers
1108 .get_register_by_dwarf_id(register.0)
1109 .and_then(|register| register.value);
1110
1111 let cfa = match reg_val {
1112 None => {
1113 tracing::error!(
1114 "UNWIND: `StackFrameIterator` unable to determine the unwind CFA: Missing value of register {}",
1115 register.0
1116 );
1117 None
1118 }
1119
1120 Some(reg_val) if reg_val.is_zero() => {
1121 tracing::trace!(
1124 "UNWIND: Stack unwind complete - The FP register value unwound to a value of zero."
1125 );
1126 None
1127 }
1128
1129 Some(reg_val) => {
1130 let unwind_cfa = add_to_address(
1131 reg_val.try_into()?,
1132 *offset,
1133 unwind_registers.get_address_size_bytes(),
1134 );
1135 tracing::trace!(
1136 "UNWIND - CFA : {:#010x}\tRule: {:?}",
1137 unwind_cfa,
1138 unwind_info.cfa()
1139 );
1140 Some(unwind_cfa)
1141 }
1142 };
1143
1144 Ok(cfa)
1145}
1146
1147pub fn unwind_pc_without_debuginfo(
1149 unwind_registers: &mut DebugRegisters,
1150 _frame_pc: u64,
1151 instruction_set: Option<InstructionSet>,
1152) -> ControlFlow<Option<DebugError>> {
1153 let callee_frame_registers = unwind_registers.clone();
1160 let unwound_return_address: Option<RegisterValue> = unwind_registers
1161 .get_return_address()
1162 .and_then(|lr| lr.value);
1163
1164 if let Some(calling_pc) = unwind_registers.get_program_counter_mut() {
1166 let Ok(current_pc) =
1167 callee_frame_registers.get_register_value_by_role(&RegisterRole::ProgramCounter)
1168 else {
1169 return ControlFlow::Break(
1170 Some(Error::Other(
1171 "UNWIND: Tried to unwind return address value where current program counter is unknown.".to_string()
1172 ).into())
1173 );
1174 };
1175 calling_pc.value = unwound_return_address.and_then(|return_address| {
1178 unwind_program_counter_register(return_address, current_pc, instruction_set)
1179 })
1180 }
1181
1182 ControlFlow::Continue(())
1183}
1184
1185pub fn unwind_register(
1187 debug_register: &super::DebugRegister,
1188 callee_frame_registers: &DebugRegisters,
1190 unwind_info: &gimli::UnwindTableRow<GimliReaderOffset>,
1191 unwind_cfa: Option<u64>,
1192 memory: &mut dyn MemoryInterface,
1193 exception_handler: &dyn ExceptionInterface,
1194) -> Result<Option<RegisterValue>, Error> {
1195 let register_rule = debug_register
1197 .dwarf_id
1198 .and_then(|register_position| unwind_info.register(gimli::Register(register_position)))
1199 .unwrap_or(RegisterRule::Undefined);
1200
1201 unwind_register_using_rule(
1202 debug_register.core_register,
1203 callee_frame_registers,
1204 unwind_cfa,
1205 memory,
1206 register_rule,
1207 exception_handler,
1208 )
1209}
1210
1211fn unwind_register_using_rule(
1212 debug_register: &CoreRegister,
1213 callee_frame_registers: &DebugRegisters,
1214 unwind_cfa: Option<u64>,
1215 memory: &mut dyn MemoryInterface,
1216 register_rule: gimli::RegisterRule<usize>,
1217 exception_handler: &dyn ExceptionInterface,
1218) -> Result<Option<RegisterValue>, Error> {
1219 use gimli::read::RegisterRule;
1220
1221 let mut register_rule_string = format!("{register_rule:?}");
1222
1223 let new_value = match register_rule {
1224 RegisterRule::Undefined => exception_handler
1225 .unwind_undefined_register(
1226 debug_register,
1227 callee_frame_registers,
1228 unwind_cfa,
1229 memory,
1230 &mut register_rule_string,
1231 )
1232 .map_err(|e| match e {
1233 crate::DebugError::Probe(err) => err,
1234 other => Error::Other(other.to_string()),
1235 })?,
1236
1237 RegisterRule::SameValue => callee_frame_registers
1238 .get_register(debug_register.id)
1239 .and_then(|reg| reg.value),
1240
1241 RegisterRule::Offset(address_offset) => {
1242 let Some(unwind_cfa) = unwind_cfa else {
1244 return Err(Error::Other(
1245 "UNWIND: Tried to unwind `RegisterRule` at CFA = None.".to_string(),
1246 ));
1247 };
1248 let address_size = callee_frame_registers.get_address_size_bytes();
1249 let previous_frame_register_address =
1250 add_to_address(unwind_cfa, address_offset, address_size);
1251
1252 register_rule_string = format!("CFA {register_rule:?}");
1253
1254 let result = match address_size {
1256 4 => {
1257 let mut buff = [0u8; 4];
1258 memory
1259 .read(previous_frame_register_address, &mut buff)
1260 .map(|_| RegisterValue::U32(u32::from_le_bytes(buff)))
1261 }
1262 8 => {
1263 let mut buff = [0u8; 8];
1264 memory
1265 .read(previous_frame_register_address, &mut buff)
1266 .map(|_| RegisterValue::U64(u64::from_le_bytes(buff)))
1267 }
1268 _ => {
1269 return Err(Error::Other(format!(
1270 "UNWIND: Address size {address_size} not supported."
1271 )));
1272 }
1273 };
1274
1275 match result {
1276 Ok(register_value) => Some(register_value),
1277 Err(error) => {
1278 tracing::error!(
1279 "UNWIND: Rule: Offset {} from address {:#010x}",
1280 address_offset,
1281 unwind_cfa
1282 );
1283
1284 return Err(Error::Other(format!(
1285 "UNWIND: Failed to read value for register {} from address {} ({} bytes): {}",
1286 debug_register,
1287 RegisterValue::from(previous_frame_register_address),
1288 4,
1289 error
1290 )));
1291 }
1292 }
1293 }
1294 _ => unimplemented!(),
1296 };
1297
1298 tracing::trace!(
1299 "UNWIND - {:>10}: Caller: {}\tCallee: {}\tRule: {}",
1300 debug_register,
1301 new_value.unwrap_or_default(),
1302 callee_frame_registers
1303 .get_register(debug_register.id)
1304 .and_then(|reg| reg.value)
1305 .unwrap_or_default(),
1306 register_rule_string,
1307 );
1308 Ok(new_value)
1309}
1310
1311pub fn unwind_program_counter_register(
1313 return_address: RegisterValue,
1314 current_pc: u64,
1315 instruction_set: Option<InstructionSet>,
1316) -> Option<RegisterValue> {
1317 if return_address.is_max_value() || return_address.is_zero() {
1318 tracing::debug!(
1319 "No reliable return address is available, so we cannot determine the program counter to unwind the previous frame."
1320 );
1321 return None;
1322 }
1323
1324 const DEFAULT_REGISTER_RULE_STR: &str = "PC=(unwound LR) (dwarf Undefined)";
1325
1326 let (caller_pc, rule_str) = match return_address {
1327 RegisterValue::U32(return_address) => {
1328 match instruction_set {
1329 Some(InstructionSet::Thumb2) => {
1330 (
1335 Some(RegisterValue::U32((return_address - 2) & !0b1)),
1336 "PC=(unwound (LR - 2) & !0b1) (dwarf Undefined)",
1337 )
1338 }
1339 Some(InstructionSet::RV32C) => (
1340 Some(RegisterValue::U32(return_address - 2)),
1341 "PC=(unwound x1 - 2) (dwarf Undefined)",
1342 ),
1343 Some(InstructionSet::RV32) => (
1344 Some(RegisterValue::U32(return_address - 4)),
1345 "PC=(unwound x1 - 4) (dwarf Undefined)",
1346 ),
1347 Some(InstructionSet::Xtensa) => {
1348 let upper_bits = (current_pc as u32) & 0xC000_0000;
1349 (
1350 Some(RegisterValue::U32(
1351 (return_address & 0x3FFF_FFFF | upper_bits) - 3,
1352 )),
1353 "PC=(unwound x0 - 3) (dwarf Undefined)",
1354 )
1355 }
1356 _ => (
1357 Some(RegisterValue::U32(return_address)),
1358 DEFAULT_REGISTER_RULE_STR,
1359 ),
1360 }
1361 }
1362 RegisterValue::U64(return_address) => (
1363 Some(RegisterValue::U64(return_address)),
1364 DEFAULT_REGISTER_RULE_STR,
1365 ),
1366 RegisterValue::U128(_) => {
1367 tracing::warn!("128 bit address space not supported");
1368 (None, "PC=(undefined) (dwarf Undefined)")
1369 }
1370 };
1371
1372 tracing::trace!(
1373 "UNWIND - PC: Caller: {}\tCallee: {:#010x}\tRule: {}",
1374 caller_pc.unwrap_or_default(),
1375 current_pc,
1376 rule_str,
1377 );
1378
1379 caller_pc
1380}
1381
1382fn add_to_address(address: u64, offset: i64, address_size_in_bytes: usize) -> u64 {
1390 match address_size_in_bytes {
1391 4 => {
1392 if offset >= 0 {
1393 (address as u32)
1394 .checked_add(offset as u32)
1395 .map(u64::from)
1396 .unwrap_or(0x0)
1397 } else {
1398 (address as u32).saturating_sub(offset.unsigned_abs() as u32) as u64
1399 }
1400 }
1401 8 => {
1402 if offset >= 0 {
1403 address.checked_add(offset as u64).unwrap_or(0x0)
1404 } else {
1405 address.saturating_sub(offset.unsigned_abs())
1406 }
1407 }
1408 _ => {
1409 panic!(
1410 "UNWIND: Address size {address_size_in_bytes} not supported. Please report this as a bug."
1411 );
1412 }
1413 }
1414}
1415
1416#[cfg(test)]
1417mod test {
1418 use crate::{
1419 DebugInfo, DebugRegister, DebugRegisters,
1420 exception_handling::{
1421 armv6m::ArmV6MExceptionHandler, armv7m::ArmV7MExceptionHandler,
1422 exception_handler_for_core,
1423 },
1424 stack_frame::{StackFrameInfo, TestFormatter},
1425 };
1426
1427 use gimli::RegisterRule;
1428 use probe_rs::{
1429 CoreDump, RegisterValue,
1430 architecture::arm::core::registers::cortex_m::{self, CORTEX_M_CORE_REGISTERS},
1431 test::MockMemory,
1432 };
1433 use std::path::{Path, PathBuf};
1434 use test_case::test_case;
1435
1436 use super::unwind_register_using_rule;
1437
1438 fn get_path_for_test_files(relative_file: &str) -> PathBuf {
1440 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1441 path.push("tests");
1442 path.push(relative_file);
1443 path
1444 }
1445
1446 fn load_test_elf_as_debug_info(elf_file: &str) -> DebugInfo {
1449 let path = get_path_for_test_files(elf_file);
1450 DebugInfo::from_file(&path).unwrap_or_else(|err: crate::DebugError| {
1451 panic!("Failed to open file {}: {:?}", path.display(), err)
1452 })
1453 }
1454
1455 #[test]
1456 fn unwinding_first_instruction_after_exception() {
1457 let debug_info = load_test_elf_as_debug_info("exceptions");
1458
1459 let values: Vec<_> = [
1483 0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000, 0x00000000, 0x00000000, 0x2001fff0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2001ffd0, 0xfffffff9, 0x00000182, 0x2001ffd0, 0x00000000, 0x2100000b, ]
1503 .into_iter()
1504 .enumerate()
1505 .map(|(id, r)| DebugRegister {
1506 dwarf_id: Some(id as u16),
1507 core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1508 value: Some(RegisterValue::U32(r)),
1509 })
1510 .collect();
1511
1512 let regs = DebugRegisters(values);
1513
1514 let expected_regs = regs.clone();
1515
1516 let mut mocked_mem = MockMemory::new();
1517
1518 mocked_mem.add_word_range(
1533 0x2001_ffd0,
1534 &[
1535 0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000, 0x0000017f, 0x00000180,
1536 0x21000000, 0x2001fff8, 0x00000161, 0x00000000, 0x0000013d,
1537 ],
1538 );
1539
1540 let exception_handler = Box::new(ArmV6MExceptionHandler {});
1541
1542 let frames = debug_info
1543 .unwind_impl(
1544 regs,
1545 &mut mocked_mem,
1546 exception_handler.as_ref(),
1547 Some(probe_rs_target::InstructionSet::Thumb2),
1548 500,
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 500,
1702 )
1703 .unwrap();
1704
1705 assert_eq!(frames[0].pc, RegisterValue::U32(0x000001a4));
1706
1707 assert_eq!(
1708 frames[1].function_name,
1709 "__cortex_m_rt_SVCall_trampoline".to_string()
1710 );
1711
1712 assert_eq!(frames[1].pc, RegisterValue::U32(0x00000188)); assert_eq!(
1720 frames[1]
1721 .registers
1722 .get_register(probe_rs::RegisterId(7))
1723 .and_then(|r| r.value),
1724 Some(RegisterValue::U32(0x2001ffc8))
1725 );
1726
1727 let printed_backtrace = frames
1728 .into_iter()
1729 .map(|f| TestFormatter(&f).to_string())
1730 .collect::<Vec<String>>()
1731 .join("");
1732
1733 insta::assert_snapshot!(printed_backtrace);
1734 }
1735
1736 #[test]
1737 fn unwinding_in_exception_trampoline() {
1738 let debug_info = load_test_elf_as_debug_info("exceptions");
1739
1740 let values: Vec<_> = [
1763 0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000, 0x00000000, 0x00000000, 0x2001ffc8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2001ffc8, 0x0000018B, 0x0000018A, 0x2001ffc8, 0x00000000, 0x2100000b, ]
1783 .into_iter()
1784 .enumerate()
1785 .map(|(id, r)| DebugRegister {
1786 dwarf_id: Some(id as u16),
1787 core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1788 value: Some(RegisterValue::U32(r)),
1789 })
1790 .collect();
1791
1792 let regs = DebugRegisters(values);
1793
1794 let mut dummy_mem = MockMemory::new();
1795
1796 dummy_mem.add_word_range(
1813 0x2001_ffc8,
1814 &[
1815 0x2001fff0, 0xfffffff9, 0x00000001, 0x2001ffcf, 0x20000044, 0x20000044, 0x00000000,
1816 0x0000017f, 0x00000180, 0x21000000, 0x2001fff8, 0x00000161, 0x00000000, 0x0000013d,
1817 ],
1818 );
1819
1820 let exception_handler = Box::new(ArmV6MExceptionHandler {});
1821
1822 let frames = debug_info
1823 .unwind_impl(
1824 regs,
1825 &mut dummy_mem,
1826 exception_handler.as_ref(),
1827 Some(probe_rs_target::InstructionSet::Thumb2),
1828 500,
1829 )
1830 .unwrap();
1831
1832 let printed_backtrace = frames
1833 .into_iter()
1834 .map(|f| TestFormatter(&f).to_string())
1835 .collect::<Vec<String>>()
1836 .join("");
1837
1838 insta::assert_snapshot!(printed_backtrace);
1839 }
1840
1841 #[test]
1842 fn unwinding_inlined() {
1843 let debug_info = load_test_elf_as_debug_info("inlined-functions");
1844
1845 let values: Vec<_> = [
1869 0xfffffecc, 0x00000001, 0x00000000, 0x40008140, 0x000f4240, 0xfffffec0, 0x00000000, 0x20003ff0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5000050c, 0x20003ff0, 0x00200000, 0x000002e4, 0x20003ff0, 0x00000000, 0x61000000, ]
1889 .into_iter()
1890 .enumerate()
1891 .map(|(id, r)| DebugRegister {
1892 dwarf_id: Some(id as u16),
1893 core_register: CORTEX_M_CORE_REGISTERS.core_register(id),
1894 value: Some(RegisterValue::U32(r)),
1895 })
1896 .collect();
1897
1898 let regs = DebugRegisters(values);
1899
1900 let mut dummy_mem = MockMemory::new();
1901
1902 dummy_mem.add_word_range(
1909 0x2000_3ff0,
1910 &[0x20003ff8, 0x00000161, 0x00000000, 0x0000013d],
1911 );
1912
1913 let exception_handler = Box::new(ArmV7MExceptionHandler);
1914
1915 let frames = debug_info
1916 .unwind_impl(
1917 regs,
1918 &mut dummy_mem,
1919 exception_handler.as_ref(),
1920 Some(probe_rs_target::InstructionSet::Thumb2),
1921 500,
1922 )
1923 .unwrap();
1924
1925 let printed_backtrace = frames
1926 .into_iter()
1927 .map(|f| TestFormatter(&f).to_string())
1928 .collect::<Vec<String>>()
1929 .join("");
1930
1931 insta::assert_snapshot!(printed_backtrace);
1932 }
1933
1934 #[test]
1935 fn test_print_stacktrace() {
1936 let elf = Path::new("./tests/gpio-hal-blinky/elf");
1937 let coredump = include_bytes!("../tests/gpio-hal-blinky/coredump");
1938
1939 let mut adapter = CoreDump::load_raw(coredump).unwrap();
1940 let debug_info = DebugInfo::from_file(elf).unwrap();
1941
1942 let initial_registers = DebugRegisters::from_coredump(&adapter);
1943 let exception_handler = exception_handler_for_core(adapter.core_type());
1944 let instruction_set = adapter.instruction_set();
1945
1946 let stack_frames = debug_info
1947 .unwind(
1948 &mut adapter,
1949 initial_registers,
1950 exception_handler.as_ref(),
1951 Some(instruction_set),
1952 1000,
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("stm32u585_nested_exceptions"; "nested exceptions Armv8-m using STM32U585")]
1975 #[test_case("stm32u585_hardfault_fp"; "hardfault frame pointer Armv8-m using STM32U585")]
1976 #[test_case("stm32u585_exception_no_debuginfo"; "exception handler without unwind info Armv8-m using STM32U585")]
1977 #[test_case("stm32u585_psp_exception"; "exception through a PSP->MSP transition Armv8-m using STM32U585")]
1978 #[test_case("atsamd51p19a"; "Armv7-em from C source code")]
1979 #[test_case("esp32c3_full_unwind"; "full_unwind RISC-V32E using esp32c3")]
1980 #[test_case("esp32s3_esp_hal_panic"; "Xtensa unwinding on an esp32s3 in a panic handler")]
1981 #[test_case("esp32c6_coredump_elf"; "Unwind using a RISC-V coredump in ELF format")]
1982 #[test_case("esp32s3_coredump_elf"; "Unwind using an Xtensa coredump in ELF format")]
1983 fn full_unwind(test_name: &str) {
1984 let debug_info =
1985 load_test_elf_as_debug_info(format!("debug-unwind-tests/{test_name}.elf").as_str());
1986
1987 let coredump_path = coredump_path(format!("debug-unwind-tests/{test_name}"));
1988 let mut adapter = CoreDump::load(&coredump_path).unwrap();
1989
1990 let snapshot_name = test_name.to_string();
1991
1992 let initial_registers = DebugRegisters::from_coredump(&adapter);
1993 let exception_handler = exception_handler_for_core(adapter.core_type());
1994 let instruction_set = adapter.instruction_set();
1995
1996 let mut stack_frames = debug_info
1997 .unwind(
1998 &mut adapter,
1999 initial_registers,
2000 exception_handler.as_ref(),
2001 Some(instruction_set),
2002 1000,
2003 )
2004 .unwrap();
2005
2006 for frame in stack_frames.iter_mut() {
2008 let mut variable_caches = Vec::new();
2009 if let Some(local_variables) = &mut frame.local_variables {
2010 variable_caches.push(local_variables);
2011 }
2012 for variable_cache in variable_caches {
2013 variable_cache.recurse_deferred_variables(
2015 &debug_info,
2016 &mut adapter,
2017 10,
2018 StackFrameInfo {
2019 registers: &frame.registers,
2020 frame_base: frame.frame_base,
2021 canonical_frame_address: frame.canonical_frame_address,
2022 },
2023 );
2024 }
2025 }
2026
2027 insta::assert_yaml_snapshot!(snapshot_name, stack_frames);
2030 }
2031
2032 #[test_case("RP2040_full_unwind"; "Armv6-m using RP2040")]
2033 #[test_case("nRF52833_xxAA_full_unwind"; "Armv7-m using nRF52833_xxAA")]
2034 #[test_case("atsamd51p19a"; "Armv7-em from C source code")]
2035 fn static_variables(chip_name: &str) {
2037 let debug_info =
2040 load_test_elf_as_debug_info(format!("debug-unwind-tests/{chip_name}.elf").as_str());
2041
2042 let coredump_path = coredump_path(format!("debug-unwind-tests/{chip_name}"));
2043 let mut adapter = CoreDump::load(&coredump_path).unwrap();
2044
2045 let initial_registers = DebugRegisters::from_coredump(&adapter);
2046
2047 let snapshot_name = format!("{chip_name}_static_variables");
2048
2049 let mut static_variables = debug_info.create_static_scope_cache();
2050
2051 static_variables.recurse_deferred_variables(
2052 &debug_info,
2053 &mut adapter,
2054 10,
2055 StackFrameInfo {
2056 registers: &initial_registers,
2057 frame_base: None,
2058 canonical_frame_address: None,
2059 },
2060 );
2061 insta::assert_yaml_snapshot!(snapshot_name, static_variables);
2064 }
2065
2066 fn coredump_path(base: String) -> PathBuf {
2067 let possible_coredump_paths = [
2068 get_path_for_test_files(format!("{base}.coredump").as_str()),
2069 get_path_for_test_files(format!("{base}_coredump.elf").as_str()),
2070 ];
2071
2072 possible_coredump_paths
2073 .iter()
2074 .find(|path| path.exists())
2075 .unwrap_or_else(|| {
2076 panic!(
2077 "No coredump found for chip {base}. Expected one of: {possible_coredump_paths:?}"
2078 )
2079 })
2080 .clone()
2081 }
2082
2083 #[test]
2084 fn unwind_same_value() {
2085 let rule = gimli::RegisterRule::SameValue;
2086
2087 let mut callee_frame_registers = DebugRegisters::default();
2088 let debug_register = CORTEX_M_CORE_REGISTERS.core_registers().next().unwrap();
2089
2090 let expected_value = Some(RegisterValue::U32(0x1234));
2091
2092 callee_frame_registers.0.push(DebugRegister {
2093 core_register: debug_register,
2094 dwarf_id: Some(0),
2095 value: expected_value,
2096 });
2097
2098 let mut memory = MockMemory::new();
2099
2100 let value = unwind_register_using_rule(
2101 debug_register,
2102 &callee_frame_registers,
2103 None,
2104 &mut memory,
2105 rule,
2106 &ArmV7MExceptionHandler,
2107 )
2108 .unwrap();
2109
2110 assert_eq!(value, expected_value);
2111 }
2112
2113 #[test]
2114 fn unwind_offset() {
2115 let cfa = 0x1000;
2116 let offset = 4;
2117 let rule = gimli::RegisterRule::Offset(offset as i64);
2118 let expected_value = 0xcafe;
2119
2120 let expected_register_value = Some(RegisterValue::U32(expected_value));
2121
2122 let mut memory = MockMemory::new();
2123 memory.add_word_range(cfa + offset, &[expected_value]);
2124
2125 let mut callee_frame_registers = DebugRegisters::default();
2126 let debug_register = CORTEX_M_CORE_REGISTERS.core_registers().next().unwrap();
2127
2128 callee_frame_registers.0.push(DebugRegister {
2129 core_register: debug_register,
2130 dwarf_id: Some(0),
2131 value: None,
2132 });
2133
2134 callee_frame_registers.0.push(DebugRegister {
2136 core_register: &cortex_m::PC,
2137 dwarf_id: Some(15),
2138 value: Some(RegisterValue::U32(0x0)),
2139 });
2140
2141 let value = unwind_register_using_rule(
2142 debug_register,
2143 &callee_frame_registers,
2144 Some(cfa),
2145 &mut memory,
2146 rule,
2147 &ArmV7MExceptionHandler,
2148 )
2149 .unwrap();
2150
2151 assert_eq!(value, expected_register_value);
2152 }
2153
2154 #[test]
2155 fn unwind_undefined_for_frame_pointer() {
2156 let mut callee_frame_registers = DebugRegisters::default();
2157 callee_frame_registers.0.push(DebugRegister {
2158 core_register: &cortex_m::FP,
2159 dwarf_id: Some(7),
2160 value: Some(RegisterValue::U32(0x100)),
2161 });
2162
2163 callee_frame_registers.0.push(DebugRegister {
2165 core_register: &cortex_m::PC,
2166 dwarf_id: Some(15),
2167 value: Some(RegisterValue::U32(0x0)),
2168 });
2169
2170 let cfa = 0x200;
2171
2172 let mut memory = MockMemory::new();
2173
2174 let value = unwind_register_using_rule(
2175 &cortex_m::FP,
2176 &callee_frame_registers,
2177 Some(cfa),
2178 &mut memory,
2179 RegisterRule::Undefined,
2180 &ArmV7MExceptionHandler,
2181 )
2182 .unwrap();
2183
2184 assert_eq!(value, Some(RegisterValue::U32(0x100)));
2189 }
2190}