Skip to content

Commit

Permalink
Add a twiggy garbage command. Fixes issue rustwasm#48.
Browse files Browse the repository at this point in the history
  • Loading branch information
data-pup committed May 9, 2018
1 parent 8ff9dec commit 24d5a35
Show file tree
Hide file tree
Showing 12 changed files with 212 additions and 5 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Use `twiggy` to make your binaries slim!
- [`twiggy monos`](#twiggy-monos)
- [`twiggy dominators`](#twiggy-dominators)
- [`twiggy diff`](#twiggy-diff)
- ['twiggy garbage'](#twiggy-garbage)
- [🦀 As a Crate](#-as-a-crate)
- [🕸 On the Web with WebAssembly](#-on-the-web-with-webassembly)
- [🔎 Supported Binary Formats](#-supported-binary-formats)
Expand Down Expand Up @@ -364,6 +365,15 @@ and new versions of a binary.
+145 ┊ <wee_alloc::neighbors::Neighbors<'a, T>>::remove::hc9e5d4284e8233b8
```
#### `twiggy garbage`
The `twiggy garbage` sub-command finds and display code and data that is not
transitively referenced by any exports or public functions.
```
AWOO : Place output example here.
```
### 🦀 As a Crate
`twiggy` is divided into a collection of crates that you can use
Expand Down
1 change: 1 addition & 0 deletions analyze/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ path = "./analyze.rs"
twiggy-ir = { version = "0.1.0", path = "../ir" }
twiggy-opt = { version = "0.1.0", path = "../opt", default-features = false }
twiggy-traits = { version = "0.1.0", path = "../traits" }
petgraph = "0.4.12"
87 changes: 83 additions & 4 deletions analyze/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]

extern crate petgraph;
extern crate twiggy_ir as ir;
extern crate twiggy_opt as opt;
extern crate twiggy_traits as traits;
Expand Down Expand Up @@ -723,10 +724,7 @@ impl traits::Emit for Diff {
]);

for entry in &self.deltas {
table.add_row(vec![
format!("{:+}", entry.delta),
entry.name.clone(),
]);
table.add_row(vec![format!("{:+}", entry.delta), entry.name.clone()]);
}

write!(dest, "{}", &table)?;
Expand Down Expand Up @@ -801,3 +799,84 @@ pub fn diff(
let diff = Diff { deltas };
Ok(Box::new(diff) as Box<traits::Emit>)
}

#[derive(Debug)]
struct Garbage {
items: Vec<ir::Id>,
opts: opt::Garbage,
}

impl traits::Emit for Garbage {
fn emit_text(&self, items: &ir::Items, dest: &mut io::Write) -> Result<(), traits::Error> {
let mut table = Table::with_header(vec![
(Align::Right, "Bytes".to_string()),
(Align::Right, "Size %".to_string()),
(Align::Right, "Item".to_string()),
]);

for &id in &self.items {
let item = &items[id];
let size = item.size();
let size_percent = (f64::from(size)) / (f64::from(items.size())) * 100.0;
table.add_row(vec![
size.to_string(),
format!("{:.2}%", size_percent),
item.name().to_string(),
]);
}

write!(dest, "{}", &table)?;
Ok(())
}

fn emit_json(&self, items: &ir::Items, dest: &mut io::Write) -> Result<(), traits::Error> {
let mut arr = json::array(dest)?;

for &id in &self.items {
let item = &items[id];

let mut obj = arr.object()?;
obj.field("name", item.name())?;

let size = item.size();
let size_percent = (f64::from(size)) / (f64::from(items.size())) * 100.0;
obj.field("bytes", size)?;
obj.field("size_percent", size_percent)?;
}

Ok(())
}
}

/// Find items that are not transitively referenced by any exports or public functions.
pub fn garbage(
_items: &ir::Items,
_opts: &opt::Garbage,
) -> Result<Box<traits::Emit>, traits::Error> {
fn get_reachable_items(_items: &ir::Items) -> BTreeSet<ir::Id> {
let mut reachable_items: BTreeSet<ir::Id> = BTreeSet::new();
let mut dfs = petgraph::visit::Dfs::new(_items, _items.meta_root());
while let Some(id) = dfs.next(&_items) {
reachable_items.insert(id);
}
reachable_items
}

let reachable_items = get_reachable_items(&_items);
let mut unreachable_items: Vec<_> = _items
.iter()
.filter(|item| !reachable_items.contains(&item.id()))
.collect();

unreachable_items.sort_by(|a, b| b.size().cmp(&a.size()));
unreachable_items.truncate(_opts.max_items() as usize);

let unreachable_items: Vec<_> = unreachable_items.iter().map(|item| item.id()).collect();

let garbage_items = Garbage {
items: unreachable_items,
opts: _opts.clone(),
};

Ok(Box::new(garbage_items) as Box<traits::Emit>)
}
66 changes: 65 additions & 1 deletion opt/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ pub enum Options {

/// Diff the old and new versions of a binary to see what sizes changed.
#[structopt(name = "diff")]
Diff(Diff)
Diff(Diff),

/// Find and display code and data that is not transitively referenced by
/// any exports or public functions.
#[structopt(name = "garbage")]
Garbage(Garbage)
}

/// List the top code size offenders in a binary.
Expand Down Expand Up @@ -409,3 +414,62 @@ impl Diff {
self.max_items = n;
}
}

/// Find and display code and data that is not transitively referenced by any
/// exports or public functions.
#[derive(Clone, Debug)]
#[derive(StructOpt)]
#[wasm_bindgen]
pub struct Garbage {
/// The path to the input binary to size profile.
#[cfg(feature = "cli")]
#[structopt(parse(from_os_str))]
input: path::PathBuf,

/// The destination to write the output to. Defaults to `stdout`.
#[cfg(feature = "cli")]
#[structopt(short = "o", default_value = "-")]
output_destination: OutputDestination,

/// The format the output should be written in.
#[cfg(feature = "cli")]
#[structopt(short = "f", long = "format", default_value = "text")]
output_format: traits::OutputFormat,

/// The maximum number of items to display.
#[structopt(short = "n", default_value = "10")]
max_items: u32,
}

impl Default for Garbage {
fn default() -> Garbage {
Garbage {
#[cfg(feature = "cli")]
input: Default::default(),
#[cfg(feature = "cli")]
output_destination: Default::default(),
#[cfg(feature = "cli")]
output_format: Default::default(),

max_items: 10,
}
}
}

#[wasm_bindgen]
impl Garbage {
/// Construct a new, default `Garbage`
pub fn new() -> Garbage {
Garbage::default()
}

/// The maximum number of items to display.
pub fn max_items(&self) -> u32 {
self.max_items
}

/// Set the maximum number of items to display.
pub fn set_max_items(&mut self, max: u32) {
self.max_items = max;
}
}
17 changes: 17 additions & 0 deletions opt/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ cfg_if! {
Options::Paths(ref paths) => paths.input(),
Options::Monos(ref monos) => monos.input(),
Options::Diff(ref diff) => diff.input(),
Options::Garbage(ref garbo) => garbo.input(),
}
}

Expand All @@ -61,6 +62,7 @@ cfg_if! {
Options::Paths(ref paths) => paths.output_destination(),
Options::Monos(ref monos) => monos.output_destination(),
Options::Diff(ref diff) => diff.output_destination(),
Options::Garbage(ref garbo) => garbo.output_destination(),
}
}

Expand All @@ -71,6 +73,7 @@ cfg_if! {
Options::Paths(ref paths) => paths.output_format(),
Options::Monos(ref monos) => monos.output_format(),
Options::Diff(ref diff) => diff.output_format(),
Options::Garbage(ref garbo) => garbo.output_format(),
}
}
}
Expand Down Expand Up @@ -152,6 +155,20 @@ cfg_if! {
}
}

impl CommonCliOptions for Garbage {
fn input(&self) -> &path::Path {
&self.input
}

fn output_destination(&self) -> &OutputDestination {
&self.output_destination
}

fn output_format(&self) -> traits::OutputFormat {
self.output_format
}
}

/// Where to output results.
#[derive(Clone, Debug)]
pub enum OutputDestination {
Expand Down
11 changes: 11 additions & 0 deletions twiggy/tests/expectations/garbage
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Bytes │ Size % │ Item
───────┼────────┼────────
11 ┊ 13.10% ┊ code[2]
8 ┊ 9.52% ┊ code[1]
7 ┊ 8.33% ┊ type[2]
5 ┊ 5.95% ┊ type[1]
5 ┊ 5.95% ┊ code[0]
4 ┊ 4.76% ┊ type[0]
1 ┊ 1.19% ┊ func[0]
1 ┊ 1.19% ┊ func[1]
1 ┊ 1.19% ┊ func[2]
1 change: 1 addition & 0 deletions twiggy/tests/expectations/garbage_json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"name":"code[2]","bytes":11,"size_percent":13.095238095238097},{"name":"code[1]","bytes":8,"size_percent":9.523809523809524},{"name":"type[2]","bytes":7,"size_percent":8.333333333333332},{"name":"type[1]","bytes":5,"size_percent":5.952380952380952},{"name":"code[0]","bytes":5,"size_percent":5.952380952380952},{"name":"type[0]","bytes":4,"size_percent":4.761904761904762},{"name":"func[0]","bytes":1,"size_percent":1.1904761904761905},{"name":"func[1]","bytes":1,"size_percent":1.1904761904761905},{"name":"func[2]","bytes":1,"size_percent":1.1904761904761905}]
4 changes: 4 additions & 0 deletions twiggy/tests/expectations/garbage_top_2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Bytes │ Size % │ Item
───────┼────────┼────────
11 ┊ 13.10% ┊ code[2]
8 ┊ 9.52% ┊ code[1]
Binary file added twiggy/tests/fixtures/garbage.wasm
Binary file not shown.
18 changes: 18 additions & 0 deletions twiggy/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,21 @@ test!(
"-n",
"5"
);

test!(garbage, "garbage", "./fixtures/garbage.wasm");

test!(
garbage_top_2,
"garbage",
"./fixtures/garbage.wasm",
"-n",
"2"
);

test!(
garbage_json,
"garbage",
"./fixtures/garbage.wasm",
"-f",
"json"
);
1 change: 1 addition & 0 deletions twiggy/twiggy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ fn run(opts: opt::Options) -> Result<(), traits::Error> {
opt::Options::Dominators(ref doms) => analyze::dominators(&mut items, doms)?,
opt::Options::Paths(ref paths) => analyze::paths(&mut items, paths)?,
opt::Options::Monos(ref monos) => analyze::monos(&mut items, monos)?,
opt::Options::Garbage(ref garbo) => analyze::garbage(&mut items, garbo)?,
opt::Options::Diff(ref diff) => {
let mut new_items = parser::read_and_parse(diff.new_input())?;
analyze::diff(&mut items, &mut new_items, diff)?
Expand Down

0 comments on commit 24d5a35

Please sign in to comment.