use std::collections::BTreeMap;
use anyhow::{anyhow, ensure, Context, Result};
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Profile {
pub(crate) call_sites: BTreeMap<u32, CallSiteProfile>,
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct CallSiteProfile {
pub(crate) total_call_count: u64,
pub(crate) callee_to_count: BTreeMap<u32, u64>,
}
impl Profile {
pub fn from_three_globals(mut read_global: impl FnMut(&str) -> Option<u64>) -> Result<Self> {
let mut profile = Profile::default();
for call_site_index in 0.. {
let total_call_count =
match read_global(&format!("__winliner_call_site_{call_site_index}_total")) {
None => break,
Some(x) => x,
};
let last_callee = read_global(&format!(
"__winliner_call_site_{call_site_index}_last_callee"
))
.ok_or_else(|| {
anyhow!(
"Failed to read `__winliner_call_site_{call_site_index}_last_callee` global"
)
})?;
let last_callee = u32::try_from(last_callee).context("callee is out of bounds")?;
let last_callee_count = read_global(&format!(
"__winliner_call_site_{call_site_index}_last_callee_count"
))
.ok_or_else(|| {
anyhow!(
"Failed to read `__winliner_call_site_{call_site_index}_last_callee` global"
)
})?;
ensure!(
total_call_count >= last_callee_count,
"Bogus profiling data: call site's total count is less than the last callee's call \
count",
);
let mut callee_to_count = BTreeMap::new();
callee_to_count.insert(last_callee, last_callee_count);
profile.call_sites.insert(
call_site_index,
CallSiteProfile {
total_call_count,
callee_to_count,
},
);
}
Ok(profile)
}
pub fn merge(&mut self, other: &Profile) {
for (call_site_index, other) in other.call_sites.iter() {
let call_site = self.call_sites.entry(*call_site_index).or_default();
call_site.total_call_count += other.total_call_count;
for (callee, count) in other.callee_to_count.iter() {
*call_site.callee_to_count.entry(*callee).or_default() += count;
}
}
}
}
#[derive(Clone, Default)]
pub struct ProfileBuilder {
profile: Profile,
}
impl ProfileBuilder {
pub fn new() -> Self {
Default::default()
}
pub fn add_indirect_call(&mut self, callee: u32, call_site: u32) {
let call_site = self.profile.call_sites.entry(call_site).or_default();
call_site.total_call_count += 1;
*call_site.callee_to_count.entry(callee).or_default() += 1;
}
pub fn build(self) -> Profile {
self.profile
}
}