1use std::collections::HashMap;
12use std::collections::HashSet;
13use std::collections::hash_map;
14use std::fmt::Write as _;
15use std::mem;
16use std::ops::Index;
17use std::ops::IndexMut;
18
19use crate::Allocative;
20use crate::global_root::roots;
21use crate::key::Key;
22use crate::visitor::NodeKind;
23use crate::visitor::Visitor;
24use crate::visitor::VisitorImpl;
25
26#[derive(Debug, Default, Clone)]
30pub struct FlameGraph {
31 children: HashMap<Key, FlameGraph>,
32 children_size: usize,
34 node_size: usize,
36}
37
38impl FlameGraph {
39 pub fn total_size(&self) -> usize {
40 self.node_size + self.children_size
41 }
42
43 pub fn add(&mut self, other: FlameGraph) {
45 self.node_size += other.node_size;
46 for (key, child) in other.children {
47 self.add_child(key, child);
48 }
49 }
50
51 pub fn add_child(&mut self, key: Key, child: FlameGraph) {
53 self.children_size += child.total_size();
54 match self.children.entry(key) {
55 hash_map::Entry::Occupied(mut entry) => {
56 entry.get_mut().add(child);
57 }
58 hash_map::Entry::Vacant(entry) => {
59 entry.insert(child);
60 }
61 }
62 }
63
64 pub fn add_self(&mut self, size: usize) {
66 self.node_size += size;
67 }
68
69 fn write_flame_graph_impl(&self, stack: &[&str], w: &mut String) {
70 if self.node_size != 0 {
71 if !stack.is_empty() {
72 writeln!(w, "{} {}", stack.join(";"), self.node_size).unwrap();
73 } else {
74 }
76 }
77 let mut stack = stack.to_vec();
78 let mut children = Vec::from_iter(self.children.iter());
79 children.sort_by_key(|(key, _)| *key);
80 for (key, child) in children {
81 stack.push(key);
82 child.write_flame_graph_impl(&stack, w);
83 stack.pop().unwrap();
84 }
85 }
86
87 pub fn write(&self) -> String {
92 let mut r = String::new();
93 self.write_flame_graph_impl(&[], &mut r);
94 r
95 }
96}
97
98#[derive(Debug)]
99pub struct FlameGraphOutput {
100 flamegraph: FlameGraph,
101 warnings: String,
102}
103
104impl FlameGraphOutput {
105 pub fn flamegraph(&self) -> &FlameGraph {
107 &self.flamegraph
108 }
109
110 pub fn warnings(&self) -> String {
112 self.warnings.clone()
113 }
114}
115
116#[derive(Default, Eq, PartialEq, Clone, Debug)]
117struct TreeData {
118 size: usize,
121 rem_size: isize,
124 unique: bool,
126 children: HashMap<Key, TreeId>,
128}
129
130impl TreeData {
131 fn inline_children_size(&self) -> isize {
132 self.size as isize - self.rem_size
133 }
134}
135
136struct TreeRef<'a> {
137 trees: &'a Trees,
138 tree_id: TreeId,
139}
140
141impl TreeRef<'_> {
142 fn write_flame_graph(&self, stack: &[&str], warnings: &mut String) -> FlameGraph {
143 let mut flame_graph = FlameGraph::default();
144 let tree = &self.trees[self.tree_id];
145 if tree.rem_size > 0 {
146 if stack.is_empty() {
147 } else {
149 flame_graph.node_size = tree.rem_size as usize;
150 }
151 } else if tree.rem_size < 0 && !stack.is_empty() {
152 writeln!(
153 warnings,
154 "Incorrect size declaration for node `{}`, size of self: {}, size of inline children: {}",
155 stack.join(";"),
156 tree.size,
157 tree.inline_children_size()
158 )
159 .unwrap();
160 }
161 let mut children: Vec<(&Key, &TreeId)> = Vec::from_iter(&tree.children);
162 let mut stack = stack.to_vec();
163 children.sort_by_key(|(k, _)| *k);
164 for (key, child) in children {
165 stack.push(key);
166 let child = TreeRef {
167 trees: self.trees,
168 tree_id: *child,
169 };
170 let child_framegraph = child.write_flame_graph(&stack, warnings);
171 flame_graph.add_child(key.clone(), child_framegraph);
172 stack.pop().unwrap();
173 }
174 flame_graph
175 }
176
177 fn to_flame_graph(&self) -> (FlameGraph, String) {
178 let mut warnings = String::new();
179 let flame_graph = self.write_flame_graph(&[], &mut warnings);
180 (flame_graph, warnings)
181 }
182}
183
184#[derive(Debug, Eq, PartialEq)]
185struct Tree {
186 trees: Trees,
187 tree_id: TreeId,
188}
189
190impl Tree {
191 fn as_ref(&self) -> TreeRef<'_> {
192 TreeRef {
193 trees: &self.trees,
194 tree_id: self.tree_id,
195 }
196 }
197
198 fn to_flame_graph(&self) -> (FlameGraph, String) {
199 self.as_ref().to_flame_graph()
200 }
201}
202
203#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
204struct TreeId(usize);
205
206#[derive(Default, Clone, Debug, Eq, PartialEq)]
207struct Trees {
208 trees: Vec<TreeData>,
209}
210
211impl Trees {
212 fn new_tree(&mut self) -> TreeId {
213 let id = TreeId(self.trees.len());
214 self.trees.push(TreeData::default());
215 id
216 }
217}
218
219impl Index<TreeId> for Trees {
220 type Output = TreeData;
221
222 fn index(&self, index: TreeId) -> &Self::Output {
223 &self.trees[index.0]
224 }
225}
226
227impl IndexMut<TreeId> for Trees {
228 fn index_mut(&mut self, index: TreeId) -> &mut Self::Output {
229 &mut self.trees[index.0]
230 }
231}
232
233#[derive(Clone, Debug)]
234struct TreeStack {
235 stack: Vec<TreeId>,
236 tree: TreeId,
237}
238
239struct TreeStackRef<'t, 's> {
240 trees: &'t mut Trees,
241 stack: &'s mut TreeStack,
242}
243
244impl<'t> TreeStackRef<'t, '_> {
245 fn current_data(&'t mut self) -> &'t mut TreeData {
246 &mut self.trees[self.stack.tree]
247 }
248
249 fn down(&mut self, key: Key) {
250 self.stack.stack.push(self.stack.tree);
251 let next_tree_id = TreeId(self.trees.trees.len());
252 let child = match self.trees[self.stack.tree].children.entry(key) {
253 hash_map::Entry::Occupied(e) => *e.get(),
254 hash_map::Entry::Vacant(e) => {
255 e.insert(next_tree_id);
256 let child = self.trees.new_tree();
257 assert_eq!(child, next_tree_id);
258 child
259 }
260 };
261 self.stack.tree = child;
262 }
263
264 #[must_use]
265 fn up(&mut self) -> bool {
266 if let Some(pop) = self.stack.stack.pop() {
267 self.stack.tree = pop;
268 true
269 } else {
270 false
271 }
272 }
273}
274
275#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
276struct VisitedSharedPointer(*const ());
277unsafe impl Send for VisitedSharedPointer {}
278
279#[derive(Debug)]
310pub struct FlameGraphBuilder {
311 visited_shared: HashSet<VisitedSharedPointer>,
313 trees: Trees,
315 current: TreeStack,
317 shared: Vec<TreeStack>,
319 root: TreeId,
321 entered_root_visitor: bool,
323}
324
325fn _assert_flame_graph_builder_is_send() {
326 fn assert_send<T: Send>() {}
327 assert_send::<FlameGraphBuilder>();
328}
329
330impl Default for FlameGraphBuilder {
331 fn default() -> FlameGraphBuilder {
332 let mut trees = Trees::default();
333 let root = trees.new_tree();
334 FlameGraphBuilder {
335 trees,
336 visited_shared: HashSet::new(),
337 current: TreeStack {
338 stack: Vec::new(),
339 tree: root,
340 },
341 shared: Vec::new(),
342 root,
343 entered_root_visitor: false,
344 }
345 }
346}
347
348impl FlameGraphBuilder {
349 pub fn root_visitor(&mut self) -> Visitor<'_> {
350 assert!(!self.entered_root_visitor);
351 self.entered_root_visitor = true;
352 Visitor {
353 visitor: self,
354 node_kind: NodeKind::Root,
355 }
356 }
357
358 pub fn visit_root(&mut self, root: &dyn Allocative) {
360 let mut visitor = self.root_visitor();
361 root.visit(&mut visitor);
362 visitor.exit();
363 }
364
365 pub fn visit_global_roots(&mut self) {
368 for root in roots() {
369 self.visit_root(root);
370 }
371 }
372
373 fn finish_impl(mut self) -> Tree {
374 assert!(self.shared.is_empty());
375 assert!(self.current.stack.is_empty());
376 assert!(!self.entered_root_visitor);
377 Self::update_sizes(self.root, &mut self.trees);
378 Tree {
379 trees: self.trees,
380 tree_id: self.root,
381 }
382 }
383
384 pub fn finish(self) -> FlameGraphOutput {
386 let tree = self.finish_impl();
387 let (flamegraph, warnings) = tree.to_flame_graph();
388 FlameGraphOutput {
389 flamegraph,
390 warnings,
391 }
392 }
393
394 pub fn finish_and_write_flame_graph(self) -> String {
396 self.finish().flamegraph.write()
397 }
398
399 fn update_sizes(tree_id: TreeId, trees: &mut Trees) {
400 let tree = &mut trees[tree_id];
401 for child in tree.children.values().copied().collect::<Vec<_>>() {
402 Self::update_sizes(child, trees);
403 }
404 let tree = &mut trees[tree_id];
405 let children_size = if tree.unique {
406 0
407 } else {
408 let tree = &trees[tree_id];
409 tree.children
410 .values()
411 .map(|child| trees[*child].size)
412 .sum::<usize>()
413 };
414 let tree = &mut trees[tree_id];
415 let size = tree.size;
416 tree.rem_size = (size as isize).saturating_sub(children_size as isize);
417 }
418
419 fn current(&mut self) -> TreeStackRef<'_, '_> {
420 TreeStackRef {
421 trees: &mut self.trees,
422 stack: &mut self.current,
423 }
424 }
425
426 fn exit_impl(&mut self) {
427 assert!(self.entered_root_visitor);
428
429 let up = self.current().up();
430 if !up {
431 if let Some(shared) = self.shared.pop() {
432 self.current = shared;
433 assert!(self.current().up());
434 } else {
435 self.entered_root_visitor = false;
436 }
437 }
438 }
439}
440
441impl VisitorImpl for FlameGraphBuilder {
442 fn enter_inline_impl(&mut self, name: Key, size: usize, _parent: NodeKind) {
443 self.current().down(name);
444 self.current().current_data().size += size;
445 }
446
447 fn enter_unique_impl(&mut self, name: Key, size: usize, _parent: NodeKind) {
448 self.current().down(name);
449 self.current().current_data().size += size;
450 self.current().current_data().unique = true;
453 }
454
455 fn enter_shared_impl(
456 &mut self,
457 name: Key,
458 size: usize,
459 ptr: *const (),
460 _parent: NodeKind,
461 ) -> bool {
462 self.current().down(name);
463 self.current().current_data().size += size;
464
465 if !self.visited_shared.insert(VisitedSharedPointer(ptr)) {
466 self.exit_impl();
467 return false;
468 }
469
470 self.shared.push(mem::replace(
471 &mut self.current,
472 TreeStack {
473 stack: Vec::new(),
474 tree: self.root,
475 },
476 ));
477 true
478 }
479
480 fn exit_inline_impl(&mut self) {
481 self.exit_impl();
482 }
483
484 fn exit_unique_impl(&mut self) {
485 self.exit_impl();
486 }
487
488 fn exit_shared_impl(&mut self) {
489 self.exit_impl();
490 }
491
492 fn exit_root_impl(&mut self) {
493 self.exit_impl();
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use crate::FlameGraph;
500 use crate::flamegraph::FlameGraphBuilder;
501 use crate::flamegraph::Tree;
502 use crate::flamegraph::Trees;
503 use crate::key::Key;
504
505 #[test]
506 fn test_empty() {
507 let mut fg = FlameGraphBuilder::default();
508 fg.root_visitor().exit();
509 let tree = fg.finish_impl();
510
511 let mut expected_trees = Trees::default();
512 let expected_id = expected_trees.new_tree();
513 let expected = Tree {
514 trees: expected_trees,
515 tree_id: expected_id,
516 };
517
518 assert_eq!(expected, tree);
519 assert_eq!("", tree.to_flame_graph().0.write());
520 }
521
522 #[test]
523 fn test_simple() {
524 let mut fg = FlameGraphBuilder::default();
525 fg.root_visitor().visit_simple(Key::new("a"), 10);
526 let tree = fg.finish_impl();
527
528 let mut expected = Trees::default();
529 let expected_root = expected.new_tree();
530 let expected_child = expected.new_tree();
531 expected[expected_root].size = 0;
532 expected[expected_root].rem_size = -10;
533 expected[expected_root]
534 .children
535 .insert(Key::new("a"), expected_child);
536 expected[expected_child].size = 10;
537 expected[expected_child].rem_size = 10;
538 let expected = Tree {
539 trees: expected,
540 tree_id: expected_root,
541 };
542 assert_eq!(expected, tree);
543 assert_eq!("a 10\n", tree.to_flame_graph().0.write());
544 }
545
546 #[test]
547 fn test_unique() {
548 let mut fg = FlameGraphBuilder::default();
549 let mut visitor = fg.root_visitor();
550 let mut s = visitor.enter(Key::new("Struct"), 10);
551 s.visit_simple(Key::new("a"), 3);
552 let mut un = s.enter_unique(Key::new("p"), 6);
553 un.visit_simple(Key::new("x"), 13);
554 un.exit();
555 s.exit();
556 visitor.exit();
557
558 let tree = fg.finish_impl();
559
560 assert_eq!(
561 "\
562 Struct 1\n\
563 Struct;a 3\n\
564 Struct;p 6\n\
565 Struct;p;x 13\n\
566 ",
567 tree.to_flame_graph().0.write(),
568 "{tree:#?}",
569 );
570 }
571
572 #[test]
573 fn test_shared() {
574 let p = 10;
575
576 let mut fg = FlameGraphBuilder::default();
577 let mut visitor = fg.root_visitor();
578
579 for _ in 0..2 {
580 let mut s = visitor.enter(Key::new("Struct"), 10);
581 s.visit_simple(Key::new("a"), 3);
582 {
583 let sh = s.enter_shared(Key::new("p"), 6, &p as *const i32 as *const ());
584 if let Some(mut sh) = sh {
585 sh.visit_simple(Key::new("Shared"), 13);
586 sh.exit();
587 }
588 }
589 s.exit();
590 }
591
592 visitor.exit();
593
594 let tree = fg.finish_impl();
595
596 assert_eq!(
597 "\
598 Shared 13\n\
599 Struct 2\n\
600 Struct;a 6\n\
601 Struct;p 12\n\
602 ",
603 tree.to_flame_graph().0.write(),
604 "{tree:#?}",
605 );
606 }
607
608 #[test]
609 fn test_inline_children_too_large() {
610 let mut fg = FlameGraphBuilder::default();
611 let mut visitor = fg.root_visitor();
612 let mut child_visitor = visitor.enter(Key::new("a"), 10);
613 child_visitor.visit_simple(Key::new("b"), 13);
614 child_visitor.exit();
615 visitor.exit();
616 let output = fg.finish();
617 assert_eq!("a;b 13\n", output.flamegraph().write());
618 assert_eq!(
619 "Incorrect size declaration for node `a`, size of self: 10, size of inline children: 13\n",
620 output.warnings()
621 );
622 }
623
624 #[test]
625 fn test_flamegraph_add() {
626 let mut a = FlameGraph::default();
627
628 let mut b_1 = FlameGraph::default();
629 b_1.add_self(10);
630
631 let mut b = FlameGraph::default();
632 b.add_child(Key::new("x"), b_1);
633
634 a.add(b);
635 assert_eq!(10, a.total_size());
636 }
637}