diff --git a/Cargo.lock b/Cargo.lock index 3622a5342c..94ecc4f1e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -189,6 +189,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytesize" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce3dbc6ab70a821f3e9c36084d4c1a96f50006fd41697123d3fe0a5f03d5912c" + [[package]] name = "cast" version = "0.3.0" @@ -2020,6 +2026,7 @@ dependencies = [ "anyhow", "arbitrary", "bitflags", + "bytesize", "clap", "clap_complete", "comfy-table", diff --git a/Cargo.toml b/Cargo.toml index a2cd32f054..25e21f63ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,6 +74,7 @@ ahash = { version = "0.8.11", default-features = false } anyhow = "1.0.58" arbitrary = "1.1.0" bitflags = "2.5.0" +bytesize = "2.0.0" clap = { version = "4.0.0", features = ["derive"] } clap_complete = "4.4.7" comfy-table = { version = "7.1.3", default-features = false } @@ -168,6 +169,7 @@ wit-parser = { workspace = true, optional = true, features = ['decoding', 'wat', # Dependencies of `metadata` wasm-metadata = { workspace = true, features = ["clap"], optional = true } +bytesize = { workspace = true } # Dependencies of `wit-smith` wit-smith = { workspace = true, features = ["clap"], optional = true } diff --git a/src/bin/wasm-tools/metadata.rs b/src/bin/wasm-tools/metadata.rs index 7d3546d2e8..db89934787 100644 --- a/src/bin/wasm-tools/metadata.rs +++ b/src/bin/wasm-tools/metadata.rs @@ -1,9 +1,10 @@ +use bytesize::ByteSize; use std::io::Write; use anyhow::Result; use comfy_table::modifiers::UTF8_ROUND_CORNERS; use comfy_table::presets::UTF8_FULL; -use comfy_table::{ContentArrangement, Table}; +use comfy_table::{CellAlignment, ContentArrangement, Table}; use termcolor::WriteColor; use wasm_metadata::{Metadata, Payload}; @@ -54,7 +55,8 @@ impl ShowOpts { if self.json { write!(output, "{}", serde_json::to_string(&payload)?)?; } else { - write_table(&payload, &mut output)?; + write_summary_table(&payload, &mut output)?; + write_details_table(&payload, &mut output)?; } Ok(()) } @@ -89,8 +91,123 @@ impl AddOpts { } } -/// Write a table containing a wasm binary's metadata to a writer -fn write_table(payload: &Payload, f: &mut Box) -> Result<()> { +/// Write a table containing a summarized overview of a wasm binary's metadata to +/// a writer. +fn write_summary_table(payload: &Payload, f: &mut Box) -> Result<()> { + // Prepare a table and get the individual metadata + let mut table = Table::new(); + table + .load_preset(UTF8_FULL) + .apply_modifier(UTF8_ROUND_CORNERS) + .set_content_arrangement(ContentArrangement::Dynamic) + .set_width(80) + .set_header(vec!["KIND", "NAME", "SIZE", "SIZE%", "LANGUAGES", "PARENT"]); + + table + .column_mut(2) + .expect("This should be the SIZE column") + .set_cell_alignment(CellAlignment::Right); + + table + .column_mut(3) + .expect("This should be the SIZE% column") + .set_cell_alignment(CellAlignment::Right); + + // Get the max value of the `range` field. This is the upper memory bound. + fn find_range_max(max: &mut usize, payload: &Payload) { + let range = &payload.metadata().range; + if range.end > *max { + *max = range.end; + } + + if let Payload::Component { children, .. } = payload { + for child in children { + find_range_max(max, child); + } + } + } + + let mut range_max = 0; + find_range_max(&mut range_max, payload); + + // Recursively add all children to the table + write_summary_table_inner(&payload, "", &mut 0, range_max, f, &mut table)?; + + // Write the table to the writer + writeln!(f, "{table}")?; + + Ok(()) +} + +// The recursing inner function of `write_summary_table` +fn write_summary_table_inner( + payload: &Payload, + parent: &str, + unknown_id: &mut u16, + range_max: usize, + f: &mut Box, + table: &mut Table, +) -> Result<()> { + let Metadata { + name, + range, + producers, + .. + } = payload.metadata(); + + let name = match name.as_deref() { + Some(name) => name.to_owned(), + None => { + let name = format!("unknown({unknown_id})"); + *unknown_id += 1; + name + } + }; + let size = ByteSize::b((range.end - range.start) as u64) + .display() + .si_short() + .to_string(); + + let usep = match ((range.end - range.start) as f64 / range_max as f64 * 100.0).round() as u8 { + // If the item was truly empty, it wouldn't be part of the binary + 0..=1 => "<1%".to_string(), + // We're hedging against the low-ends, this hedges against the high-ends. + // Makes sure we don't see a mix of <1% and 100% in the same table, unless + // the item is actually 100% of the binary. + 100 if range.end != range_max => ">99%".to_string(), + usep => format!("{}%", usep), + }; + let kind = match payload { + Payload::Component { .. } => "component", + Payload::Module(_) => "module", + }; + let languages = match producers { + Some(producers) => match producers.iter().find(|(name, _)| *name == "language") { + Some((_, pairs)) => pairs + .iter() + .map(|(lang, _)| lang.to_owned()) + .collect::>() + .join(", "), + None => "-".to_string(), + }, + None => "-".to_string(), + }; + + table.add_row(vec![&kind, &*name, &*size, &usep, &languages, &parent]); + + // Recursively print any children + if let Payload::Component { children, .. } = payload { + for payload in children { + write_summary_table_inner(payload, &name, unknown_id, range_max, f, table)?; + } + } + + Ok(()) +} + +/// Write a table containing a detailed overview of a wasm binary's metadata to +/// a writer. +fn write_details_table(payload: &Payload, f: &mut Box) -> Result<()> { // Prepare a table and get the individual metadata let mut table = Table::new(); table @@ -194,7 +311,7 @@ fn write_table(payload: &Payload, f: &mut Box) -> Result<()> { // Recursively print any children if let Payload::Component { children, .. } = payload { for payload in children { - write_table(payload, f)?; + write_details_table(payload, f)?; } } diff --git a/tests/cli/add-metadata-merge-sections.wat.stdout b/tests/cli/add-metadata-merge-sections.wat.stdout index b3564981c0..35f759ab6c 100644 --- a/tests/cli/add-metadata-merge-sections.wat.stdout +++ b/tests/cli/add-metadata-merge-sections.wat.stdout @@ -1,3 +1,8 @@ +╭────────┬────────────┬──────┬───────┬───────────┬────────╮ +│ KIND ┆ NAME ┆ SIZE ┆ SIZE% ┆ LANGUAGES ┆ PARENT │ +╞════════╪════════════╪══════╪═══════╪═══════════╪════════╡ +│ module ┆ unknown(0) ┆ 54B ┆ 100% ┆ foo, bar ┆ │ +╰────────┴────────────┴──────┴───────┴───────────┴────────╯ ╭──────────┬───────────╮ │ KIND ┆ VALUE │ ╞══════════╪═══════════╡ diff --git a/tests/cli/add-metadata-overwrite-name.wat.stdout b/tests/cli/add-metadata-overwrite-name.wat.stdout index b8a8496fbf..d3c387cfca 100644 --- a/tests/cli/add-metadata-overwrite-name.wat.stdout +++ b/tests/cli/add-metadata-overwrite-name.wat.stdout @@ -1,3 +1,8 @@ +╭────────┬──────┬──────┬───────┬───────────┬────────╮ +│ KIND ┆ NAME ┆ SIZE ┆ SIZE% ┆ LANGUAGES ┆ PARENT │ +╞════════╪══════╪══════╪═══════╪═══════════╪════════╡ +│ module ┆ foo ┆ 21B ┆ 100% ┆ - ┆ │ +╰────────┴──────┴──────┴───────┴───────────┴────────╯ ╭───────┬───────────╮ │ KIND ┆ VALUE │ ╞═══════╪═══════════╡ diff --git a/tests/cli/add-metadata.wat.stdout b/tests/cli/add-metadata.wat.stdout index d78adae2a9..a8f31911e0 100644 --- a/tests/cli/add-metadata.wat.stdout +++ b/tests/cli/add-metadata.wat.stdout @@ -1,3 +1,8 @@ +╭────────┬──────┬──────┬───────┬───────────┬────────╮ +│ KIND ┆ NAME ┆ SIZE ┆ SIZE% ┆ LANGUAGES ┆ PARENT │ +╞════════╪══════╪══════╪═══════╪═══════════╪════════╡ +│ module ┆ foo ┆ 84B ┆ 100% ┆ bar ┆ │ +╰────────┴──────┴──────┴───────┴───────────┴────────╯ ╭──────────────┬────────────╮ │ KIND ┆ VALUE │ ╞══════════════╪════════════╡ diff --git a/tests/cli/metadata-add-component.wat.stdout b/tests/cli/metadata-add-component.wat.stdout index dec6045f26..d7d2856696 100644 --- a/tests/cli/metadata-add-component.wat.stdout +++ b/tests/cli/metadata-add-component.wat.stdout @@ -1,3 +1,10 @@ +╭───────────┬────────────┬──────┬───────┬───────────┬────────╮ +│ KIND ┆ NAME ┆ SIZE ┆ SIZE% ┆ LANGUAGES ┆ PARENT │ +╞═══════════╪════════════╪══════╪═══════╪═══════════╪════════╡ +│ component ┆ foo ┆ 101B ┆ 100% ┆ foo ┆ │ +├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┤ +│ module ┆ unknown(0) ┆ 39B ┆ 39% ┆ - ┆ foo │ +╰───────────┴────────────┴──────┴───────┴───────────┴────────╯ ╭──────────┬────────────────────╮ │ KIND ┆ VALUE │ ╞══════════╪════════════════════╡ diff --git a/tests/cli/metadata-component.wat.stdout b/tests/cli/metadata-component.wat.stdout index 27421471df..7a192fcc9a 100644 --- a/tests/cli/metadata-component.wat.stdout +++ b/tests/cli/metadata-component.wat.stdout @@ -1,3 +1,12 @@ +╭───────────┬───────────────────┬──────┬───────┬───────────┬─────────╮ +│ KIND ┆ NAME ┆ SIZE ┆ SIZE% ┆ LANGUAGES ┆ PARENT │ +╞═══════════╪═══════════════════╪══════╪═══════╪═══════════╪═════════╡ +│ component ┆ my-name ┆ 136B ┆ 100% ┆ - ┆ │ +├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ +│ module ┆ submodule ┆ 27B ┆ 20% ┆ - ┆ my-name │ +├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ +│ module ┆ another submodule ┆ 35B ┆ 26% ┆ - ┆ my-name │ +╰───────────┴───────────────────┴──────┴───────┴───────────┴─────────╯ ╭───────┬────────────────────────────╮ │ KIND ┆ VALUE │ ╞═══════╪════════════════════════════╡ diff --git a/tests/cli/metadata.wat.stdout b/tests/cli/metadata.wat.stdout index 8cc41d314b..9009926594 100644 --- a/tests/cli/metadata.wat.stdout +++ b/tests/cli/metadata.wat.stdout @@ -1,3 +1,8 @@ +╭────────┬────────────┬──────┬───────┬───────────┬────────╮ +│ KIND ┆ NAME ┆ SIZE ┆ SIZE% ┆ LANGUAGES ┆ PARENT │ +╞════════╪════════════╪══════╪═══════╪═══════════╪════════╡ +│ module ┆ unknown(0) ┆ 8B ┆ 100% ┆ - ┆ │ +╰────────┴────────────┴──────┴───────┴───────────┴────────╯ ╭───────┬───────────╮ │ KIND ┆ VALUE │ ╞═══════╪═══════════╡