use std::ops::Range;
use crate::util::ReadRaw as _;
use crate::ErrorExt as _;
use crate::IntoError as _;
use crate::Result;
#[derive(Clone)]
pub(super) struct InlineInfo {
pub name: u32,
pub call_file: Option<u32>,
pub call_line: Option<u32>,
ranges: Vec<Range<u64>>,
children: Vec<Self>,
}
impl InlineInfo {
pub(crate) fn parse(
data: &mut &[u8],
base_addr: u64,
lookup_addr: Option<u64>,
) -> Result<Option<InlineInfo>> {
let range_cnt = data
.read_u64_leb128()
.ok_or_invalid_data(|| "failed to read range count from inline information")?;
let range_cnt = usize::try_from(range_cnt)
.ok()
.ok_or_invalid_data(|| "range count ({}) is too big")?;
if range_cnt == 0 {
return Ok(None)
}
let mut ranges = Vec::new();
let mut child_base_addr = 0u64;
if let Some(lookup_addr) = lookup_addr {
for i in 0..range_cnt {
let offset = data
.read_u64_leb128()
.ok_or_invalid_data(|| "failed to read offset from inline information")?;
let size = data
.read_u64_leb128()
.ok_or_invalid_data(|| "failed to read size from inline information")?;
let start = base_addr
.checked_add(offset)
.ok_or_invalid_data(|| {
format!("offset {offset:#x} overflowed base address {base_addr:#x}")
})
.context("failed calculate start address")?;
let end = start
.checked_add(size)
.ok_or_invalid_data(|| {
format!("size {size:#x} overflowed start address {start:#x}")
})
.context("failed calculate end address")?;
if i == 0 {
child_base_addr = start;
}
let range = start..end;
if range.contains(&lookup_addr) {
let () = ranges.push(range);
}
}
} else {
for _ in 0..range_cnt {
let _offset = data
.read_u64_leb128()
.ok_or_invalid_data(|| "failed to read offset from inline information")?;
let _size = data
.read_u64_leb128()
.ok_or_invalid_data(|| "failed to read size from inline information")?;
}
}
let child_cnt = data
.read_u8()
.ok_or_invalid_data(|| "failed to read child count from inline information")?;
let has_children = child_cnt != 0;
let name = data
.read_u32()
.ok_or_invalid_data(|| "failed to read name from inline information")?;
let (call_file, call_line) = if lookup_addr.is_some() {
let call_file = data
.read_u64_leb128()
.ok_or_invalid_data(|| "failed to read call file from inline information")?;
let call_file = u32::try_from(call_file)
.ok()
.ok_or_invalid_data(|| "call file index ({}) is too big")?;
let call_line = data
.read_u64_leb128()
.ok_or_invalid_data(|| "failed to read call line from inline information")?;
let call_line = u32::try_from(call_line).unwrap_or(u32::MAX);
(Some(call_file), Some(call_line))
} else {
let _call_file = data
.read_u64_leb128()
.ok_or_invalid_data(|| "failed to read call file from inline information")?;
let _call_line = data
.read_u64_leb128()
.ok_or_invalid_data(|| "failed to read call line from inline information")?;
(None, None)
};
let mut children = Vec::new();
if has_children {
if ranges.is_empty() {
while let Some(_child) = Self::parse(data, child_base_addr, None)? {
}
} else {
while let Some(child) = Self::parse(data, child_base_addr, lookup_addr)? {
let () = children.push(child);
}
}
}
let slf = Self {
name,
call_file,
call_line,
ranges,
children,
};
Ok(Some(slf))
}
fn inline_stack_impl<'slf>(&'slf self, addr: u64, inlined: &mut Vec<&'slf Self>) -> bool {
for range in &self.ranges {
if range.contains(&addr) {
if self.name > 0 {
let () = inlined.push(self);
}
for child in &self.children {
if child.inline_stack_impl(addr, inlined) {
break
}
}
return !inlined.is_empty()
}
}
false
}
pub(crate) fn inline_stack(&self, addr: u64) -> Vec<&Self> {
let mut inlined = Vec::new();
let _done = self.inline_stack_impl(addr, &mut inlined);
inlined
}
}