From 1cbd59f5bf8d24d374666604ebc5c465044d72fa Mon Sep 17 00:00:00 2001 From: data-pup Date: Sat, 12 May 2018 15:30:00 -0700 Subject: [PATCH] This commit solves #57. The `twiggy paths` will now print all paths if no arguments are given. In adittion, there is now a descending flag that can be used to move down rather than up the retaining paths. --- analyze/analyze.rs | 64 ++++++++++---- opt/definitions.rs | 16 ++++ .../tests/expectations/paths_test_called_once | 7 ++ .../expectations/paths_test_called_twice | 13 +++ .../expectations/paths_test_default_output | 81 ++++++++++++++++++ .../paths_test_default_output_desc | 30 +++++++ .../paths_test_default_output_desc_with_depth | 15 ++++ .../{paths_json => paths_wee_alloc_json} | 0 twiggy/tests/fixtures/paths_test.wasm | Bin 0 -> 144 bytes twiggy/tests/fixtures/paths_test.wat | 50 +++++++++++ twiggy/tests/tests.rs | 62 +++++++++++--- 11 files changed, 309 insertions(+), 29 deletions(-) create mode 100644 twiggy/tests/expectations/paths_test_called_once create mode 100644 twiggy/tests/expectations/paths_test_called_twice create mode 100644 twiggy/tests/expectations/paths_test_default_output create mode 100644 twiggy/tests/expectations/paths_test_default_output_desc create mode 100644 twiggy/tests/expectations/paths_test_default_output_desc_with_depth rename twiggy/tests/expectations/{paths_json => paths_wee_alloc_json} (100%) create mode 100644 twiggy/tests/fixtures/paths_test.wasm create mode 100644 twiggy/tests/fixtures/paths_test.wat diff --git a/analyze/analyze.rs b/analyze/analyze.rs index 13c259cf..14d1f74e 100644 --- a/analyze/analyze.rs +++ b/analyze/analyze.rs @@ -412,7 +412,11 @@ impl traits::Emit for Paths { label.push_str(" "); } if depth > 0 { - label.push_str(" ⬑ "); + if opts.descending() { + label.push_str(" ↳ "); + } else { + label.push_str(" ⬑ "); + } } label.push_str(item.name()); @@ -432,12 +436,21 @@ impl traits::Emit for Paths { ]); seen.insert(id); - for (i, caller) in items.predecessors(id).enumerate() { - if i > 0 { - *paths += 1; + + if opts.descending() { + for callee in items.neighbors(id) { + *paths += 1; // FIXUP: Should this be wrapped in a conditional as above? + recursive_callers(items, seen, table, depth + 1, &mut paths, &opts, callee); + } + } else { + for (i, caller) in items.predecessors(id).enumerate() { + if i > 0 { + *paths += 1; + } + recursive_callers(items, seen, table, depth + 1, &mut paths, &opts, caller); } - recursive_callers(items, seen, table, depth + 1, &mut paths, &opts, caller); } + seen.remove(&id); } @@ -518,20 +531,39 @@ impl traits::Emit for Paths { /// Find all retaining paths for the given items. pub fn paths(items: &mut ir::Items, opts: &opt::Paths) -> Result, traits::Error> { - items.compute_predecessors(); + if !opts.descending() { + items.compute_predecessors(); + } - let mut paths = Paths { - items: Vec::with_capacity(opts.functions().len()), - opts: opts.clone(), + let functions: Vec = match opts.functions().is_empty() { + true => { + if opts.descending() { + let mut roots: Vec<_> = items + .neighbors(items.meta_root()) + .map(|id| &items[id]) + .collect(); + roots.sort_by(|a, b| b.size().cmp(&a.size())); + roots.into_iter().map(|item| item.id()).collect() + } else { + let mut sorted_items: Vec<_> = items + .iter() + .filter(|item| item.id() != items.meta_root()) + .collect(); + sorted_items.sort_by(|a, b| b.size().cmp(&a.size())); + sorted_items.iter().map(|item| item.id()).collect() + } + } + false => opts.functions() + .iter() + .filter_map(|s| items.get_item_by_name(s)) + .map(|item| item.id()) + .collect(), }; - let functions: BTreeSet<_> = opts.functions().iter().map(|s| s.as_str()).collect(); - - for item in items.iter() { - if functions.contains(item.name()) { - paths.items.push(item.id()); - } - } + let paths = Paths { + items: functions, + opts: opts.clone(), + }; Ok(Box::new(paths) as Box) } diff --git a/opt/definitions.rs b/opt/definitions.rs index b7a57410..a0cd3915 100644 --- a/opt/definitions.rs +++ b/opt/definitions.rs @@ -220,6 +220,10 @@ pub struct Paths { /// The maximum number of paths, regardless of depth in the tree, to display. #[structopt(short = "r", default_value = "10")] max_paths: u32, + + /// This direction of the path traversal. + #[structopt(long = "descending")] + descending: bool, } impl Default for Paths { @@ -235,6 +239,7 @@ impl Default for Paths { functions: Default::default(), max_depth: 10, max_paths: 10, + descending: false, } } } @@ -271,6 +276,11 @@ impl Paths { self.max_paths } + /// The direction in which the call paths are traversed. + pub fn descending(&self) -> bool { + self.descending + } + /// Set the maximum depth to print the paths. pub fn set_max_depth(&mut self, max_depth: u32) { self.max_depth = max_depth; @@ -280,6 +290,12 @@ impl Paths { pub fn set_max_paths(&mut self, max_paths: u32) { self.max_paths = max_paths; } + + /// Set the call path traversal direction. + pub fn set_descending(&mut self, descending: bool) { + self.descending = descending; + } + } /// List the generic function monomorphizations that are contributing to diff --git a/twiggy/tests/expectations/paths_test_called_once b/twiggy/tests/expectations/paths_test_called_once new file mode 100644 index 00000000..7e6208ab --- /dev/null +++ b/twiggy/tests/expectations/paths_test_called_once @@ -0,0 +1,7 @@ + Shallow Bytes │ Shallow % │ Retaining Paths +───────────────┼───────────┼──────────────────────────────── + 5 ┊ 3.47% ┊ calledOnce + ┊ ┊ ⬑ func[0] + ┊ ┊ ⬑ woof + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" diff --git a/twiggy/tests/expectations/paths_test_called_twice b/twiggy/tests/expectations/paths_test_called_twice new file mode 100644 index 00000000..de51b0a6 --- /dev/null +++ b/twiggy/tests/expectations/paths_test_called_twice @@ -0,0 +1,13 @@ + Shallow Bytes │ Shallow % │ Retaining Paths +───────────────┼───────────┼──────────────────────────────────────── + 5 ┊ 3.47% ┊ calledTwice + ┊ ┊ ⬑ func[1] + ┊ ┊ ⬑ bark + ┊ ┊ ⬑ func[2] + ┊ ┊ ⬑ export "bark" + ┊ ┊ ⬑ awoo + ┊ ┊ ⬑ func[4] + ┊ ┊ ⬑ export "awoo" + ┊ ┊ ⬑ woof + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" diff --git a/twiggy/tests/expectations/paths_test_default_output b/twiggy/tests/expectations/paths_test_default_output new file mode 100644 index 00000000..0f6ce735 --- /dev/null +++ b/twiggy/tests/expectations/paths_test_default_output @@ -0,0 +1,81 @@ + Shallow Bytes │ Shallow % │ Retaining Paths +───────────────┼───────────┼──────────────────────────────────────── + 44 ┊ 30.56% ┊ "function names" subsection + 8 ┊ 5.56% ┊ woof + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" + 7 ┊ 4.86% ┊ export "awoo" + 7 ┊ 4.86% ┊ export "bark" + 7 ┊ 4.86% ┊ export "woof" + 5 ┊ 3.47% ┊ calledOnce + ┊ ┊ ⬑ func[0] + ┊ ┊ ⬑ woof + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" + 5 ┊ 3.47% ┊ calledTwice + ┊ ┊ ⬑ func[1] + ┊ ┊ ⬑ bark + ┊ ┊ ⬑ func[2] + ┊ ┊ ⬑ export "bark" + ┊ ┊ ⬑ awoo + ┊ ┊ ⬑ func[4] + ┊ ┊ ⬑ export "awoo" + ┊ ┊ ⬑ woof + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" + 5 ┊ 3.47% ┊ bark + ┊ ┊ ⬑ func[2] + ┊ ┊ ⬑ export "bark" + ┊ ┊ ⬑ awoo + ┊ ┊ ⬑ func[4] + ┊ ┊ ⬑ export "awoo" + 5 ┊ 3.47% ┊ awoo + ┊ ┊ ⬑ func[4] + ┊ ┊ ⬑ export "awoo" + 4 ┊ 2.78% ┊ type[0] + ┊ ┊ ⬑ func[0] + ┊ ┊ ⬑ woof + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" + ┊ ┊ ⬑ func[1] + ┊ ┊ ⬑ bark + ┊ ┊ ⬑ func[2] + ┊ ┊ ⬑ export "bark" + ┊ ┊ ⬑ awoo + ┊ ┊ ⬑ func[4] + ┊ ┊ ⬑ export "awoo" + ┊ ┊ ⬑ woof + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" + ┊ ┊ ⬑ func[2] + ┊ ┊ ⬑ export "bark" + ┊ ┊ ⬑ awoo + ┊ ┊ ⬑ func[4] + ┊ ┊ ⬑ export "awoo" + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" + ┊ ┊ ⬑ func[4] + ┊ ┊ ⬑ export "awoo" + 1 ┊ 0.69% ┊ func[0] + ┊ ┊ ⬑ woof + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" + 1 ┊ 0.69% ┊ func[1] + ┊ ┊ ⬑ bark + ┊ ┊ ⬑ func[2] + ┊ ┊ ⬑ export "bark" + ┊ ┊ ⬑ awoo + ┊ ┊ ⬑ func[4] + ┊ ┊ ⬑ export "awoo" + ┊ ┊ ⬑ woof + ┊ ┊ ⬑ func[3] + ┊ ┊ ⬑ export "woof" + 1 ┊ 0.69% ┊ func[2] + ┊ ┊ ⬑ export "bark" + ┊ ┊ ⬑ awoo + ┊ ┊ ⬑ func[4] + ┊ ┊ ⬑ export "awoo" + 1 ┊ 0.69% ┊ func[3] + ┊ ┊ ⬑ export "woof" + 1 ┊ 0.69% ┊ func[4] + ┊ ┊ ⬑ export "awoo" diff --git a/twiggy/tests/expectations/paths_test_default_output_desc b/twiggy/tests/expectations/paths_test_default_output_desc new file mode 100644 index 00000000..8ae2cee1 --- /dev/null +++ b/twiggy/tests/expectations/paths_test_default_output_desc @@ -0,0 +1,30 @@ + Shallow Bytes │ Shallow % │ Retaining Paths +───────────────┼───────────┼────────────────────────────────────── + 44 ┊ 30.56% ┊ "function names" subsection + 7 ┊ 4.86% ┊ export "awoo" + ┊ ┊ ↳ func[4] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ awoo + ┊ ┊ ↳ func[2] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ bark + ┊ ┊ ↳ func[1] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ calledTwice + 7 ┊ 4.86% ┊ export "bark" + ┊ ┊ ↳ func[2] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ bark + ┊ ┊ ↳ func[1] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ calledTwice + 7 ┊ 4.86% ┊ export "woof" + ┊ ┊ ↳ func[3] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ woof + ┊ ┊ ↳ func[0] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ calledOnce + ┊ ┊ ↳ func[1] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ calledTwice diff --git a/twiggy/tests/expectations/paths_test_default_output_desc_with_depth b/twiggy/tests/expectations/paths_test_default_output_desc_with_depth new file mode 100644 index 00000000..e3f6076e --- /dev/null +++ b/twiggy/tests/expectations/paths_test_default_output_desc_with_depth @@ -0,0 +1,15 @@ + Shallow Bytes │ Shallow % │ Retaining Paths +───────────────┼───────────┼──────────────────────────── + 44 ┊ 30.56% ┊ "function names" subsection + 7 ┊ 4.86% ┊ export "awoo" + ┊ ┊ ↳ func[4] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ awoo + 7 ┊ 4.86% ┊ export "bark" + ┊ ┊ ↳ func[2] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ bark + 7 ┊ 4.86% ┊ export "woof" + ┊ ┊ ↳ func[3] + ┊ ┊ ↳ type[0] + ┊ ┊ ↳ woof diff --git a/twiggy/tests/expectations/paths_json b/twiggy/tests/expectations/paths_wee_alloc_json similarity index 100% rename from twiggy/tests/expectations/paths_json rename to twiggy/tests/expectations/paths_wee_alloc_json diff --git a/twiggy/tests/fixtures/paths_test.wasm b/twiggy/tests/fixtures/paths_test.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6421c8251d9a7637b8c938e4061da731d3bbd9de GIT binary patch literal 144 zcmXAiyA8rX5JYD`hb3>~h>Q*>0xEFg0$7ei0{oFo;E>i?j$~$DG7tWq2mmFzfIhNI z0YI}C^=(<8diC`M)_^;tJtW1MEP^9CvIbq$^9O7=>FiUXV?vvK9Luerb0PchVSD7l Q^-$`cRtH-+2@HYa3s5o`