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