1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use uuid::Uuid;
6
7use super::nodes::{ParserNode, RgbColor};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Tree {
12 pub nodes: HashMap<Uuid, TreeNode>,
14 pub roots: Vec<Uuid>,
16 pub total_samples: u64,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct TreeNode {
23 pub id: Uuid,
25 pub parent: Option<Uuid>,
27 pub children: Vec<Uuid>,
29 pub descendants: Vec<Uuid>,
31
32 pub function_name: String,
35 pub raw_signature: String,
37
38 pub cpu_percent: f64,
41 pub sample_count: u64,
43 pub self_sample_count: u64,
45
46 pub color: Option<RgbColor>,
49
50 pub source_library: Option<String>,
52 pub is_alloc: bool,
53}
54
55impl Tree {
56 pub fn from_parser_nodes(parser_nodes: Vec<ParserNode>, _total_samples: u64) -> Self {
58 let mut tree = Tree {
59 nodes: HashMap::new(),
60 roots: Vec::new(),
61 total_samples: 0, };
63
64 let mut node_map: HashMap<Uuid,String> = HashMap::new();
66
67 for parser_node in &parser_nodes {
68 let id = Uuid::new_v4();
69 let tree_node = TreeNode {
70 id,
71 parent: None, children: Vec::new(), descendants: Vec::new(), function_name: parser_node.function_name.clone(),
75 raw_signature: parser_node.raw_signature.clone(),
76 cpu_percent: parser_node.cpu_percent,
77 sample_count: parser_node.sample_count,
78 self_sample_count: 0, color: parser_node.color.clone(),
80 source_library: None, is_alloc: false, };
83
84 node_map.insert(id, parser_node.unique_key.clone());
85 tree.nodes.insert(id, tree_node);
86 }
87
88 tree.build_relationships(parser_nodes, &node_map);
90
91 tree.calculate_self_sample_counts();
93
94 tree.calculate_total_samples();
96
97 tree
98 }
99
100 fn build_relationships(&mut self, parser_nodes: Vec<ParserNode>, node_map: &HashMap<Uuid, String>) {
102 let key_to_uuid: HashMap<&String, Uuid> = node_map.iter().map(|(uuid, key)| (key, *uuid)).collect();
104
105 let mut nodes_by_y: HashMap<u32, Vec<&ParserNode>> = HashMap::new();
107 for node in &parser_nodes {
108 let y_level = (node.y_position as u32 / 16) * 16; nodes_by_y.entry(y_level).or_default().push(node);
110 }
111
112 let mut y_levels: Vec<u32> = nodes_by_y.keys().copied().collect();
113 y_levels.sort();
114
115 log::debug!("Found {} Y levels: {:?}", y_levels.len(), y_levels);
116
117 let mut assigned_as_child = std::collections::HashSet::new();
119
120 for (level_idx, ¤t_y) in y_levels.iter().enumerate() {
122 let empty_vec = vec![];
123 let current_nodes = nodes_by_y.get(¤t_y).unwrap_or(&empty_vec);
124
125 if let Some(&parent_y) = y_levels.get(level_idx + 1) {
127 let empty_parent_vec = vec![];
128 let parent_nodes = nodes_by_y.get(&parent_y).unwrap_or(&empty_parent_vec);
129
130 for child_node in current_nodes {
132 let child_id = match key_to_uuid.get(&child_node.unique_key) {
133 Some(&id) => id,
134 None => continue,
135 };
136
137 if assigned_as_child.contains(&child_id) {
139 continue;
140 }
141
142 for parent_node in parent_nodes {
144 if self.x_ranges_overlap(parent_node, child_node) {
145 let parent_id = match key_to_uuid.get(&parent_node.unique_key) {
146 Some(&id) => id,
147 None => continue,
148 };
149
150 if let Some(child_tree_node) = self.nodes.get_mut(&child_id) {
152 child_tree_node.parent = Some(parent_id);
153 assigned_as_child.insert(child_id);
154 }
155
156 if let Some(parent_tree_node) = self.nodes.get_mut(&parent_id) {
158 parent_tree_node.children.push(child_id);
159 }
160
161 break; }
163 }
164 }
165 }
166 }
167
168 self.roots.clear();
170 for (&node_id, node) in &self.nodes {
171 if !assigned_as_child.contains(&node_id) {
172 self.roots.push(node_id);
173 log::debug!("Root: {}", node.function_name);
174 }
175 }
176
177 self.build_descendants();
179
180 log::debug!("Relationships complete: {} roots, {} nodes with children",
181 self.roots.len(),
182 self.nodes.values().filter(|n| !n.children.is_empty()).count());
183 }
184
185 fn build_descendants(&mut self) {
187 let mut descendants_map: HashMap<Uuid, Vec<Uuid>> = HashMap::new();
189
190 for &node_id in self.nodes.keys() {
191 let descendants = self.collect_all_descendants(node_id);
192 descendants_map.insert(node_id, descendants);
193 }
194
195 for (node_id, descendants) in descendants_map {
197 if let Some(node) = self.nodes.get_mut(&node_id) {
198 node.descendants = descendants;
199 }
200 }
201 }
202
203 fn collect_all_descendants(&self, node_id: Uuid) -> Vec<Uuid> {
205 let mut descendants = Vec::new();
206
207 if let Some(node) = self.nodes.get(&node_id) {
208 for &child_id in &node.children {
209 descendants.push(child_id);
210 descendants.extend(self.collect_all_descendants(child_id));
212 }
213 }
214
215 descendants
216 }
217
218 pub fn calculate_self_sample_counts(&mut self) {
221 for &root_id in self.roots.clone().iter() {
223 self.calculate_self_sample_counts_recursive(root_id);
224 }
225 }
226
227 pub fn calculate_total_samples(&mut self) {
231 self.total_samples = self.roots
232 .iter()
233 .filter_map(|&root_id| self.nodes.get(&root_id))
234 .map(|node| node.sample_count)
235 .sum();
236
237 log::debug!("Calculated total_samples from {} root nodes: {}",
238 self.roots.len(), self.total_samples);
239 }
240
241 fn calculate_self_sample_counts_recursive(&mut self, node_id: Uuid) {
243 let child_ids: Vec<Uuid> = if let Some(node) = self.nodes.get(&node_id) {
245 node.children.clone()
246 } else {
247 return;
248 };
249
250 for child_id in child_ids {
251 self.calculate_self_sample_counts_recursive(child_id);
252 }
253
254 if let Some(node) = self.nodes.get(&node_id) {
256 let inclusive_count = node.sample_count;
257
258 let children_inclusive_sum: u64 = node.children
260 .iter()
261 .filter_map(|&child_id| self.nodes.get(&child_id))
262 .map(|child| child.sample_count)
263 .sum();
264
265 let self_count = inclusive_count.saturating_sub(children_inclusive_sum);
266
267 if let Some(node_mut) = self.nodes.get_mut(&node_id) {
269 node_mut.self_sample_count = self_count;
270 }
271 }
272 }
273
274 fn x_ranges_overlap(&self, parent: &ParserNode, child: &ParserNode) -> bool {
276 let parent_start = parent.x_position;
277 let parent_end = parent.x_position + parent.width;
278 let child_start = child.x_position;
279 let child_end = child.x_position + child.width;
280
281 child_start >= parent_start && child_end <= parent_end
283 }
284
285
286 pub fn get_node(&self, id: Uuid) -> Option<&TreeNode> {
288 self.nodes.get(&id)
289 }
290
291 pub fn get_node_mut(&mut self, id: Uuid) -> Option<&mut TreeNode> {
293 self.nodes.get_mut(&id)
294 }
295
296 pub fn get_roots(&self) -> Vec<&TreeNode> {
298 self.roots
299 .iter()
300 .filter_map(|&id| self.nodes.get(&id))
301 .collect()
302 }
303
304 pub fn get_children(&self, node_id: Uuid) -> Vec<&TreeNode> {
306 if let Some(node) = self.nodes.get(&node_id) {
307 node.children
308 .iter()
309 .filter_map(|&child_id| self.nodes.get(&child_id))
310 .collect()
311 } else {
312 Vec::new()
313 }
314 }
315
316 pub fn get_parent(&self, node_id: Uuid) -> Option<&TreeNode> {
318 if let Some(node) = self.nodes.get(&node_id) {
319 node.parent.and_then(|parent_id| self.nodes.get(&parent_id))
320 } else {
321 None
322 }
323 }
324
325 pub fn node_count(&self) -> usize {
327 self.nodes.len()
328 }
329}
330
331impl TreeNode {
332 pub fn is_root(&self) -> bool {
334 self.parent.is_none()
335 }
336
337 pub fn is_leaf(&self) -> bool {
339 self.children.is_empty()
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346 use crate::parser::nodes::FlamegraphParser;
347
348 #[test]
390 fn test_tree_with_real_flamegraph() {
391 crate::init_test_logging();
392 let svg_content = include_str!("test_flamegraph.svg");
393 let mut parser = FlamegraphParser::default();
394 let parser_nodes = parser.parse_svg(svg_content).expect("Should parse SVG");
395
396 let tree = Tree::from_parser_nodes(parser_nodes, 0);
397
398 log::debug!("Created tree with {} nodes from real flamegraph", tree.node_count());
399 log::debug!("Number of root nodes: {}", tree.roots.len());
400
401 log::debug!("Root node IDs: {:?}", tree.roots);
402
403 log::debug!("{}", tree);
405
406 assert!(tree.node_count() > 0);
408 let nodes_with_children = tree
412 .nodes
413 .values()
414 .filter(|node| !node.children.is_empty())
415 .count();
416
417 log::debug!("Nodes with children: {}", nodes_with_children);
418
419 assert!(nodes_with_children > 0);
421 }
422}