Skip to main content

cargo_show_asm/
callgraph.rs

1use regex::Regex;
2use std::collections::{HashMap, HashSet, VecDeque};
3
4use crate::esafeprintln;
5
6#[derive(Debug, Clone, Default)]
7/// caller -> callee
8pub struct CallGraph<'a>(pub HashMap<&'a str, HashSet<&'a str>>);
9
10/// callee -> caller
11#[derive(Debug, Clone, Default)]
12pub struct InvCallGraph<'a>(pub HashMap<&'a str, HashSet<&'a str>>);
13
14impl<'a> CallGraph<'a> {
15    pub fn filter(&self, regex_str: &str, max_depth: usize) -> HashMap<&'a str, usize> {
16        let re = match Regex::new(regex_str) {
17            Ok(re) => re,
18            Err(err) => {
19                esafeprintln!("{err}");
20                std::process::exit(1);
21            }
22        };
23        self.invert()
24            .callers_of(&re)
25            .into_iter()
26            .filter(|(_, d)| *d <= max_depth)
27            .collect()
28    }
29
30    pub fn invert(&self) -> InvCallGraph<'a> {
31        let mut inv: HashMap<&str, HashSet<&str>> = HashMap::new();
32        for (&caller, callees) in &self.0 {
33            for &callee in callees {
34                inv.entry(callee).or_default().insert(caller);
35            }
36        }
37        InvCallGraph(inv)
38    }
39}
40
41impl<'a> InvCallGraph<'a> {
42    pub fn callers_of(&self, re: &Regex) -> HashMap<&'a str, usize> {
43        let mut depths: HashMap<&str, usize> = HashMap::new();
44        let mut queue: VecDeque<(&str, usize)> = VecDeque::new();
45
46        for &node in self.0.keys() {
47            if re.is_match(node) {
48                depths.insert(node, 0);
49                queue.push_back((node, 0));
50            }
51        }
52
53        while let Some((node, depth)) = queue.pop_front() {
54            if let Some(callers) = self.0.get(node) {
55                for &caller in callers {
56                    use std::collections::hash_map::Entry;
57                    if let Entry::Vacant(e) = depths.entry(caller) {
58                        e.insert(depth + 1);
59                        queue.push_back((caller, depth + 1));
60                    }
61                }
62            }
63        }
64
65        depths
66    }
67}