1pub mod debug_info;
8pub mod debug_step;
10pub mod function_die;
12pub(crate) mod language;
14pub mod registers;
16pub(crate) mod source_instructions;
18pub mod stack_frame;
20pub mod unit_info;
22pub mod variable;
24pub mod variable_cache;
26
27pub(crate) mod exception_handling;
28
29pub use self::{
30 debug_info::*, debug_step::SteppingMode, exception_handling::exception_handler_for_core,
31 registers::*, source_instructions::SourceLocation, source_instructions::VerifiedBreakpoint,
32 stack_frame::StackFrame, variable::*, variable_cache::VariableCache,
33};
34
35use probe_rs::{Core, MemoryInterface};
36
37use gimli::DebuggingInformationEntry;
38use gimli::EvaluationResult;
39use gimli::{AttributeValue, RunTimeEndian};
40use serde::Serialize;
41use typed_path::TypedPathBuf;
42
43use std::num::ParseIntError;
44use std::{
45 io,
46 num::NonZeroU32,
47 str::Utf8Error,
48 sync::atomic::{AtomicU32, Ordering},
49 vec,
50};
51
52pub type EndianReader = gimli::EndianReader<RunTimeEndian, std::rc::Rc<[u8]>>;
54
55#[derive(Debug, thiserror::Error)]
57pub enum DebugError {
58 #[error("IO Error while accessing debug data")]
60 Io(#[from] io::Error),
61 #[error("Error accessing debug data")]
63 DebugData(#[from] object::read::Error),
64 #[error("Error parsing debug data")]
66 Parse(#[from] gimli::read::Error),
67 #[error("Non-UTF8 data found in debug data")]
69 NonUtf8(#[from] Utf8Error),
70 #[error("Error using the probe")]
72 Probe(#[from] probe_rs::Error),
73 #[error(transparent)]
75 CharConversion(#[from] std::char::CharTryFromError),
76 #[error(transparent)]
78 IntConversion(#[from] std::num::TryFromIntError),
79 #[error("{message}")]
83 WarnAndContinue {
84 message: String,
86 },
87
88 #[error("Not implemented: {0}")]
90 NotImplemented(&'static str),
91
92 #[error("{0}")]
94 Other(String),
95}
96
97#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
99pub enum ColumnType {
100 LeftEdge,
102 Column(u64),
104}
105
106impl From<gimli::ColumnType> for ColumnType {
107 fn from(column: gimli::ColumnType) -> Self {
108 match column {
109 gimli::ColumnType::LeftEdge => ColumnType::LeftEdge,
110 gimli::ColumnType::Column(c) => ColumnType::Column(c.get()),
111 }
112 }
113}
114
115impl From<u64> for ColumnType {
116 fn from(column: u64) -> Self {
117 match column {
118 0 => ColumnType::LeftEdge,
119 _ => ColumnType::Column(column),
120 }
121 }
122}
123
124#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
126pub enum ObjectRef {
127 Valid(NonZeroU32),
129 #[default]
131 Invalid,
132}
133
134impl PartialOrd for ObjectRef {
135 fn partial_cmp(&self, other: &ObjectRef) -> Option<std::cmp::Ordering> {
136 Some(self.cmp(other))
137 }
138}
139
140impl Ord for ObjectRef {
141 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
142 i64::from(*self).cmp(&i64::from(*other))
143 }
144}
145
146impl From<ObjectRef> for i64 {
147 fn from(value: ObjectRef) -> Self {
148 match value {
149 ObjectRef::Valid(v) => v.get() as i64,
150 ObjectRef::Invalid => 0,
151 }
152 }
153}
154
155impl From<i64> for ObjectRef {
156 fn from(value: i64) -> Self {
157 if value > 0 {
158 ObjectRef::Valid(NonZeroU32::try_from(value as u32).unwrap())
159 } else {
160 ObjectRef::Invalid
161 }
162 }
163}
164
165impl std::str::FromStr for ObjectRef {
166 type Err = ParseIntError;
167
168 fn from_str(s: &str) -> Result<Self, Self::Err> {
169 let value = s.parse::<i64>()?;
170 Ok(ObjectRef::from(value))
171 }
172}
173
174static CACHE_KEY: AtomicU32 = AtomicU32::new(1);
175pub fn get_object_reference() -> ObjectRef {
177 let key = CACHE_KEY.fetch_add(1, Ordering::SeqCst);
178 ObjectRef::Valid(NonZeroU32::new(key).unwrap())
179}
180
181fn extract_file(
183 debug_info: &DebugInfo,
184 unit: &gimli::Unit<GimliReader>,
185 attribute_value: AttributeValue<GimliReader>,
186) -> Option<TypedPathBuf> {
187 match attribute_value {
188 AttributeValue::FileIndex(index) => {
189 if let Some(path) = debug_info.find_file_and_directory(unit, index) {
190 Some(path)
191 } else {
192 tracing::warn!("Unable to extract file or path from {:?}.", attribute_value);
193 None
194 }
195 }
196 other => {
197 tracing::warn!(
198 "Unable to extract file information from attribute value {:?}: Not implemented.",
199 other
200 );
201 None
202 }
203 }
204}
205
206fn extract_byte_size(node_die: &DebuggingInformationEntry<GimliReader>) -> Option<u64> {
208 match node_die.attr(gimli::DW_AT_byte_size) {
209 Ok(Some(byte_size_attr)) => match byte_size_attr.value() {
210 AttributeValue::Udata(byte_size) => Some(byte_size),
211 AttributeValue::Data1(byte_size) => Some(byte_size as u64),
212 AttributeValue::Data2(byte_size) => Some(byte_size as u64),
213 AttributeValue::Data4(byte_size) => Some(byte_size as u64),
214 AttributeValue::Data8(byte_size) => Some(byte_size),
215 other => {
216 tracing::warn!("Unimplemented: DW_AT_byte_size value: {other:?}");
217 None
218 }
219 },
220 Ok(None) => None,
221 Err(error) => {
222 tracing::warn!(
223 "Failed to extract byte_size: {error:?} for debug_entry {:?}",
224 node_die.tag().static_string()
225 );
226 None
227 }
228 }
229}
230
231fn extract_line(attribute_value: AttributeValue<GimliReader>) -> Option<u64> {
232 match attribute_value {
233 AttributeValue::Udata(line) => Some(line),
234 _ => None,
235 }
236}
237
238#[expect(clippy::unwrap_used)]
239pub(crate) fn _print_all_attributes(
240 core: &mut Core<'_>,
241 stackframe_cfa: Option<u64>,
242 dwarf: &gimli::Dwarf<DwarfReader>,
243 unit: &gimli::Unit<DwarfReader>,
244 tag: &gimli::DebuggingInformationEntry<DwarfReader>,
245 print_depth: usize,
246) {
247 let mut attrs = tag.attrs();
248
249 while let Some(attr) = attrs.next().unwrap() {
250 for _ in 0..print_depth {
251 print!("\t");
252 }
253 print!("{}: ", attr.name());
254
255 match attr.value() {
256 AttributeValue::Addr(a) => println!("{a:#010x}"),
257 AttributeValue::DebugStrRef(str_ref) => {
258 let val = dwarf.string(str_ref).unwrap();
259 println!("{}", std::str::from_utf8(&val).unwrap());
260 }
261 AttributeValue::Exprloc(e) => {
262 let mut evaluation = e.evaluation(unit.encoding());
263
264 let mut result = evaluation.evaluate().unwrap();
266
267 while let Some(next) = iterate(result, core, &mut evaluation, stackframe_cfa) {
268 result = next;
269 }
270
271 let result = evaluation.result();
272
273 println!("Expression: {:x?}", &result[0]);
274 }
275 AttributeValue::LocationListsRef(_) => println!("LocationList"),
276 AttributeValue::DebugLocListsBase(_) => println!(" LocationList"),
277 AttributeValue::DebugLocListsIndex(_) => println!(" LocationList"),
278 _ => println!("print_all_attributes {:?}", attr.value()),
279 }
280 }
281}
282
283#[expect(dead_code)]
284fn iterate(
285 result: EvaluationResult<DwarfReader>,
286 core: &mut Core,
287 evaluation: &mut gimli::Evaluation<DwarfReader>,
288 stackframe_cfa: Option<u64>,
289) -> Option<EvaluationResult<DwarfReader>> {
290 let resume_result = match result {
291 EvaluationResult::Complete => return None,
292 EvaluationResult::RequiresMemory { address, size, .. } => {
293 let mut buff = vec![0u8; size as usize];
294 core.read(address, &mut buff)
295 .expect("Failed to read memory");
296
297 let value = match size {
298 1 => gimli::Value::U8(buff[0]),
299 2 => gimli::Value::U16(u16::from_be_bytes([buff[0], buff[1]])),
300 4 => gimli::Value::U32(u32::from_be_bytes([buff[0], buff[1], buff[2], buff[3]])),
301 x => unimplemented!("Requested memory with size {x}, which is not supported yet."),
302 };
303
304 evaluation.resume_with_memory(value)
305 }
306 EvaluationResult::RequiresFrameBase => {
307 evaluation.resume_with_frame_base(stackframe_cfa.unwrap())
308 }
309 EvaluationResult::RequiresRegister {
310 register,
311 base_type,
312 } => {
313 let raw_value = core
314 .read_core_reg::<u64>(register.0)
315 .expect("Failed to read memory");
316
317 if base_type != gimli::UnitOffset(0) {
318 unimplemented!(
319 "Support for units in RequiresRegister request is not yet implemented."
320 )
321 }
322 evaluation.resume_with_register(gimli::Value::Generic(raw_value))
323 }
324 EvaluationResult::RequiresRelocatedAddress(address_index) => {
325 evaluation.resume_with_relocated_address(address_index)
327 }
328 x => {
329 println!("print_all_attributes {x:?}");
330 todo!()
332 }
333 };
334
335 Some(resume_result.unwrap())
336}