From d025b273370caf412f0c761b7437233d480f4940 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 1 Mar 2018 16:07:03 -0800 Subject: [PATCH 1/2] Add a useful WebAssembly API Returns the results of the analyses as a JSON string. --- .gitignore | 7 +- Cargo.lock | 619 ++++++++++++++++++ Cargo.toml | 6 + analyze/Cargo.toml | 2 +- analyze/analyze.rs | 128 +++- analyze/json.rs | 141 ++++ ci/script.sh | 15 +- ir/ir.rs | 3 +- opt/Cargo.toml | 4 + opt/build.rs | 28 + opt/definitions.rs | 114 ++++ opt/opt.rs | 120 +--- traits/traits.rs | 12 +- twiggy/tests/expectations/paths_json | 1 + twiggy/tests/expectations/top_2_json | 1 + twiggy/tests/expectations/top_2_json_retained | 1 + twiggy/tests/tests.rs | 45 +- wasm-api/Cargo.toml | 9 + wasm-api/wasm-api.rs | 44 +- 19 files changed, 1174 insertions(+), 126 deletions(-) create mode 100644 Cargo.lock create mode 100755 analyze/json.rs create mode 100644 opt/build.rs create mode 100644 opt/definitions.rs create mode 100644 twiggy/tests/expectations/paths_json create mode 100644 twiggy/tests/expectations/top_2_json create mode 100644 twiggy/tests/expectations/top_2_json_retained mode change 100644 => 100755 wasm-api/wasm-api.rs diff --git a/.gitignore b/.gitignore index 2bd97f65..55c38432 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ - /target **/*.rs.bk Cargo.lock twiggy/tests/whatever-output.txt +wasm-api/.crates.toml +wasm-api/bin +twiggy_wasm_api.d.ts +twiggy_wasm_api.js +twiggy_wasm_api.wasm +twiggy_wasm_api_bg.wasm diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..8d9b7a21 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,619 @@ +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cpp_demangle" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "diff" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dtoa" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "failure" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fixedbitset" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "frozen" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "glob" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "itoa" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ordermap" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "owning_ref" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-wasm" +version = "0.27.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "petgraph" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive_internals" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "stable_deref_trait" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "structopt" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "twiggy" +version = "0.1.0" +dependencies = [ + "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "twiggy-analyze 0.1.0", + "twiggy-ir 0.1.0", + "twiggy-opt 0.1.0", + "twiggy-parser 0.1.0", + "twiggy-traits 0.1.0", +] + +[[package]] +name = "twiggy-analyze" +version = "0.1.0" +dependencies = [ + "twiggy-ir 0.1.0", + "twiggy-opt 0.1.0", + "twiggy-traits 0.1.0", +] + +[[package]] +name = "twiggy-ir" +version = "0.1.0" +dependencies = [ + "cpp_demangle 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "frozen 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "petgraph 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "twiggy-opt" +version = "0.1.0" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "twiggy-traits 0.1.0", + "wasm-bindgen 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "twiggy-parser" +version = "0.1.0" +dependencies = [ + "parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)", + "twiggy-ir 0.1.0", + "twiggy-traits 0.1.0", +] + +[[package]] +name = "twiggy-traits" +version = "0.1.0" +dependencies = [ + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)", + "twiggy-ir 0.1.0", +] + +[[package]] +name = "twiggy-wasm-api" +version = "0.1.0" +dependencies = [ + "twiggy-analyze 0.1.0", + "twiggy-ir 0.1.0", + "twiggy-opt 0.1.0", + "twiggy-parser 0.1.0", + "twiggy-traits 0.1.0", + "wasm-bindgen 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasm-bindgen" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wasm-bindgen-macro 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[patch.unused]] +name = "structopt" +version = "0.2.4" +source = "git+https://github.com/fitzgen/structopt.git?branch=proc-macro2-nightly-feature-workaround#a028c45d67f6b92b3db78bc2681d26b34b2335ac" + +[metadata] +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4" +"checksum backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe525f66f42d207968308ee86bc2dd60aa5fab535b22e616323a173d097d8e" +"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" +"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" +"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" +"checksum cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d2900f78631a5876dc5d6c9033ede027253efcd33dd36b1309fc6cab97ee0" +"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" +"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" +"checksum cpp_demangle 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "23d0c112e412a39379dcb1ccbfbe1ea633bd337577ca2fc163ecdd098e636a93" +"checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" +"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" +"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" +"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" +"checksum frozen 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f677be708300866a6ec8ead0c71da49551867dece3fda611113cc52413fd699" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" +"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" +"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" +"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364" +"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" +"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +"checksum parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4dc02a80a0315b109e48992c46942c79bcdb8fac416dd575d330ed9ced6cbd" +"checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd" +"checksum parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "538ef00b7317875071d5e00f603f24d16f0b474c1a5fc0ccb8b454ca72eafa79" +"checksum petgraph 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "8b30dc85588cd02b9b76f5e386535db546d21dc68506cff2abebee0b6445e8e4" +"checksum proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "49b6a521dc81b643e9a51e0d1cf05df46d5a2f3c0280ea72bcb68276ba64a118" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a" +"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" +"checksum serde 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "53e6b6859395f46cf528414659ce43e70902b2277519707c3bd91797b3320330" +"checksum serde_derive 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "16e97f8dc5b2dabc0183e0cde24b1a53835e5bb3d2c9e0fdb077f895bba7f2a9" +"checksum serde_derive_internals 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d30c4596450fd7bbda79ef15559683f9a79ac0193ea819db90000d7e1cae794" +"checksum serde_json 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7bf1cbb1387028a13739cb018ee0d9b3db534f22ca3c84a5904f7eadfde14e75" +"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9" +"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum structopt 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbf9b178b64479997d9515aa4a6956ada20a9829fa03fee3bbcff5962e4e405" +"checksum structopt-derive 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "29040b33bfc5dae3a321f79cbcd86813a5f024e3040a31f057188e7f2b6228ba" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" +"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" +"checksum wasm-bindgen 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6686541e684bfb7e78fee21042f6503178b6326629ae1ce058541689249342dd" +"checksum wasm-bindgen-backend 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fdc4203576e6d4a37afdba567c2d880d62459ae2a980da8128b480292009aa96" +"checksum wasm-bindgen-macro 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fc88ecc5e496421ea1bb8565d34b7df83f4603b60f06447bf2f5728ee75b30c" +"checksum wasm-bindgen-shared 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d58874fc368cf455747df795e6448addfa4a389ce42996def76b2f9763ba7d94" +"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index a601750a..66924c34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,10 @@ members = [ ] [profile.release] +lto = true debug = true +opt-level = "s" + +[patch.crates-io.structopt] +git = "https://github.com/fitzgen/structopt.git" +branch = "proc-macro2-nightly-feature-workaround" diff --git a/analyze/Cargo.toml b/analyze/Cargo.toml index 150d50e1..4177a828 100644 --- a/analyze/Cargo.toml +++ b/analyze/Cargo.toml @@ -8,5 +8,5 @@ path = "./analyze.rs" [dependencies] twiggy-ir = { version = "0.1.0", path = "../ir" } -twiggy-opt = { version = "0.1.0", path = "../opt" } +twiggy-opt = { version = "0.1.0", path = "../opt", default-features = false } twiggy-traits = { version = "0.1.0", path = "../traits" } diff --git a/analyze/analyze.rs b/analyze/analyze.rs index 45519550..5adc8c52 100644 --- a/analyze/analyze.rs +++ b/analyze/analyze.rs @@ -7,6 +7,8 @@ extern crate twiggy_ir as ir; extern crate twiggy_opt as opt; extern crate twiggy_traits as traits; +mod json; + use std::cmp; use std::collections::{BTreeMap, BTreeSet}; use std::fmt; @@ -52,7 +54,6 @@ impl fmt::Display for Table { let last = self.header.len() - 1; for (i, h) in self.header.iter().map(|h| &h.1).enumerate() { - if i == 0 { write!(f, " ")?; } else { @@ -151,6 +152,31 @@ impl traits::Emit for Top { 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 = (size as f64) / (items.size() as f64) * 100.0; + obj.field("shallow_size", size)?; + obj.field("shallow_size_percent", size_percent)?; + + if self.opts.retained { + let size = items.retained_size(id); + let size_percent = (size as f64) / (items.size() as f64) * 100.0; + obj.field("retained_size", size)?; + obj.field("retained_size_percent", size_percent)?; + } + } + + Ok(()) + } } /// Run the `top` analysis on the given IR items. @@ -283,6 +309,50 @@ impl traits::Emit for DominatorTree { write!(dest, "{}", &table)?; Ok(()) } + + fn emit_json(&self, items: &ir::Items, dest: &mut io::Write) -> Result<(), traits::Error> { + fn recursive_add_children( + items: &ir::Items, + opts: &opt::Dominators, + dominator_tree: &BTreeMap>, + id: ir::Id, + obj: &mut json::Object, + ) -> Result<(), traits::Error> { + let item = &items[id]; + + obj.field("name", item.name())?; + + let size = item.size(); + let size_percent = (size as f64) / (items.size() as f64) * 100.0; + obj.field("shallow_size", size)?; + obj.field("shallow_size_percent", size_percent)?; + + let size = items.retained_size(id); + let size_percent = (size as f64) / (items.size() as f64) * 100.0; + obj.field("retained_size", size)?; + obj.field("retained_size_percent", size_percent)?; + + // TODO FITZGEN: this needs to do the filtering like how text + // formatting does, but it would be ncie to push that earlier, like + // `top` does. + + if let Some(children) = dominator_tree.get(&id) { + let mut children: Vec<_> = children.iter().cloned().collect(); + children.sort_by(|a, b| items.retained_size(*b).cmp(&items.retained_size(*a))); + + let mut arr = obj.array("children")?; + for child in children { + let mut obj = arr.object()?; + recursive_add_children(items, opts, dominator_tree, child, &mut obj)?; + } + } + + Ok(()) + } + + let mut obj = json::object(dest)?; + recursive_add_children(items, &self.opts, &self.tree, items.meta_root(), &mut obj) + } } /// Compute the dominator tree for the given IR graph. @@ -378,6 +448,62 @@ impl traits::Emit for Paths { write!(dest, "{}", table)?; Ok(()) } + + fn emit_json(&self, items: &ir::Items, dest: &mut io::Write) -> Result<(), traits::Error> { + fn recursive_callers( + items: &ir::Items, + seen: &mut BTreeSet, + obj: &mut json::Object, + depth: usize, + mut paths: &mut usize, + opts: &opt::Paths, + id: ir::Id, + ) -> io::Result<()> { + let item = &items[id]; + + obj.field("name", item.name())?; + + let size = item.size(); + let size_percent = (size as f64) / (items.size() as f64) * 100.0; + obj.field("shallow_size", size)?; + obj.field("shallow_size_percent", size_percent)?; + + let mut callers = obj.array("callers")?; + + let depth = depth + 1; + if depth <= opts.max_depth { + seen.insert(id); + for (i, caller) in items.predecessors(id).enumerate() { + if seen.contains(&caller) || items.meta_root() == caller { + continue; + } + + if i > 0 { + *paths += 1; + } + if opts.max_paths == *paths { + break; + } + + let mut obj = callers.object()?; + recursive_callers(items, seen, &mut obj, depth, &mut paths, &opts, caller)?; + } + seen.remove(&id); + } + + Ok(()) + } + + let mut arr = json::array(dest)?; + for id in &self.items { + let mut paths = 0 as usize; + let mut seen = BTreeSet::new(); + let mut obj = arr.object()?; + recursive_callers(items, &mut seen, &mut obj, 0, &mut paths, &self.opts, *id)?; + } + + Ok(()) + } } /// Find all retaining paths for the given items. diff --git a/analyze/json.rs b/analyze/json.rs new file mode 100755 index 00000000..28d7b2e6 --- /dev/null +++ b/analyze/json.rs @@ -0,0 +1,141 @@ +// A couple methods are dead, but removing them would make the API oddly +// imbalanced and we might want to use them in some future analysis. +#![allow(dead_code)] + +use std::io; + +pub trait JsonPrimitive { + fn json_primitive(&self, w: &mut io::Write) -> io::Result<()>; +} + +impl<'a> JsonPrimitive for &'a str { + fn json_primitive(&self, w: &mut io::Write) -> io::Result<()> { + write!(w, "\"")?; + for c in self.chars() { + match c { + '"' => write!(w, "\\\"")?, + '\\' => write!(w, "\\")?, + '\n' => write!(w, "\\n")?, + c => write!(w, "{}", c)?, + } + } + write!(w, "\"") + } +} + +impl JsonPrimitive for f64 { + fn json_primitive(&self, w: &mut io::Write) -> io::Result<()> { + write!(w, "{}", self) + } +} + +impl JsonPrimitive for u32 { + fn json_primitive(&self, w: &mut io::Write) -> io::Result<()> { + write!(w, "{}", self) + } +} + +pub fn array(w: &mut io::Write) -> io::Result { + write!(w, "[")?; + Ok(Array { + w, + need_comma: false, + }) +} + +pub fn object(w: &mut io::Write) -> io::Result { + write!(w, "{{")?; + Ok(Object { + w, + need_comma: false, + }) +} + +pub struct Array<'a> { + w: &'a mut io::Write, + need_comma: bool, +} + +impl<'a> Drop for Array<'a> { + fn drop(&mut self) { + let _ = write!(self.w, "]"); + } +} + +impl<'a> Array<'a> { + fn comma(&mut self) -> io::Result<()> { + if self.need_comma { + write!(self.w, ",")?; + } + self.need_comma = true; + Ok(()) + } + + pub fn object(&mut self) -> io::Result { + self.comma()?; + object(&mut *self.w) + } + + pub fn array(&mut self) -> io::Result { + self.comma()?; + array(&mut *self.w) + } + + pub fn elem

(&mut self, elem: P) -> io::Result<()> + where + P: JsonPrimitive, + { + self.comma()?; + elem.json_primitive(self.w) + } +} + +pub struct Object<'a> { + w: &'a mut io::Write, + need_comma: bool, +} + +impl<'a> Drop for Object<'a> { + fn drop(&mut self) { + let _ = write!(self.w, "}}"); + } +} + +impl<'a> Object<'a> { + fn comma_and_name(&mut self, name: S) -> io::Result<()> + where + S: AsRef, + { + if self.need_comma { + write!(self.w, ",")?; + } + self.need_comma = true; + name.as_ref().json_primitive(self.w)?; + write!(self.w, ":") + } + + pub fn object(&mut self, name: S) -> io::Result + where + S: AsRef, + { + self.comma_and_name(name)?; + object(&mut *self.w) + } + + pub fn array(&mut self, name: S) -> io::Result + where + S: AsRef, + { + self.comma_and_name(name)?; + array(&mut *self.w) + } + + pub fn field(&mut self, name: S, val: P) -> io::Result<()> + where + S: AsRef, + P: JsonPrimitive, + { + self.comma_and_name(name)?; + val.json_primitive(self.w) + } +} diff --git a/ci/script.sh b/ci/script.sh index 2282edac..5366e667 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -1,15 +1,24 @@ +#!/usr/bin/env bash + set -eux +cd "$(dirname "$0")/.." + case "$JOB" in "test") cargo test --all ;; "wasm") - rustup target add wasm32-unknown-unknown + rustup update nightly + rustup target add wasm32-unknown-unknown --toolchain nightly + + cd ./wasm-api + cargo +nightly build --release --target wasm32-unknown-unknown - cd wasm-api - cargo build --release --target wasm32-unknown-unknown + test -x ./bin/wasm-bindgen || cargo install wasm-bindgen-cli --version 0.2.3 --root "$(pwd)" + ./bin/wasm-bindgen --out-dir . ../target/wasm32-unknown-unknown/release/twiggy_wasm_api.wasm + wc -c *.wasm ;; *) diff --git a/ir/ir.rs b/ir/ir.rs index 2561a59b..bdf9bd4e 100644 --- a/ir/ir.rs +++ b/ir/ir.rs @@ -79,7 +79,8 @@ impl ItemsBuilder { /// Add a range of static data and the `Id` that defines it. pub fn link_data(&mut self, offset: i64, len: usize, id: Id) { - if offset >= 0 && offset <= i64::from(u32::MAX) && offset as usize + len < u32::MAX as usize { + if offset >= 0 && offset <= i64::from(u32::MAX) && offset as usize + len < u32::MAX as usize + { self.data.insert(offset as u32, (id, len as u32)); } } diff --git a/opt/Cargo.toml b/opt/Cargo.toml index 85c8cb43..0ac5ee67 100644 --- a/opt/Cargo.toml +++ b/opt/Cargo.toml @@ -9,6 +9,10 @@ path = "opt.rs" [dependencies] structopt = { version = "0.2", optional = true } twiggy-traits = { version = "0.1.0", path = "../traits" } +wasm-bindgen = { version = "0.2.3", optional = true } +cfg-if = "0.1.2" [features] +default = ["cli"] cli = ["structopt"] +wasm = ["wasm-bindgen"] diff --git a/opt/build.rs b/opt/build.rs new file mode 100644 index 00000000..d81da5d5 --- /dev/null +++ b/opt/build.rs @@ -0,0 +1,28 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let mut cli = PathBuf::new(); + cli.push(env::var("OUT_DIR").unwrap()); + + let mut wasm = cli.clone(); + wasm.push("wasm.rs"); + + cli.push("cli.rs"); + + run(format!( + "cat ./definitions.rs | grep -vi wasm_bindgen > '{}'", + cli.display() + )); + run(format!( + "cat ./definitions.rs | grep -vi structopt > '{}'", + wasm.display() + )); +} + +fn run>(cmd: S) { + let cmd = cmd.as_ref(); + let status = Command::new("sh").arg("-c").arg(cmd).status().unwrap(); + assert!(status.success()); +} diff --git a/opt/definitions.rs b/opt/definitions.rs new file mode 100644 index 00000000..a86460f5 --- /dev/null +++ b/opt/definitions.rs @@ -0,0 +1,114 @@ +// Fun times ahead! +// +// Apparently, proc-macros don't play well with `cfg_attr` yet, and their +// combination is buggy. So we can't use cfg_attr to choose between +// `wasm-bindgen` and `structopt` depending on if we're building the CLI or the +// wasm API respectively. Instead, we have `build.rs` remove unwanted attributes +// for us by invoking `grep`. +// +// It's terrible! But it works for now. + +/// Options for configuring `svelte`. +#[derive(Clone, Debug)] +#[derive(StructOpt)] +#[structopt(about = "\n`svelte` is a code size profiler.\n\nIt analyzes a binary's call graph to answer questions like:\n\n* Why was this function included in the binary in the first place?\n\n* What is the retained size of this function? I.e. how much space\n would be saved if I removed it and all the functions that become\n dead code after its removal.\n\nUse `svelte` to make your binaries slim!")] +pub enum Options { + /// List the top code size offenders in a binary. + #[structopt(name = "top")] + Top(Top), + + /// Compute and display the dominator tree for a binary's call graph. + #[structopt(name = "dominators")] + Dominators(Dominators), + + /// Find and display the call paths to a function in the given binary's call + /// graph. + #[structopt(name = "paths")] + Paths(Paths), +} + +/// List the top code size offenders in a binary. +#[derive(Clone, Debug)] +#[derive(StructOpt)] +#[wasm_bindgen] +pub struct Top { + /// The path to the input binary to size profile. + #[structopt(parse(from_os_str))] + pub input: path::PathBuf, + + /// The destination to write the output to. Defaults to `stdout`. + #[structopt(short = "o", default_value = "-")] + pub output_destination: OutputDestination, + + /// The format the output should be written in. + #[structopt(short = "f", long = "format", default_value = "text")] + pub output_format: traits::OutputFormat, + + /// The maximum number of items to display. + #[structopt(short = "n")] + pub number: Option, + + /// Display retaining paths. + #[structopt(short = "r", long = "retaining-paths")] + pub retaining_paths: bool, + + /// Sort list by retained + #[structopt(long = "retained")] + pub retained: bool, +} + +/// Compute and display the dominator tree for a binary's call graph. +#[derive(Clone, Debug)] +#[derive(StructOpt)] +#[wasm_bindgen] +pub struct Dominators { + /// The path to the input binary to size profile. + #[structopt(parse(from_os_str))] + pub input: path::PathBuf, + + /// The destination to write the output to. Defaults to `stdout`. + #[structopt(short = "o", default_value = "-")] + pub output_destination: OutputDestination, + + /// The format the output should be written in. + #[structopt(short = "f", long = "format", default_value = "text")] + pub output_format: traits::OutputFormat, + + /// The maximum depth to print the dominators tree. + #[structopt(short = "d")] + pub max_depth: Option, + + /// The maximum number of rows, regardless of depth in the tree, to display. + #[structopt(short = "r")] + pub max_rows: Option, +} + +/// Find and display the call paths to a function in the given binary's call +/// graph. +#[derive(Clone, Debug)] +#[derive(StructOpt)] +#[wasm_bindgen] +pub struct Paths { + /// The path to the input binary to size profile. + #[structopt(parse(from_os_str))] + pub input: path::PathBuf, + + /// The functions to find call paths to. + pub functions: Vec, + + /// The destination to write the output to. Defaults to `stdout`. + #[structopt(short = "o", default_value = "-")] + pub output_destination: OutputDestination, + + /// The format the output should be written in. + #[structopt(short = "f", long = "format", default_value = "text")] + pub output_format: traits::OutputFormat, + + /// The maximum depth to print the paths. + #[structopt(short = "d", default_value = "10")] + pub max_depth: usize, + + /// The maximum number of paths, regardless of depth in the tree, to display. + #[structopt(short = "r", default_value = "10")] + pub max_paths: usize, +} diff --git a/opt/opt.rs b/opt/opt.rs index 04b0b994..1941b8d0 100755 --- a/opt/opt.rs +++ b/opt/opt.rs @@ -1,37 +1,32 @@ //! Options for running `twiggy`. #![deny(missing_debug_implementations)] +#![cfg_attr(feature = "wasm", feature(proc_macro, wasm_custom_section, wasm_import_module))] -#[cfg(feature = "cli")] #[macro_use] -extern crate structopt; +extern crate cfg_if; extern crate twiggy_traits as traits; +cfg_if! { + if #[cfg(feature = "cli")] { + #[macro_use] + extern crate structopt; + include!(concat!(env!("OUT_DIR"), "/cli.rs")); + } else if #[cfg(feature = "wasm")] { + extern crate wasm_bindgen; + use wasm_bindgen::prelude::*; + include!(concat!(env!("OUT_DIR"), "/wasm.rs")); + } else { + compile_error!("Must enable one of either `cli` or `wasm` features"); + } +} + use std::fs; use std::io; use std::path; use std::str::FromStr; -/// Options for configuring `twiggy`. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "cli", derive(StructOpt))] -#[cfg_attr(feature = "cli", structopt(about = "\n`twiggy` is a code size profiler.\n\nIt analyzes a binary's call graph to answer questions like:\n\n* Why was this function included in the binary in the first place?\n\n* What is the retained size of this function? I.e. how much space\n would be saved if I removed it and all the functions that become\n dead code after its removal.\n\nUse `twiggy` to make your binaries slim!"))] -pub enum Options { - /// List the top code size offenders in a binary. - #[cfg_attr(feature = "cli", structopt(name = "top"))] - Top(Top), - - /// Compute and display the dominator tree for a binary's call graph. - #[cfg_attr(feature = "cli", structopt(name = "dominators"))] - Dominators(Dominators), - - /// Find and display the call paths to a function in the given binary's call - /// graph. - #[cfg_attr(feature = "cli", structopt(name = "paths"))] - Paths(Paths), -} - /// Options that are common to all commands. pub trait CommonOptions { /// Get the input file path. @@ -70,35 +65,6 @@ impl CommonOptions for Options { } } -/// List the top code size offenders in a binary. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "cli", derive(StructOpt))] -pub struct Top { - /// The path to the input binary to size profile. - #[cfg_attr(feature = "cli", structopt(parse(from_os_str)))] - pub input: path::PathBuf, - - /// The destination to write the output to. Defaults to `stdout`. - #[cfg_attr(feature = "cli", structopt(short = "o", default_value = "-"))] - pub output_destination: OutputDestination, - - /// The format the output should be written in. - #[cfg_attr(feature = "cli", structopt(short = "f", long = "format", default_value = "text"))] - pub output_format: traits::OutputFormat, - - /// The maximum number of items to display. - #[cfg_attr(feature = "cli", structopt(short = "n"))] - pub number: Option, - - /// Display retaining paths. - #[cfg_attr(feature = "cli", structopt(short = "r", long = "retaining-paths"))] - pub retaining_paths: bool, - - /// Sort list by retained - #[cfg_attr(feature = "cli", structopt(long = "retained"))] - pub retained: bool, -} - impl CommonOptions for Top { fn input(&self) -> &path::Path { &self.input @@ -113,31 +79,6 @@ impl CommonOptions for Top { } } -/// Compute and display the dominator tree for a binary's call graph. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "cli", derive(StructOpt))] -pub struct Dominators { - /// The path to the input binary to size profile. - #[cfg_attr(feature = "cli", structopt(parse(from_os_str)))] - pub input: path::PathBuf, - - /// The destination to write the output to. Defaults to `stdout`. - #[cfg_attr(feature = "cli", structopt(short = "o", default_value = "-"))] - pub output_destination: OutputDestination, - - /// The format the output should be written in. - #[cfg_attr(feature = "cli", structopt(short = "f", long = "format", default_value = "text"))] - pub output_format: traits::OutputFormat, - - /// The maximum depth to print the dominators tree. - #[cfg_attr(feature = "cli", structopt(short = "d"))] - pub max_depth: Option, - - /// The maximum number of rows, regardless of depth in the tree, to display. - #[cfg_attr(feature = "cli", structopt(short = "r"))] - pub max_rows: Option, -} - impl CommonOptions for Dominators { fn input(&self) -> &path::Path { &self.input @@ -152,35 +93,6 @@ impl CommonOptions for Dominators { } } -/// Find and display the call paths to a function in the given binary's call -/// graph. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "cli", derive(StructOpt))] -pub struct Paths { - /// The path to the input binary to size profile. - #[cfg_attr(feature = "cli", structopt(parse(from_os_str)))] - pub input: path::PathBuf, - - /// The functions to find call paths to. - pub functions: Vec, - - /// The destination to write the output to. Defaults to `stdout`. - #[cfg_attr(feature = "cli", structopt(short = "o", default_value = "-"))] - pub output_destination: OutputDestination, - - /// The format the output should be written in. - #[cfg_attr(feature = "cli", structopt(short = "f", long = "format", default_value = "text"))] - pub output_format: traits::OutputFormat, - - /// The maximum depth to print the paths. - #[cfg_attr(feature = "cli", structopt(short = "d", default_value = "10"))] - pub max_depth: usize, - - /// The maximum number of paths, regardless of depth in the tree, to display. - #[cfg_attr(feature = "cli", structopt(short = "r", default_value = "10"))] - pub max_paths: usize, -} - impl CommonOptions for Paths { fn input(&self) -> &path::Path { &self.input diff --git a/traits/traits.rs b/traits/traits.rs index 7d5cc406..86d4537d 100644 --- a/traits/traits.rs +++ b/traits/traits.rs @@ -112,9 +112,8 @@ pub enum OutputFormat { // /// Comma-separated values (CSV) format. // Csv, - - // /// JavaScript Object Notation format. - // Json, + /// JavaScript Object Notation format. + Json, } impl Default for OutputFormat { @@ -129,6 +128,7 @@ impl FromStr for OutputFormat { fn from_str(s: &str) -> Result { match s { "text" => Ok(OutputFormat::Text), + "json" => Ok(OutputFormat::Json), _ => Err(Error::with_msg(format!("Unknown output format: {}", s))), } } @@ -149,7 +149,7 @@ pub trait Emit { // OutputFormat::Html => self.emit_html(destination), // OutputFormat::Dot => self.emit_dot(destination), // OutputFormat::Csv => self.emit_csv(destination), - // OutputFormat::Json => self.emit_json(destination), + OutputFormat::Json => self.emit_json(items, destination), } } @@ -165,8 +165,8 @@ pub trait Emit { // /// Emit CSV. // fn emit_csv(&self, destination: &mut io::Write) -> Result<(), Error>; - // /// Emit JSON. - // fn emit_json(&self, destination: &mut io::Write) -> Result<(), Error>; + /// Emit JSON. + fn emit_json(&self, items: &ir::Items, destination: &mut io::Write) -> Result<(), Error>; } #[cfg(test)] diff --git a/twiggy/tests/expectations/paths_json b/twiggy/tests/expectations/paths_json new file mode 100644 index 00000000..39862872 --- /dev/null +++ b/twiggy/tests/expectations/paths_json @@ -0,0 +1 @@ +[{"name":"wee_alloc::alloc_first_fit::h9a72de3af77ef93f","shallow_size":225,"shallow_size_percent":7.987220447284344,"callers":[{"name":"func[3]","shallow_size":1,"shallow_size_percent":0.03549875754348598,"callers":[{"name":"wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e","shallow_size":152,"shallow_size_percent":5.395811146609868,"callers":[{"name":"func[2]","shallow_size":1,"shallow_size_percent":0.03549875754348598,"callers":[]}]}]}]},{"name":"hello","shallow_size":164,"shallow_size_percent":5.8217962371317,"callers":[{"name":"func[8]","shallow_size":1,"shallow_size_percent":0.03549875754348598,"callers":[{"name":"export \"hello\"","shallow_size":8,"shallow_size_percent":0.2839900603478878,"callers":[]}]}]},{"name":"goodbye","shallow_size":44,"shallow_size_percent":1.5619453319133831,"callers":[{"name":"func[9]","shallow_size":1,"shallow_size_percent":0.03549875754348598,"callers":[{"name":"export \"goodbye\"","shallow_size":10,"shallow_size_percent":0.3549875754348598,"callers":[]}]}]}] diff --git a/twiggy/tests/expectations/top_2_json b/twiggy/tests/expectations/top_2_json new file mode 100644 index 00000000..c55d6aa9 --- /dev/null +++ b/twiggy/tests/expectations/top_2_json @@ -0,0 +1 @@ +[{"name":"data[3]","shallow_size":1034,"shallow_size_percent":36.7057152999645},{"name":"\"function names\" subsection","shallow_size":774,"shallow_size_percent":27.47603833865815}] diff --git a/twiggy/tests/expectations/top_2_json_retained b/twiggy/tests/expectations/top_2_json_retained new file mode 100644 index 00000000..dd2b2e6c --- /dev/null +++ b/twiggy/tests/expectations/top_2_json_retained @@ -0,0 +1 @@ +[{"name":"data[3]","shallow_size":1034,"shallow_size_percent":36.7057152999645,"retained_size":1034,"retained_size_percent":36.7057152999645},{"name":"\"function names\" subsection","shallow_size":774,"shallow_size_percent":27.47603833865815,"retained_size":774,"retained_size_percent":27.47603833865815}] diff --git a/twiggy/tests/tests.rs b/twiggy/tests/tests.rs index 7ddf99b9..efde38e9 100644 --- a/twiggy/tests/tests.rs +++ b/twiggy/tests/tests.rs @@ -28,7 +28,7 @@ macro_rules! test { assert!( output.status.success(), - "should have run `svelete` OK\n\n\ + "should have run `twiggy` OK\n\n\ ============================== stdout ==============================\n\n\ {}\n\n\ ============================== stderr ==============================\n\n\ @@ -45,8 +45,13 @@ macro_rules! test { // Ignore errors. The diffing will provide a better diagnostic report. let expected = slurp(expected_path).unwrap_or(vec![]); + let expected = String::from_utf8_lossy(&expected); + let expected = expected.trim(); - if output.stdout != expected { + let actual = String::from_utf8_lossy(&output.stdout); + let actual = actual.trim(); + + if actual != expected { let mut cmd = "twiggy".to_string(); $( cmd.push(' '); @@ -56,8 +61,6 @@ macro_rules! test { println!("--- {}", expected_path); println!("+++ actually generated by `{}`", cmd); - let expected = String::from_utf8_lossy(&expected); - let actual = String::from_utf8_lossy(&output.stdout); for diff in diff::lines(&expected, &actual) { match diff { diff::Result::Left(l) => println!("-{}", l), @@ -143,3 +146,37 @@ test!( "-o", "whatever-output.txt" ); + +test!( + top_2_json, + "top", + "./fixtures/wee_alloc.wasm", + "-n", + "2", + "-f", + "json" +); + +test!( + top_2_json_retained, + "top", + "./fixtures/wee_alloc.wasm", + "--retained", + "-n", + "2", + "-f", + "json" +); + +test!( + paths_json, + "paths", + "./fixtures/wee_alloc.wasm", + "wee_alloc::alloc_first_fit::h9a72de3af77ef93f", + "hello", + "goodbye", + "-d", + "3", + "-f", + "json" +); diff --git a/wasm-api/Cargo.toml b/wasm-api/Cargo.toml index 3857db81..ef3eebac 100644 --- a/wasm-api/Cargo.toml +++ b/wasm-api/Cargo.toml @@ -16,9 +16,18 @@ version = "0.1.0" path = "../analyze" [dependencies.twiggy-opt] +default-features = false +features = ["wasm"] version = "0.1.0" path = "../opt" +[dependencies.twiggy-parser] +version = "0.1.0" +path = "../parser" + [dependencies.twiggy-traits] version = "0.1.0" path = "../traits" + +[dependencies.wasm-bindgen] +version = "0.2.3" diff --git a/wasm-api/wasm-api.rs b/wasm-api/wasm-api.rs old mode 100644 new mode 100755 index 8fdddfb5..fff79d8b --- a/wasm-api/wasm-api.rs +++ b/wasm-api/wasm-api.rs @@ -1,12 +1,46 @@ +#![cfg(target_arch = "wasm32")] +#![feature(proc_macro, wasm_custom_section, wasm_import_module)] + +extern crate wasm_bindgen; + extern crate twiggy_analyze as analyze; extern crate twiggy_ir as ir; extern crate twiggy_opt as opt; +extern crate twiggy_parser as parser; extern crate twiggy_traits as traits; -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct Items { + items: ir::Items, +} + +#[wasm_bindgen] +impl Items { + pub fn parse(data: &[u8]) -> Items { + let items = parser::parse(data).unwrap(); + Items { items } + } + + pub fn top(&mut self, options: &opt::Top) -> String { + let top = analyze::top(&mut self.items, options).unwrap(); + let mut buf = Vec::new(); + top.emit_json(&self.items, &mut buf).unwrap(); + String::from_utf8(buf).unwrap() + } + + pub fn dominators(&mut self, options: &opt::Dominators) -> String { + let dominators = analyze::dominators(&mut self.items, options).unwrap(); + let mut buf = Vec::new(); + dominators.emit_json(&self.items, &mut buf).unwrap(); + String::from_utf8(buf).unwrap() + } + + pub fn paths(&mut self, options: &opt::Paths) -> String { + let paths = analyze::paths(&mut self.items, options).unwrap(); + let mut buf = Vec::new(); + paths.emit_json(&self.items, &mut buf).unwrap(); + String::from_utf8(buf).unwrap() } } From fd74085bf189da9e349a06a22995fe95b218fee5 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 17 Apr 2018 15:25:48 -0700 Subject: [PATCH 2/2] Don't `cargo test` the wasm API in CI It won't work, and we really want to do `JOB=wasm ./ci/script.sh` instead. --- Cargo.lock | 5 ----- Cargo.toml | 4 ---- ci/script.sh | 2 +- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d9b7a21..00e4c089 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,11 +548,6 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[patch.unused]] -name = "structopt" -version = "0.2.4" -source = "git+https://github.com/fitzgen/structopt.git?branch=proc-macro2-nightly-feature-workaround#a028c45d67f6b92b3db78bc2681d26b34b2335ac" - [metadata] "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4" diff --git a/Cargo.toml b/Cargo.toml index 66924c34..1057e944 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,3 @@ members = [ lto = true debug = true opt-level = "s" - -[patch.crates-io.structopt] -git = "https://github.com/fitzgen/structopt.git" -branch = "proc-macro2-nightly-feature-workaround" diff --git a/ci/script.sh b/ci/script.sh index 5366e667..18d5f354 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -6,7 +6,7 @@ cd "$(dirname "$0")/.." case "$JOB" in "test") - cargo test --all + cargo test --all --exclude twiggy-wasm-api ;; "wasm")