#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
mod graph_impl;
use frozen::Frozen;
use std::cmp;
use std::collections::btree_map;
use std::collections::{BTreeMap, BTreeSet};
use std::ops;
use std::slice;
use std::u32;
#[derive(Debug)]
pub struct ItemsBuilder {
size: u32,
size_added: u32,
parsed: BTreeSet<Id>,
items: BTreeMap<Id, Item>,
edges: BTreeMap<Id, BTreeSet<Id>>,
roots: BTreeSet<Id>,
data: BTreeMap<u32, (Id, u32)>,
}
impl ItemsBuilder {
pub fn new(size: u32) -> ItemsBuilder {
ItemsBuilder {
size,
size_added: 0,
parsed: Default::default(),
items: Default::default(),
edges: Default::default(),
roots: Default::default(),
data: Default::default(),
}
}
pub fn add_item(&mut self, item: Item) -> Id {
let id = item.id;
self.size_added += item.size;
self.items.insert(id, item);
let old_value = self.parsed.insert(id);
assert!(
old_value,
"should not parse the same key into multiple items"
);
id
}
pub fn add_root(&mut self, item: Item) -> Id {
let id = self.add_item(item);
self.roots.insert(id);
id
}
pub fn add_edge(&mut self, from: Id, to: Id) {
debug_assert!(self.items.contains_key(&from), "`from` is not known");
debug_assert!(self.items.contains_key(&to), "`to` is not known");
self.edges
.entry(from)
.or_insert_with(BTreeSet::new)
.insert(to);
}
pub fn link_data(&mut self, offset: i64, len: usize, id: Id) {
if offset >= 0 && offset <= i64::from(u32::MAX) && offset as usize + len < u32::MAX as usize
{
self.data.insert(offset as u32, (id, len as u32));
}
}
pub fn get_data(&self, offset: u32) -> Option<Id> {
self.data
.range(offset..)
.next()
.and_then(
|(start, &(id, len))| {
if offset < start + len {
Some(id)
} else {
None
}
},
)
}
pub fn size_added(&self) -> u32 {
self.size_added
}
pub fn finish(mut self) -> Items {
let meta_root_id = Id::root();
let meta_root = Item::new(meta_root_id, "<meta root>", 0, Misc::new());
self.items.insert(meta_root_id, meta_root);
self.edges.insert(meta_root_id, self.roots.clone());
Items {
size: self.size,
dominator_tree: None,
retained_sizes: None,
predecessors: None,
immediate_dominators: None,
items: Frozen::freeze(self.items),
edges: Frozen::freeze(
self.edges
.into_iter()
.map(|(from, tos)| (from, tos.into_iter().collect::<Vec<_>>()))
.collect(),
),
roots: Frozen::freeze(self.roots),
meta_root: meta_root_id,
}
}
}
#[derive(Debug)]
pub struct Items {
size: u32,
dominator_tree: Option<BTreeMap<Id, Vec<Id>>>,
immediate_dominators: Option<BTreeMap<Id, Id>>,
retained_sizes: Option<BTreeMap<Id, u32>>,
predecessors: Option<BTreeMap<Id, Vec<Id>>>,
items: Frozen<BTreeMap<Id, Item>>,
edges: Frozen<BTreeMap<Id, Vec<Id>>>,
roots: Frozen<BTreeSet<Id>>,
meta_root: Id,
}
impl ops::Index<Id> for Items {
type Output = Item;
fn index(&self, id: Id) -> &Item {
&self.items[&id]
}
}
impl Items {
pub fn iter(&self) -> Iter {
Iter {
inner: self.items.iter(),
}
}
pub fn neighbors(&self, id: Id) -> Neighbors {
Neighbors {
inner: self
.edges
.get(&id)
.map_or_else(|| [].iter(), |edges| edges.iter()),
}
}
pub fn predecessors(&self, id: Id) -> Predecessors {
Predecessors {
inner: self
.predecessors
.as_ref()
.expect("To access predecessors, must have already called compute_predecessors")
.get(&id)
.map_or_else(|| [].iter(), |edges| edges.iter()),
}
}
pub fn size(&self) -> u32 {
self.size
}
pub fn meta_root(&self) -> Id {
self.meta_root
}
pub fn compute_predecessors(&mut self) {
if self.predecessors.is_some() {
return;
}
let mut predecessors = BTreeMap::new();
for (from, tos) in self.edges.iter() {
for to in tos {
predecessors
.entry(*to)
.or_insert_with(BTreeSet::new)
.insert(*from);
}
}
self.predecessors = Some(
predecessors
.into_iter()
.map(|(k, v)| (k, v.into_iter().collect()))
.collect(),
);
}
pub fn compute_dominators(&mut self) {
if self.immediate_dominators.is_some() {
return;
}
let mut immediate_dominators = BTreeMap::new();
let dominators = petgraph::algo::dominators::simple_fast(&*self, self.meta_root);
for item in self.iter() {
if let Some(idom) = dominators.immediate_dominator(item.id()) {
immediate_dominators.insert(item.id(), idom);
}
}
self.immediate_dominators = Some(immediate_dominators);
}
pub fn immediate_dominators(&self) -> &BTreeMap<Id, Id> {
self.immediate_dominators
.as_ref()
.expect("must call compute_immediate_dominators before calling immediate_dominators")
}
pub fn compute_dominator_tree(&mut self) {
if self.dominator_tree.is_some() {
return;
}
let mut dominator_tree = BTreeMap::new();
let dominators = petgraph::algo::dominators::simple_fast(&*self, self.meta_root);
for item in self.iter() {
if let Some(idom) = dominators.immediate_dominator(item.id()) {
dominator_tree
.entry(idom)
.or_insert_with(BTreeSet::new)
.insert(item.id());
}
}
self.dominator_tree = Some(
dominator_tree
.into_iter()
.map(|(k, v)| (k, v.into_iter().collect()))
.collect(),
);
}
pub fn dominator_tree(&self) -> &BTreeMap<Id, Vec<Id>> {
self.dominator_tree
.as_ref()
.expect("must call compute_dominator_tree before calling dominator_tree")
}
pub fn compute_retained_sizes(&mut self) {
if self.retained_sizes.is_some() {
return;
}
self.compute_dominator_tree();
fn recursive_retained_size(
retained_sizes: &mut BTreeMap<Id, u32>,
items: &Items,
item: &Item,
dominator_tree: &BTreeMap<Id, Vec<Id>>,
) -> u32 {
if let Some(rsize) = retained_sizes.get(&item.id()) {
return *rsize;
}
let mut rsize = item.size();
if let Some(children) = dominator_tree.get(&item.id()) {
for child in children {
rsize += recursive_retained_size(
retained_sizes,
items,
&items[*child],
dominator_tree,
);
}
}
let old_value = retained_sizes.insert(item.id(), rsize);
assert!(old_value.is_none());
rsize
}
let mut retained_sizes = BTreeMap::new();
{
let dominator_tree = self.dominator_tree.as_ref().unwrap();
for item in self.iter() {
recursive_retained_size(&mut retained_sizes, self, item, dominator_tree);
}
}
self.retained_sizes = Some(retained_sizes);
}
pub fn retained_size(&self, id: Id) -> u32 {
self.retained_sizes
.as_ref()
.expect(
"Cannot call retained_sizes unless compute_retained_sizes \
has already been called",
)
.get(&id)
.cloned()
.unwrap()
}
pub fn get_item_by_name(&self, name: &str) -> Option<&Item> {
for item in self.iter() {
if item.name() == name {
return Some(item);
}
}
None }
}
#[derive(Debug)]
pub struct Neighbors<'a> {
inner: slice::Iter<'a, Id>,
}
impl<'a> Iterator for Neighbors<'a> {
type Item = Id;
#[inline]
fn next(&mut self) -> Option<Id> {
self.inner.next().cloned()
}
}
#[derive(Debug)]
pub struct Predecessors<'a> {
inner: slice::Iter<'a, Id>,
}
impl<'a> Iterator for Predecessors<'a> {
type Item = Id;
#[inline]
fn next(&mut self) -> Option<Id> {
self.inner.next().cloned()
}
}
#[derive(Clone, Debug)]
pub struct Iter<'a> {
inner: btree_map::Iter<'a, Id, Item>,
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a Item;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(_, item)| item)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Id(u32, u32);
impl Id {
pub fn section(section: usize) -> Id {
assert!(section < u32::MAX as usize);
Id(section as u32, u32::MAX)
}
pub fn entry(section: usize, index: usize) -> Id {
assert!(section < u32::MAX as usize);
assert!(index < u32::MAX as usize);
Id(section as u32, index as u32)
}
pub fn root() -> Id {
Id(u32::MAX, u32::MAX)
}
pub fn serializable(self) -> u64 {
let top = (u64::from(self.0)) << 32;
top | u64::from(self.1)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Item {
id: Id,
name: String,
size: u32,
kind: ItemKind,
}
impl Item {
pub fn new<S, K>(id: Id, name: S, size: u32, kind: K) -> Item
where
S: Into<String>,
K: Into<ItemKind>,
{
let name = name.into();
Item {
id,
name,
size,
kind: kind.into(),
}
}
#[inline]
pub fn id(&self) -> Id {
self.id
}
#[inline]
pub fn size(&self) -> u32 {
self.size
}
#[inline]
pub fn name(&self) -> &str {
if let ItemKind::Code(ref code) = self.kind {
code.demangled().unwrap_or(&self.name)
} else {
&self.name
}
}
#[inline]
pub fn kind(&self) -> &ItemKind {
&self.kind
}
#[inline]
pub fn monomorphization_of(&self) -> Option<&str> {
if let ItemKind::Code(ref code) = self.kind {
code.monomorphization_of()
} else {
None
}
}
}
impl PartialOrd for Item {
fn partial_cmp(&self, rhs: &Item) -> Option<cmp::Ordering> {
self.id.partial_cmp(&rhs.id)
}
}
impl Ord for Item {
fn cmp(&self, rhs: &Item) -> cmp::Ordering {
self.id.cmp(&rhs.id)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ItemKind {
Code(Code),
Data(Data),
Debug(DebugInfo),
Misc(Misc),
}
impl ItemKind {
pub fn is_data(&self) -> bool {
match self {
ItemKind::Data(_) => true,
_ => false,
}
}
}
impl From<Code> for ItemKind {
fn from(c: Code) -> ItemKind {
ItemKind::Code(c)
}
}
impl From<Data> for ItemKind {
fn from(d: Data) -> ItemKind {
ItemKind::Data(d)
}
}
impl From<DebugInfo> for ItemKind {
fn from(d: DebugInfo) -> ItemKind {
ItemKind::Debug(d)
}
}
impl From<Misc> for ItemKind {
fn from(m: Misc) -> ItemKind {
ItemKind::Misc(m)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Code {
demangled: Option<String>,
monomorphization_of: Option<String>,
}
impl Code {
pub fn new(name: &str) -> Code {
let demangled = Self::demangle(&name);
let monomorphization_of =
Self::extract_generic_function(demangled.as_ref().map(|s| s.as_str()).unwrap_or(name));
Code {
demangled,
monomorphization_of,
}
}
pub fn demangled(&self) -> Option<&str> {
self.demangled.as_ref().map(|s| s.as_str())
}
pub fn monomorphization_of(&self) -> Option<&str> {
self.monomorphization_of.as_ref().map(|s| s.as_str())
}
fn demangle(s: &str) -> Option<String> {
if let Ok(sym) = rustc_demangle::try_demangle(s) {
return Some(sym.to_string());
}
if !s.starts_with("_Z") && !s.starts_with("__Z") && !s.starts_with("_GLOBAL_") {
return Some(s.to_string());
}
if let Ok(sym) = cpp_demangle::Symbol::new(s) {
return Some(sym.to_string());
}
None
}
fn extract_generic_function(demangled: &str) -> Option<String> {
if let Some(idx) = demangled.rfind("::h") {
let idx2 = demangled.rfind("::").unwrap();
assert!(idx2 >= idx);
if idx2 == idx {
let generic = demangled[..idx].to_string();
return Some(generic);
}
}
let open_bracket = match demangled.char_indices().find(|&(_, ch)| ch == '<') {
None => return None,
Some((idx, _)) => idx,
};
let close_bracket = match demangled.char_indices().rev().find(|&(_, ch)| ch == '>') {
None => return None,
Some((idx, _)) => idx,
};
if close_bracket < open_bracket || open_bracket == 0 {
return None;
}
Some(demangled[..open_bracket].to_string())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Data {
ty: Option<String>,
}
impl Data {
pub fn new(ty: Option<String>) -> Data {
Data { ty }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct DebugInfo;
impl DebugInfo {
pub fn new() -> DebugInfo {
DebugInfo
}
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct Misc;
impl Misc {
pub fn new() -> Misc {
Misc
}
}