跳到主要内容

WebAssembly 调试

问题

如何调试 Rust 编译到 WebAssembly 的应用?

答案

Wasm 调试挑战

挑战说明
无 stdoutWasm 无法直接 println!
堆栈信息受限panic 信息默认被裁剪
内存不透明JS 侧看不到 Wasm 内存细节
Source Map 有限DWARF 调试信息支持尚不完善

基础调试:console_log

use wasm_bindgen::prelude::*;

// 绑定 JS 的 console.log
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}

// 方便的宏
macro_rules! console_log {
($($t:tt)*) => (log(&format!($($t)*)))
}

#[wasm_bindgen]
pub fn process_data(input: &str) -> String {
console_log!("输入: {}", input);

let result = input.to_uppercase();
console_log!("结果: {}", result);

result
}

使用 console_error_panic_hook

Cargo.toml
[dependencies]
console_error_panic_hook = "0.1"
wasm-bindgen = "0.2"
use wasm_bindgen::prelude::*;

/// 初始化时注册 panic hook,使 panic 信息输出到浏览器控制台
#[wasm_bindgen(start)]
pub fn init() {
console_error_panic_hook::set_once();
}

#[wasm_bindgen]
pub fn divide(a: f64, b: f64) -> f64 {
if b == 0.0 {
panic!("除以零: a={}, b={}", a, b);
// 有了 panic hook,浏览器控制台会显示完整堆栈
}
a / b
}

使用 tracing-wasm

use tracing::{info, warn, instrument};
use tracing_wasm::WASMLayerConfigBuilder;

#[wasm_bindgen(start)]
pub fn init() {
let config = WASMLayerConfigBuilder::default()
.set_max_level(tracing::Level::DEBUG)
.build();
tracing_wasm::set_as_global_default_with_config(config);
}

#[instrument] // 自动记录函数入参和耗时
#[wasm_bindgen]
pub fn compute(n: u32) -> u32 {
info!("开始计算");
let result = fibonacci(n);
info!(result, "计算完成");
result
}

浏览器 DevTools 调试

# 构建时保留调试信息
wasm-pack build --debug

# 或在 Cargo.toml 中配置
[profile.dev]
opt-level = 0 # 不优化
debug = true # 保留 DWARF 调试信息

Chrome DevTools 中可以在 Sources 面板看到 .wasm 文件,配合 DWARF 插件可以设断点。

wasm-bindgen-test 测试

use wasm_bindgen_test::*;

wasm_bindgen_test_configure!(run_in_browser);

#[wasm_bindgen_test]
fn test_greet() {
let result = greet("World");
assert_eq!(result, "Hello, World!");
}

#[wasm_bindgen_test]
async fn test_async_fetch() {
let data = fetch_data().await;
assert!(!data.is_empty());
}
# 在浏览器或 Node 中运行 wasm 测试
wasm-pack test --chrome --headless
wasm-pack test --node

常见面试问题

Q1: Wasm 中 panic 该如何处理?

答案

  1. 使用 console_error_panic_hook 将 panic 信息输出到 Console
  2. 生产环境设置 panic = "abort" 减小体积
  3. 在关键入口用 std::panic::catch_unwind 捕获,转为 JS 错误
  4. 优先使用 Result 而非 panic

Q2: 如何分析 Wasm 的包体积?

答案

  • twiggy 工具分析 .wasm 文件各符号大小
  • wasm-opt -Oz 优化体积
  • wee_alloc 替换默认分配器(减少约 10KB)
  • cargo bloat --target wasm32-unknown-unknown 查看函数体积

相关链接