rapx/analysis/path_analysis/
mod.rs1pub mod default;
2pub mod graph;
3
4use crate::utils::source::get_fn_name_byid;
5use rustc_hir::def_id::DefId;
6use std::fmt::{self, Display};
7
8use crate::compat::FxHashMap;
9use graph::PathGraph;
10
11pub fn format_path_annotated(path: &[usize], graph: &PathGraph<'_>) -> String {
16 let blocks: Vec<String> = path
17 .iter()
18 .map(|&b| {
19 if graph.is_cleanup_block(b) {
20 format!("{}*", b)
21 } else {
22 b.to_string()
23 }
24 })
25 .collect();
26 format!("[{}]", blocks.join(", "))
27}
28
29#[derive(Debug, Clone)]
45pub struct PathTree {
46 root: Option<PathNode>,
47 len: usize,
48}
49
50#[derive(Debug, Clone)]
57pub struct PathNode {
58 pub block: usize,
59 pub children: Vec<PathNode>,
60 pub is_path_end: bool,
61}
62
63impl PathNode {
64 fn from_path(path: &[usize]) -> Self {
65 let mut node = PathNode {
66 block: path[0],
67 children: Vec::new(),
68 is_path_end: path.len() == 1,
69 };
70 if path.len() > 1 {
71 node.children.push(PathNode::from_path(&path[1..]));
72 }
73 node
74 }
75}
76
77impl PathTree {
78 pub fn new() -> Self {
79 PathTree { root: None, len: 0 }
80 }
81
82 pub fn len(&self) -> usize {
83 self.len
84 }
85
86 pub fn is_empty(&self) -> bool {
87 self.len == 0
88 }
89
90 pub fn root(&self) -> Option<&PathNode> {
91 self.root.as_ref()
92 }
93
94 pub fn insert(&mut self, path: &[usize]) -> bool {
97 if path.is_empty() {
98 return false;
99 }
100
101 match &mut self.root {
102 None => {
103 self.root = Some(PathNode::from_path(path));
104 self.len = 1;
105 true
106 }
107 Some(root) => {
108 if root.block != path[0] {
109 return false;
110 }
111 if Self::insert_into(root, &path[1..]) {
112 self.len += 1;
113 true
114 } else {
115 false
116 }
117 }
118 }
119 }
120
121 fn insert_into(node: &mut PathNode, suffix: &[usize]) -> bool {
122 if suffix.is_empty() {
123 if node.is_path_end {
124 return false;
125 }
126 node.is_path_end = true;
127 return true;
128 }
129
130 let target = suffix[0];
131 for child in &mut node.children {
132 if child.block == target {
133 return Self::insert_into(child, &suffix[1..]);
134 }
135 }
136
137 node.children.push(PathNode::from_path(suffix));
138 true
139 }
140
141 pub fn contains(&self, path: &[usize]) -> bool {
143 let mut node = match self.root.as_ref() {
144 Some(n) => n,
145 None => return false,
146 };
147 if node.block != path[0] {
148 return false;
149 }
150 for &block in &path[1..] {
151 node = match node.children.iter().find(|c| c.block == block) {
152 Some(n) => n,
153 None => return false,
154 };
155 }
156 node.is_path_end
157 }
158
159 pub fn iter(&self) -> PathTreeIter<'_> {
161 PathTreeIter {
162 stack: self
163 .root
164 .as_ref()
165 .map(|r| vec![(r, vec![r.block])])
166 .unwrap_or_default(),
167 }
168 }
169
170 pub fn to_vecs(&self) -> Vec<Vec<usize>> {
172 self.iter().collect()
173 }
174
175 pub fn walk_prefixes<F>(&self, target_block: usize, f: &mut F) -> Result<(), ()>
183 where
184 F: FnMut(&[usize]) -> bool,
185 {
186 let Some(root) = self.root.as_ref() else {
187 return Ok(());
188 };
189 let mut path = Vec::new();
190 Self::walk_prefixes_impl(root, &mut path, target_block, false, f)
191 }
192
193 pub fn walk_all_prefixes<F>(&self, target_block: usize, f: &mut F) -> Result<(), ()>
197 where
198 F: FnMut(&[usize]) -> bool,
199 {
200 let Some(root) = self.root.as_ref() else {
201 return Ok(());
202 };
203 let mut path = Vec::new();
204 Self::walk_prefixes_impl(root, &mut path, target_block, true, f)
205 }
206
207 fn walk_prefixes_impl<F>(
208 node: &PathNode,
209 path: &mut Vec<usize>,
210 target_block: usize,
211 continue_past_target: bool,
212 f: &mut F,
213 ) -> Result<(), ()>
214 where
215 F: FnMut(&[usize]) -> bool,
216 {
217 path.push(node.block);
218 if node.block == target_block {
219 let cont = f(path);
220 if !cont {
221 path.pop();
222 return Err(());
223 }
224 if !continue_past_target {
225 path.pop();
226 return Ok(());
227 }
228 }
229 for child in &node.children {
230 Self::walk_prefixes_impl(child, path, target_block, continue_past_target, f)?;
231 }
232 path.pop();
233 Ok(())
234 }
235}
236
237impl Default for PathTree {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243pub struct PathTreeIter<'a> {
249 stack: Vec<(&'a PathNode, Vec<usize>)>,
250}
251
252impl<'a> Iterator for PathTreeIter<'a> {
253 type Item = Vec<usize>;
254
255 fn next(&mut self) -> Option<Self::Item> {
256 loop {
257 let (node, path) = self.stack.pop()?;
258 for child in node.children.iter().rev() {
259 let mut child_path = path.clone();
260 child_path.push(child.block);
261 self.stack.push((child, child_path));
262 }
263 if node.is_path_end {
264 return Some(path);
265 }
266 }
267 }
268}
269
270pub struct PathMapWrapper<'a, 'tcx>(
273 pub FxHashMap<DefId, PathTree>,
274 pub &'a FxHashMap<DefId, PathGraph<'tcx>>,
275);
276
277impl Display for PathMapWrapper<'_, '_> {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 writeln!(f, "=== Print path analysis results ===")?;
280 for (def_id, paths) in &self.0 {
281 let fn_name = get_fn_name_byid(def_id);
282 if fn_name.contains("__raw_ptr_deref_dummy") {
283 continue;
284 }
285 writeln!(f, "Function: {:?}:", fn_name)?;
286 let graph = self.1.get(def_id);
287 for path in paths.iter() {
288 if let Some(g) = graph {
289 writeln!(f, " Path {}", format_path_annotated(&path, g))?;
290 } else {
291 writeln!(f, " Path {:?}", path)?;
292 }
293 }
294 }
295 Ok(())
296 }
297}