1 use crate::{debug::Reader, translate::get_vmctx_value_label};
2 use core::fmt;
3 use cranelift_codegen::{LabelValueLoc, ValueLabelsRanges, ir::ValueLabel, isa::TargetIsa};
4 use gimli::{AttributeValue, DebuggingInformationEntry, LittleEndian, UnitOffset, UnitRef, write};
5 
6 macro_rules! dbi_log_enabled {
7     () => {
8         cfg!(any(feature = "trace-log", debug_assertions))
9             && ::log::log_enabled!(target: "debug-info-transform", ::log::Level::Trace)
10     };
11 }
12 macro_rules! dbi_log {
13     ($($tt:tt)*) => {
14         if cfg!(any(feature = "trace-log", debug_assertions)) {
15             ::log::trace!(target: "debug-info-transform", $($tt)*);
16         }
17     };
18 }
19 pub(crate) use dbi_log;
20 pub(crate) use dbi_log_enabled;
21 
22 pub struct CompileUnitSummary<'a, 'r> {
23     unit: UnitRef<'a, Reader<'r>>,
24 }
25 
26 impl<'a, 'r> fmt::Debug for CompileUnitSummary<'a, 'r> {
27     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28         let unit = self.unit;
29         write!(f, "0x{:08x} [", unit.header.offset().0)?;
30         let comp_dir = match unit.comp_dir {
31             Some(dir) => &dir.to_string_lossy(),
32             None => "None",
33         };
34         write!(f, "\"{comp_dir}\"")?;
35         let name = match unit.name {
36             Some(name) => &name.to_string_lossy(),
37             None => "None",
38         };
39         write!(f, ", \"{name}\"]")
40     }
41 }
42 
43 pub fn log_get_cu_summary<'a, 'r>(unit: UnitRef<'a, Reader<'r>>) -> CompileUnitSummary<'a, 'r> {
44     CompileUnitSummary { unit }
45 }
46 
47 pub struct DieRefSummary<'a, 'r> {
48     unit: UnitRef<'a, Reader<'r>>,
49     unit_ref: UnitOffset,
50 }
51 
52 impl<'a, 'r> fmt::Debug for DieRefSummary<'a, 'r> {
53     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54         let section_offs = self.unit_ref.to_unit_section_offset(&self.unit);
55         write!(f, "0x{:08x}", section_offs.0)
56     }
57 }
58 
59 pub fn log_get_die_ref<'a, 'r>(
60     unit: UnitRef<'a, Reader<'r>>,
61     unit_ref: UnitOffset,
62 ) -> DieRefSummary<'a, 'r> {
63     DieRefSummary { unit, unit_ref }
64 }
65 
66 struct DieDetailedSummary<'a, 'r> {
67     unit: UnitRef<'a, Reader<'r>>,
68     die: &'a DebuggingInformationEntry<Reader<'r>>,
69 }
70 
71 pub fn log_begin_input_die<'r>(
72     unit: UnitRef<Reader<'r>>,
73     die: &DebuggingInformationEntry<Reader<'r>>,
74     depth: isize,
75 ) {
76     dbi_log!(
77         "=== Begin DIE at {:?} (depth = {}):\n{:?}",
78         log_get_die_ref(unit, die.offset()),
79         depth,
80         DieDetailedSummary { unit, die }
81     );
82 }
83 
84 impl<'a, 'r> fmt::Debug for DieDetailedSummary<'a, 'r> {
85     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86         let die = self.die;
87         let unit = self.unit;
88         write!(f, "{}\n", die.tag())?;
89 
90         for attr in die.attrs() {
91             write!(f, "  {} (", attr.name())?;
92             let attr_value = attr.value();
93             match attr_value {
94                 AttributeValue::Addr(addr) => {
95                     write!(f, "{addr:08x}")
96                 }
97                 AttributeValue::DebugAddrIndex(index) => {
98                     if let Some(addr) = unit.address(index).ok() {
99                         write!(f, "{addr:08x}")
100                     } else {
101                         write!(f, "<error reading address at index: {}>", index.0)
102                     }
103                 }
104                 AttributeValue::Block(d) => write!(f, "{d:?}"),
105                 AttributeValue::Udata(d) => write!(f, "{d}"),
106                 AttributeValue::Data1(d) => write!(f, "{d}"),
107                 AttributeValue::Data2(d) => write!(f, "{d}"),
108                 AttributeValue::Data4(d) => write!(f, "{d}"),
109                 AttributeValue::Data8(d) => write!(f, "{d}"),
110                 AttributeValue::Sdata(d) => write!(f, "{d}"),
111                 AttributeValue::Flag(d) => write!(f, "{d}"),
112                 AttributeValue::DebugLineRef(offset) => write!(f, "0x{:08x}", offset.0),
113                 AttributeValue::FileIndex(index) => write!(f, "0x{index:08x}"),
114                 AttributeValue::String(_)
115                 | AttributeValue::DebugStrRef(_)
116                 | AttributeValue::DebugStrOffsetsIndex(_) => {
117                     if let Ok(s) = unit.attr_string(attr_value) {
118                         write!(f, "\"{}\"", &s.to_string_lossy())
119                     } else {
120                         write!(f, "<error reading string>")
121                     }
122                 }
123                 AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => {
124                     let _ = unit.attr_ranges_offset(attr_value);
125                     write!(f, "<TODO: rnglist dump>")
126                 }
127                 AttributeValue::LocationListsRef(_) | AttributeValue::DebugLocListsIndex(_) => {
128                     let _ = unit.attr_locations_offset(attr_value);
129                     write!(f, "<TODO: loclist dump>")
130                 }
131                 AttributeValue::Exprloc(_) => {
132                     write!(f, "<TODO: exprloc dump>")
133                 }
134                 AttributeValue::Encoding(value) => write!(f, "{value}"),
135                 AttributeValue::DecimalSign(value) => write!(f, "{value}"),
136                 AttributeValue::Endianity(value) => write!(f, "{value}"),
137                 AttributeValue::Accessibility(value) => write!(f, "{value}"),
138                 AttributeValue::Visibility(value) => write!(f, "{value}"),
139                 AttributeValue::Virtuality(value) => write!(f, "{value}"),
140                 AttributeValue::Language(value) => write!(f, "{value}"),
141                 AttributeValue::AddressClass(value) => write!(f, "{value}"),
142                 AttributeValue::IdentifierCase(value) => write!(f, "{value}"),
143                 AttributeValue::CallingConvention(value) => write!(f, "{value}"),
144                 AttributeValue::Inline(value) => write!(f, "{value}"),
145                 AttributeValue::Ordering(value) => write!(f, "{value}"),
146                 AttributeValue::UnitRef(offset) => {
147                     write!(f, "0x{:08x}", offset.to_unit_section_offset(&unit).0)
148                 }
149                 AttributeValue::DebugInfoRef(offset) => write!(f, "0x{:08x}", offset.0),
150                 unexpected_attr => write!(f, "<unexpected attr: {unexpected_attr:?}>"),
151             }?;
152             write!(f, ")\n")?;
153         }
154         Ok(())
155     }
156 }
157 
158 struct OutDieDetailedSummary<'a> {
159     die_id: write::UnitEntryId,
160     unit: &'a write::Unit,
161     strings: &'a write::StringTable,
162 }
163 
164 impl<'a> fmt::Debug for OutDieDetailedSummary<'a> {
165     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166         let die = self.unit.get(self.die_id);
167         write!(f, "{}\n", die.tag())?;
168         for attr in die.attrs() {
169             write!(f, "  {} (", attr.name())?;
170             let attr_value = attr.get();
171             match attr_value {
172                 write::AttributeValue::Address(addr) => match addr {
173                     write::Address::Constant(addr_value) => write!(f, "{addr_value:08x}"),
174                     write::Address::Symbol { symbol, addend } => {
175                         write!(f, "symbol #{symbol}+{addend}")
176                     }
177                 },
178                 write::AttributeValue::Block(d) => {
179                     write!(f, "{:?}", Reader::new(d.as_slice(), LittleEndian))
180                 }
181                 write::AttributeValue::Udata(d) => write!(f, "{d}"),
182                 write::AttributeValue::Data1(d) => write!(f, "{d}"),
183                 write::AttributeValue::Data2(d) => write!(f, "{d}"),
184                 write::AttributeValue::Data4(d) => write!(f, "{d}"),
185                 write::AttributeValue::Data8(d) => write!(f, "{d}"),
186                 write::AttributeValue::Sdata(d) => write!(f, "{d}"),
187                 write::AttributeValue::Flag(d) => write!(f, "{d}"),
188                 write::AttributeValue::LineProgramRef => write!(f, "LineProgramRef"),
189                 write::AttributeValue::FileIndex(index) => match index {
190                     Some(id) => write!(f, "{id:?}"),
191                     None => write!(f, "<file index missing>"),
192                 },
193                 write::AttributeValue::String(s) => {
194                     write!(f, "\"{}\"", &String::from_utf8_lossy(s))
195                 }
196                 write::AttributeValue::StringRef(id) => {
197                     write!(f, "\"{}\"", &String::from_utf8_lossy(self.strings.get(*id)))
198                 }
199                 write::AttributeValue::RangeListRef(_) => {
200                     write!(f, "<TODO: out rnglist dump>")
201                 }
202                 write::AttributeValue::LocationListRef(_) => {
203                     write!(f, "<TODO: out loclist dump>")
204                 }
205                 write::AttributeValue::Exprloc(_) => {
206                     write!(f, "<TODO: out exprloc dump>")
207                 }
208                 write::AttributeValue::Encoding(value) => write!(f, "{value}"),
209                 write::AttributeValue::DecimalSign(value) => write!(f, "{value}"),
210                 write::AttributeValue::Endianity(value) => write!(f, "{value}"),
211                 write::AttributeValue::Accessibility(value) => write!(f, "{value}"),
212                 write::AttributeValue::Visibility(value) => write!(f, "{value}"),
213                 write::AttributeValue::Virtuality(value) => write!(f, "{value}"),
214                 write::AttributeValue::Language(value) => write!(f, "{value}"),
215                 write::AttributeValue::AddressClass(value) => write!(f, "{value}"),
216                 write::AttributeValue::IdentifierCase(value) => write!(f, "{value}"),
217                 write::AttributeValue::CallingConvention(value) => write!(f, "{value}"),
218                 write::AttributeValue::Inline(value) => write!(f, "{value}"),
219                 write::AttributeValue::Ordering(value) => write!(f, "{value}"),
220                 write::AttributeValue::UnitRef(unit_ref) => write!(f, "{unit_ref:?}>"),
221                 write::AttributeValue::DebugInfoRef(reference) => match reference {
222                     write::DebugInfoRef::Symbol(index) => write!(f, "symbol #{index}>"),
223                     write::DebugInfoRef::Entry(unit_id, die_id) => {
224                         write!(f, "{die_id:?} in {unit_id:?}>")
225                     }
226                 },
227                 unexpected_attr => write!(f, "<unexpected attr: {unexpected_attr:?}>"),
228             }?;
229             write!(f, ")\n")?;
230         }
231         Ok(())
232     }
233 }
234 
235 pub fn log_end_output_die(
236     input_die: &DebuggingInformationEntry<Reader<'_>>,
237     input_unit: UnitRef<Reader<'_>>,
238     die_id: write::UnitEntryId,
239     unit: &write::Unit,
240     strings: &write::StringTable,
241     depth: isize,
242 ) {
243     dbi_log!(
244         "=== End DIE at {:?} (depth = {}):\n{:?}",
245         log_get_die_ref(input_unit, input_die.offset()),
246         depth,
247         OutDieDetailedSummary {
248             die_id,
249             unit,
250             strings
251         }
252     );
253 }
254 
255 pub fn log_end_output_die_skipped(
256     input_die: &DebuggingInformationEntry<Reader<'_>>,
257     input_unit: UnitRef<Reader<'_>>,
258     reason: &str,
259     depth: isize,
260 ) {
261     dbi_log!(
262         "=== End DIE at {:?} (depth = {}):\n  Skipped as {}\n",
263         log_get_die_ref(input_unit, input_die.offset()),
264         depth,
265         reason
266     );
267 }
268 
269 pub fn log_get_value_name(value: ValueLabel) -> ValueNameSummary {
270     ValueNameSummary { value }
271 }
272 
273 pub struct ValueNameSummary {
274     value: ValueLabel,
275 }
276 
277 impl fmt::Debug for ValueNameSummary {
278     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279         if self.value == get_vmctx_value_label() {
280             f.pad("VMCTX")
281         } else {
282             f.pad(&format!("L#{}", self.value.as_u32()))
283         }
284     }
285 }
286 
287 pub fn log_get_value_loc(loc: LabelValueLoc, isa: &dyn TargetIsa) -> ValueLocSummary<'_> {
288     ValueLocSummary { loc, isa }
289 }
290 
291 pub struct ValueLocSummary<'a> {
292     loc: LabelValueLoc,
293     isa: &'a dyn TargetIsa,
294 }
295 
296 impl<'a> fmt::Debug for ValueLocSummary<'a> {
297     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298         if let LabelValueLoc::Reg(reg) = self.loc {
299             let reg_name = self.isa.pretty_print_reg(reg, self.isa.pointer_bytes());
300             return write!(f, "{reg_name}");
301         }
302 
303         write!(f, "{:?}", self.loc)
304     }
305 }
306 
307 pub fn log_get_value_ranges<'a>(
308     ranges: Option<&'a ValueLabelsRanges>,
309     isa: &'a dyn TargetIsa,
310 ) -> ValueRangesSummary<'a> {
311     ValueRangesSummary { ranges, isa }
312 }
313 
314 pub struct ValueRangesSummary<'a> {
315     ranges: Option<&'a ValueLabelsRanges>,
316     isa: &'a dyn TargetIsa,
317 }
318 
319 impl<'a> fmt::Debug for ValueRangesSummary<'a> {
320     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321         if let Some(ranges) = self.ranges {
322             // Sort the output first for nicer display.
323             let mut locals = Vec::new();
324             for value in ranges {
325                 locals.push(*value.0);
326             }
327             locals.sort_by_key(|n| n.as_u32());
328 
329             for i in 0..locals.len() {
330                 let name = locals[i];
331                 write!(f, "{:<6?}:", log_get_value_name(name))?;
332                 for range in ranges.get(&name).unwrap() {
333                     write!(f, " {:?}", log_get_value_loc(range.loc, self.isa))?;
334                     write!(f, "@[{}..{})", range.start, range.end)?;
335                 }
336                 if i != locals.len() - 1 {
337                     writeln!(f)?;
338                 }
339             }
340         }
341         Ok(())
342     }
343 }
344