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