From 364f86d223ad2ef460ca75690a6cdd7652ed92cf Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 16 Mar 2023 12:23:11 +0100 Subject: [PATCH] Turn on allocation tracker at run-time and for web (#1591) * Fix copy/paste in web viewer * Memory tracker callstack: shorten file paths * Fix typo * clean up copy-pasta comment * code refactor * allow turning on allocation tracking at runtime from the memory panel * nicer formatting in memory panel * Notice text layout allocations in the summaries * Notice failures to copy the backtrace * Notice empty backtraces * Fix stacktraces on web * wasm_bindgen_check: build to target_wasm * Always use target_wasm as build-dir when building wasm32 * Trim callstacks on web * typo fixe Co-authored-by: Andreas Reich * Use a checkbox for the detailed allocation tracking * Make web callstack even nicer * Even nicer callstacks --------- Co-authored-by: Andreas Reich --- .github/workflows/rust.yml | 4 +- Cargo.lock | 2 + crates/re_build_web_viewer/src/lib.rs | 8 +- crates/re_memory/Cargo.toml | 7 +- crates/re_memory/src/accounting_allocator.rs | 8 +- crates/re_memory/src/allocation_tracker.rs | 47 +------- crates/re_memory/src/backtrace_native.rs | 120 +++++++++++++++++++ crates/re_memory/src/backtrace_web.rs | 63 ++++++++++ crates/re_memory/src/lib.rs | 27 +++++ crates/re_viewer/src/ui/memory_panel.rs | 59 +++++---- crates/re_viewer/src/web.rs | 3 +- scripts/check.sh | 2 +- scripts/wasm_bindgen_check.sh | 24 ++-- 13 files changed, 286 insertions(+), 88 deletions(-) create mode 100644 crates/re_memory/src/backtrace_native.rs create mode 100644 crates/re_memory/src/backtrace_web.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fb5aba63f709..1f375d176aa8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -218,13 +218,13 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: --locked --all-features --lib --target wasm32-unknown-unknown -p re_viewer + args: --locked --all-features --lib --target wasm32-unknown-unknown --target-dir target_wasm -p re_viewer - name: Check re_renderer examples wasm32 uses: actions-rs/cargo@v1 with: command: check - args: --locked --target wasm32-unknown-unknown -p re_renderer --examples + args: --locked --target wasm32-unknown-unknown --target-dir target_wasm -p re_renderer --examples - run: ./scripts/wasm_bindgen_check.sh --skip-setup diff --git a/Cargo.lock b/Cargo.lock index 1c29d810a1be..fd1d117841e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3881,6 +3881,7 @@ dependencies = [ "backtrace", "emath", "instant", + "itertools", "memory-stats", "nohash-hasher", "once_cell", @@ -3888,6 +3889,7 @@ dependencies = [ "re_format", "re_log", "smallvec", + "wasm-bindgen", ] [[package]] diff --git a/crates/re_build_web_viewer/src/lib.rs b/crates/re_build_web_viewer/src/lib.rs index 25debfa12333..28d26de81c03 100644 --- a/crates/re_build_web_viewer/src/lib.rs +++ b/crates/re_build_web_viewer/src/lib.rs @@ -56,13 +56,13 @@ pub fn build(release: bool) { let mut cmd = std::process::Command::new("cargo"); cmd.args([ "build", - "--target-dir", - target_wasm_dir.as_str(), - "-p", + "--package", crate_name, "--lib", "--target", "wasm32-unknown-unknown", + "--target-dir", + target_wasm_dir.as_str(), ]); if release { cmd.arg("--release"); @@ -78,7 +78,7 @@ pub fn build(release: bool) { // These pre-encoded flags are generally generated by Cargo itself when loading its // configuration from e.g. `$CARGO_HOME/config.toml`; which means they will contain // values that only make sense for the native target host, not for a wasm build. - cmd.env("CARGO_ENCODED_RUSTFLAGS", ""); + cmd.env("CARGO_ENCODED_RUSTFLAGS", "--cfg=web_sys_unstable_apis"); eprintln!("> {cmd:?}"); let status = cmd diff --git a/crates/re_memory/Cargo.toml b/crates/re_memory/Cargo.toml index dfe5e836aff3..b05c08c9b61a 100644 --- a/crates/re_memory/Cargo.toml +++ b/crates/re_memory/Cargo.toml @@ -21,9 +21,9 @@ re_format.workspace = true re_log.workspace = true ahash.workspace = true -backtrace = { version = "0.3" } emath.workspace = true instant = { version = "0.1", features = ["wasm-bindgen"] } +itertools = "0.10" nohash-hasher = "0.2" once_cell = "1.16" parking_lot.workspace = true @@ -31,4 +31,9 @@ smallvec = "1.10" # native dependencies: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] +backtrace = "0.3" memory-stats = "1.0" + +# web dependencies: +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "=0.2.84" diff --git a/crates/re_memory/src/accounting_allocator.rs b/crates/re_memory/src/accounting_allocator.rs index 2157bd07321c..ee9b89e02998 100644 --- a/crates/re_memory/src/accounting_allocator.rs +++ b/crates/re_memory/src/accounting_allocator.rs @@ -31,7 +31,7 @@ thread_local! { /// /// Tracking an allocation (taking its backtrace etc) can itself create allocations. /// We don't want to track those allocations, or we will have infinite recursion. - static IS_TRHEAD_IN_ALLOCATION_TRACKER: std::cell::Cell = std::cell::Cell::new(false); + static IS_THREAD_IN_ALLOCATION_TRACKER: std::cell::Cell = std::cell::Cell::new(false); } // ---------------------------------------------------------------------------- @@ -188,7 +188,7 @@ pub fn tracking_stats() -> Option { } GLOBAL_STATS.track_callstacks.load(Relaxed).then(|| { - IS_TRHEAD_IN_ALLOCATION_TRACKER.with(|is_thread_in_allocation_tracker| { + IS_THREAD_IN_ALLOCATION_TRACKER.with(|is_thread_in_allocation_tracker| { // prevent double-lock of ALLOCATION_TRACKER: is_thread_in_allocation_tracker.set(true); let mut top_big_callstacks = tracker_stats(&BIG_ALLOCATION_TRACKER.lock()); @@ -303,7 +303,7 @@ fn note_alloc(ptr: *mut u8, size: usize) { // Big enough to track - but make sure we don't create a deadlock by trying to // track the allocations made by the allocation tracker: - IS_TRHEAD_IN_ALLOCATION_TRACKER.with(|is_thread_in_allocation_tracker| { + IS_THREAD_IN_ALLOCATION_TRACKER.with(|is_thread_in_allocation_tracker| { if !is_thread_in_allocation_tracker.get() { is_thread_in_allocation_tracker.set(true); @@ -337,7 +337,7 @@ fn note_dealloc(ptr: *mut u8, size: usize) { } else { // Big enough to track - but make sure we don't create a deadlock by trying to // track the allocations made by the allocation tracker: - IS_TRHEAD_IN_ALLOCATION_TRACKER.with(|is_thread_in_allocation_tracker| { + IS_THREAD_IN_ALLOCATION_TRACKER.with(|is_thread_in_allocation_tracker| { if !is_thread_in_allocation_tracker.get() { is_thread_in_allocation_tracker.set(true); diff --git a/crates/re_memory/src/allocation_tracker.rs b/crates/re_memory/src/allocation_tracker.rs index 731c523a8058..7fe23acd35eb 100644 --- a/crates/re_memory/src/allocation_tracker.rs +++ b/crates/re_memory/src/allocation_tracker.rs @@ -1,6 +1,6 @@ -use std::{hash::Hash, sync::Arc}; +use std::sync::Arc; -use backtrace::Backtrace; +use crate::{Backtrace, BacktraceHash}; use crate::CountAndSize; @@ -22,25 +22,6 @@ impl PtrHash { // ---------------------------------------------------------------------------- -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -struct BacktraceHash(u64); - -impl nohash_hasher::IsEnabled for BacktraceHash {} - -fn hash_backtrace(backtrace: &Backtrace) -> BacktraceHash { - use std::hash::Hasher as _; - let mut hasher = - std::hash::BuildHasher::build_hasher(&ahash::RandomState::with_seeds(0, 1, 2, 3)); - - for frame in backtrace.frames() { - frame.ip().hash(&mut hasher); - } - - BacktraceHash(hasher.finish()) -} - -// ---------------------------------------------------------------------------- - /// Formatted [`Backtrace`]. /// /// Clones without allocating. @@ -58,26 +39,10 @@ impl std::fmt::Display for ReadableBacktrace { impl ReadableBacktrace { fn new(mut backtrace: Backtrace) -> Self { - backtrace.resolve(); - let readable = format_backtrace(&backtrace); - Self { readable } - } -} - -fn format_backtrace(backtrace: &Backtrace) -> Arc { - let stack = format!("{backtrace:?}"); - let mut stack = stack.as_str(); - let start_pattern = "re_memory::accounting_allocator::note_alloc\n"; - if let Some(start_offset) = stack.find(start_pattern) { - stack = &stack[start_offset + start_pattern.len()..]; - } - - if let Some(end_offset) = stack.find("std::sys_common::backtrace::__rust_begin_short_backtrace") - { - stack = &stack[..end_offset]; + Self { + readable: backtrace.format(), + } } - - stack.into() } // ---------------------------------------------------------------------------- @@ -137,7 +102,7 @@ impl AllocationTracker { } let unresolved_backtrace = Backtrace::new_unresolved(); - let hash = hash_backtrace(&unresolved_backtrace); + let hash = BacktraceHash::new(&unresolved_backtrace); self.readable_backtraces .entry(hash) diff --git a/crates/re_memory/src/backtrace_native.rs b/crates/re_memory/src/backtrace_native.rs new file mode 100644 index 000000000000..601dbb301e6d --- /dev/null +++ b/crates/re_memory/src/backtrace_native.rs @@ -0,0 +1,120 @@ +use std::sync::Arc; + +pub(crate) struct Backtrace(backtrace::Backtrace); + +impl Backtrace { + pub fn new_unresolved() -> Self { + Self(backtrace::Backtrace::new_unresolved()) + } + + pub fn format(&mut self) -> Arc { + self.0.resolve(); + let stack = backtrace_to_string(&self.0); + trim_backtrace(&stack).into() + } +} + +impl std::hash::Hash for Backtrace { + fn hash(&self, state: &mut H) { + for frame in self.0.frames() { + frame.ip().hash(state); + } + } +} + +fn trim_backtrace(mut stack: &str) -> &str { + let start_pattern = "re_memory::accounting_allocator::note_alloc\n"; + if let Some(start_offset) = stack.find(start_pattern) { + stack = &stack[start_offset + start_pattern.len()..]; + } + + let end_pattern = "std::sys_common::backtrace::__rust_begin_short_backtrace"; + if let Some(end_offset) = stack.find(end_pattern) { + stack = &stack[..end_offset]; + } + + stack +} + +fn backtrace_to_string(backtrace: &backtrace::Backtrace) -> String { + if backtrace.frames().is_empty() { + return "[empty backtrace]".to_owned(); + } + + // We need to get a `std::fmt::Formatter`, and there is no easy way to do that, so we do it the hard way: + + struct AnonymizedBacktrace<'a>(&'a backtrace::Backtrace); + + impl<'a> std::fmt::Display for AnonymizedBacktrace<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + format_backtrace_with_fmt(self.0, f) + } + } + + AnonymizedBacktrace(backtrace).to_string() +} + +fn format_backtrace_with_fmt( + backtrace: &backtrace::Backtrace, + fmt: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + let mut print_path = |fmt: &mut std::fmt::Formatter<'_>, + path: backtrace::BytesOrWideString<'_>| { + let path = path.into_path_buf(); + let shortened = shorten_source_file_path(&path); + std::fmt::Display::fmt(&shortened, fmt) + }; + + let style = if fmt.alternate() { + backtrace::PrintFmt::Full + } else { + backtrace::PrintFmt::Short + }; + let mut f = backtrace::BacktraceFmt::new(fmt, style, &mut print_path); + f.add_context()?; + for frame in backtrace.frames() { + f.frame().backtrace_frame(frame)?; + } + f.finish()?; + Ok(()) +} + +/// Anonymize a path to a Rust source file from a callstack. +/// +/// Example input: +/// * `/Users/emilk/.cargo/registry/src/jackfan.us.kg-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs` +/// * `crates/rerun/src/main.rs` +/// * `/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs` +fn shorten_source_file_path(path: &std::path::Path) -> String { + // We must make sure we strip everything sensitive (especially user name). + // The easiest way is to look for `src` and strip everything up to it. + + use itertools::Itertools as _; + let components = path.iter().map(|path| path.to_string_lossy()).collect_vec(); + + // Look for the last `src`: + if let Some((src_rev_idx, _)) = components.iter().rev().find_position(|&c| c == "src") { + let src_idx = components.len() - src_rev_idx - 1; + // Before `src` comes the name of the crate - let's include that: + let first_index = src_idx.saturating_sub(1); + components.iter().skip(first_index).format("/").to_string() + } else { + // No `src` directory found - weird! + path.display().to_string() + } +} + +#[test] +fn test_shorten_path() { + for (before, after) in [ + ("/Users/emilk/.cargo/registry/src/jackfan.us.kg-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs", "tokio-1.24.1/src/runtime/runtime.rs"), + ("crates/rerun/src/main.rs", "rerun/src/main.rs"), + ("/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs", "core/src/ops/function.rs"), + ("/weird/path/file.rs", "/weird/path/file.rs"), + ] + { + use std::str::FromStr as _; + let before = std::path::PathBuf::from_str(before).unwrap(); + assert_eq!(shorten_source_file_path(&before), after); + } +} diff --git a/crates/re_memory/src/backtrace_web.rs b/crates/re_memory/src/backtrace_web.rs new file mode 100644 index 000000000000..f042b62de542 --- /dev/null +++ b/crates/re_memory/src/backtrace_web.rs @@ -0,0 +1,63 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = console)] + fn error(msg: String); + + type Error; + + #[wasm_bindgen(constructor)] + fn new() -> Error; + + #[wasm_bindgen(structural, method, getter)] + fn stack(error: &Error) -> String; +} + +#[derive(Hash)] +pub(crate) struct Backtrace(String); + +impl Backtrace { + pub fn new_unresolved() -> Self { + Self(Error::new().stack()) + } + + pub fn format(&mut self) -> std::sync::Arc { + trim_backtrace(&self.0).into() + } +} + +fn trim_backtrace(mut stack: &str) -> String { + let start_pattern = "__rust_alloc_zeroed"; + if let Some(start_offset) = stack.find(start_pattern) { + if let Some(next_newline) = stack[start_offset..].find('\n') { + stack = &stack[start_offset + next_newline + 1..]; + } + } + + let end_pattern = "paint_and_schedule"; // normal eframe entry-point + if let Some(end_offset) = stack.find(end_pattern) { + if let Some(next_newline) = stack[end_offset..].find('\n') { + stack = &stack[..end_offset + next_newline]; + } + } + + stack.split('\n').map(trim_line).collect::() +} + +/// Example inputs: +/// * `eframe::web::backend::AppRunner::paint::h584aff3234354fd5@http://127.0.0.1:9090/re_viewer.js line 366 > WebAssembly.instantiate:wasm-function[3352]:0x5d46b4` +/// * `getImports/imports.wbg.__wbg_new_83e4891414f9e5c1/<@http://127.0.0.1:9090/re_viewer.js:453:21` +/// * `__rg_realloc@http://127.0.0.1:9090/re_viewer.js line 366 > WebAssembly.instantiate:wasm-function[17996]:0x9b935f` +fn trim_line(mut line: &str) -> String { + if let Some(index) = line.rfind("::") { + line = &line[..index]; + } + if let Some(index) = line.find("/imports.wbg") { + line = &line[..index]; + } + if let Some(index) = line.find("@http:") { + line = &line[..index]; + } + format!("{line}\n") +} diff --git a/crates/re_memory/src/lib.rs b/crates/re_memory/src/lib.rs index 90467a5aa927..44b4701c78a3 100644 --- a/crates/re_memory/src/lib.rs +++ b/crates/re_memory/src/lib.rs @@ -9,6 +9,18 @@ mod memory_limit; mod memory_use; pub mod util; +#[cfg(not(target_arch = "wasm32"))] +mod backtrace_native; + +#[cfg(not(target_arch = "wasm32"))] +use backtrace_native::Backtrace; + +#[cfg(target_arch = "wasm32")] +mod backtrace_web; + +#[cfg(target_arch = "wasm32")] +use backtrace_web::Backtrace; + pub use { accounting_allocator::AccountingAllocator, memory_history::MemoryHistory, memory_limit::MemoryLimit, memory_use::MemoryUse, @@ -41,3 +53,18 @@ impl CountAndSize { self.size -= size; } } + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +struct BacktraceHash(u64); + +impl BacktraceHash { + pub fn new(backtrace: &Backtrace) -> Self { + use std::hash::{Hash as _, Hasher as _}; + let mut hasher = + std::hash::BuildHasher::build_hasher(&ahash::RandomState::with_seeds(0, 1, 2, 3)); + backtrace.hash(&mut hasher); + Self(hasher.finish()) + } +} + +impl nohash_hasher::IsEnabled for BacktraceHash {} diff --git a/crates/re_viewer/src/ui/memory_panel.rs b/crates/re_viewer/src/ui/memory_panel.rs index 2ebe2af24d26..98bd19dd1c8b 100644 --- a/crates/re_viewer/src/ui/memory_panel.rs +++ b/crates/re_viewer/src/ui/memory_panel.rs @@ -91,9 +91,11 @@ impl MemoryPanel { if let Some(limit) = limit.limit { ui.label(format!("Memory limit: {}", format_bytes(limit as _))); } else { - ui.label( - "You can set an upper limit of RAM use with the command-lime option --memory-limit", - ); + ui.horizontal(|ui| { + ui.spacing_mut().item_spacing.x = 0.0; + ui.label("You can set an upper limit of RAM use with the command-line option "); + ui.code("--memory-limit"); + }); ui.separator(); } @@ -113,13 +115,17 @@ impl MemoryPanel { } } + let mut is_tracking_callstacks = re_memory::accounting_allocator::is_tracking_callstacks(); + ui.checkbox(&mut is_tracking_callstacks, "Detailed allocation tracking") + .on_hover_text("This will slow down the program."); + re_memory::accounting_allocator::set_tracking_callstacks(is_tracking_callstacks); + if let Some(tracking_stats) = re_memory::accounting_allocator::tracking_stats() { ui.style_mut().wrap = Some(false); Self::tracking_stats(ui, tracking_stats); - } else { - ui.separator(); + } else if !cfg!(target_arch = "wasm32") { ui.label(format!( - "Set {RERUN_TRACK_ALLOCATIONS}=1 to turn on detailed allocation tracking." + "Set {RERUN_TRACK_ALLOCATIONS}=1 for detailed allocation tracking from startup." )); } } @@ -325,28 +331,33 @@ impl MemoryPanel { let stochastic_rate = callstack.stochastic_rate; let is_stochastic = stochastic_rate > 1; + let text = format!( + "{}{} in {} allocs (≈{} / alloc){} - {}", + if is_stochastic { "≈" } else { "" }, + format_bytes((callstack.extant.size * stochastic_rate) as _), + format_number(callstack.extant.count * stochastic_rate), + format_bytes( + callstack.extant.size as f64 / callstack.extant.count as f64 + ), + if stochastic_rate <= 1 { + String::new() + } else { + format!(" ({} stochastic samples)", callstack.extant.count) + }, + summarize_callstack(&callstack.readable_backtrace.to_string()) + ); + if ui - .button(format!( - "{}{} in {} allocs (≈{} / alloc){} - {}", - if is_stochastic { "≈" } else { "" }, - format_bytes((callstack.extant.size * stochastic_rate) as _), - format_number(callstack.extant.count * stochastic_rate), - format_bytes( - callstack.extant.size as f64 - / callstack.extant.count as f64 - ), - if stochastic_rate <= 1 { - String::new() - } else { - format!(" ({} stochastic samples)", callstack.extant.count) - }, - summarize_callstack(&callstack.readable_backtrace.to_string()) - )) + .button(text) .on_hover_text("Click to copy callstack to clipboard") .clicked() { ui.output_mut(|o| { o.copied_text = callstack.readable_backtrace.to_string(); + if o.copied_text.is_empty() { + // This is weird + o.copied_text = "No callstack available".to_owned(); + } }); } } @@ -424,6 +435,10 @@ fn summarize_callstack(callstack: &str) -> String { ("ImageCache", "ImageCache"), ("gltf", "gltf"), ("image::image", "image"), + ("epaint::text::text_layout", "text_layout"), + ("egui_wgpu", "egui_wgpu"), + ("wgpu_hal", "wgpu_hal"), + ("prepare_staging_buffer", "prepare_staging_buffer"), // ----- // Very general: ("crossbeam::channel::Sender", "crossbeam::channel::Sender"), diff --git a/crates/re_viewer/src/web.rs b/crates/re_viewer/src/web.rs index e5f60474e8d8..24684eddc654 100644 --- a/crates/re_viewer/src/web.rs +++ b/crates/re_viewer/src/web.rs @@ -6,10 +6,9 @@ use re_memory::AccountingAllocator; static GLOBAL: AccountingAllocator = AccountingAllocator::new(std::alloc::System); -/// This is the entry-point for all the web-assembly. +/// This is the entry-point for all the Wasm. /// This is called once from the HTML. /// It loads the app, installs some callbacks, then returns. -/// You can add more callbacks like this if you want to call in to your code. #[wasm_bindgen] pub async fn start(canvas_id: &str) -> std::result::Result<(), eframe::wasm_bindgen::JsValue> { // Make sure panics are logged using `console.error`. diff --git a/scripts/check.sh b/scripts/check.sh index 46366206f898..80c62d0a3a0e 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -13,7 +13,7 @@ export RUSTFLAGS="--deny warnings" export RUSTDOCFLAGS="--deny warnings --deny rustdoc::missing_crate_level_docs" cargo check --all-targets --all-features -cargo check -p re_viewer --all-features --target wasm32-unknown-unknown +cargo check -p re_viewer --all-features --target wasm32-unknown-unknown --target-dir target_wasm cargo fmt --all -- --check cargo cranky --all-targets --all-features -- --deny warnings cargo test --all-targets --all-features diff --git a/scripts/wasm_bindgen_check.sh b/scripts/wasm_bindgen_check.sh index f52e060259a6..6b7e6258a97e 100755 --- a/scripts/wasm_bindgen_check.sh +++ b/scripts/wasm_bindgen_check.sh @@ -3,7 +3,6 @@ set -eu script_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) cd "$script_path/.." - if [[ $* == --skip-setup ]] then echo "Skipping setup_web.sh" @@ -21,22 +20,25 @@ export RUSTFLAGS=--cfg=web_sys_unstable_apis echo "Building rust…" BUILD=debug # debug builds are faster +TARGET_DIR="target_wasm" +WASM_TARGET_FILEPATH="${TARGET_DIR}/wasm32-unknown-unknown/${BUILD}/${CRATE_NAME}.wasm" -(cd crates/$CRATE_NAME && - cargo build \ - --lib \ - --target wasm32-unknown-unknown -) +# make sure we re-build it: +rm -f "${WASM_TARGET_FILEPATH}" -TARGET="target" +cargo build \ + --package "${CRATE_NAME}" \ + --lib \ + --target wasm32-unknown-unknown \ + --target-dir "${TARGET_DIR}" echo "Generating JS bindings for wasm…" -rm -f "${CRATE_NAME}_bg.wasm" # Remove old output (if any) +# Remove old output (if any): +rm -f "${CRATE_NAME}.js" +rm -f "${CRATE_NAME}_bg.wasm" -TARGET_NAME="${CRATE_NAME}.wasm" -wasm-bindgen "${TARGET}/wasm32-unknown-unknown/$BUILD/$TARGET_NAME" \ - --out-dir . --no-modules --no-typescript +wasm-bindgen "${WASM_TARGET_FILEPATH}" --out-dir . --no-modules --no-typescript # Remove output: rm -f "${CRATE_NAME}_bg.wasm"