调试

在我们编写更多代码之前,我们需要一些,在出现问题时使用的调试工具。花一点时间回顾下参考页面:可用于调试 Rust 生成的 WebAssembly 的工具和方法

为恐慌(panic),启用日志记录,

如果我们的代码发生恐慌,我们希望在开发者控制台中,显示信息性错误消息。

我们的wasm-pack-template附带一个console_error_panic_hook,其有个可选的,默认启用的依赖项,你可在wasm-game-of-life/src/utils.rs看到已配置了。我们需要做的就是在初始化函数或常用代码路径中安装钩子。我们可以在wasm-game-of-life/src/lib.rs中的,Universe::new构造函数里面调用它:


# #![allow(unused_variables)]
#fn main() {
pub fn new() -> Universe {
    utils::set_panic_hook();

    // ...
}
#}

记录功能,添加到我们的生命游戏中

运用console.log函数的方式,是由web-sys添加一些记录日志,记录我们Universe::tick函数的每个细胞。

首先,在wasm-game-of-life/Cargo.toml添加web-sys依赖项,并启用它的"console"功能(特性):

[dependencies.web-sys]
version = "0.3"
features = [
  "console",
]

为了更符合偷懒,我们将包装console.log到类println!形式的宏中:


# #![allow(unused_variables)]
#fn main() {
extern crate web_sys;

// 一个 macro(宏) 提供 `println!(..)`-形式 语法,给到 `console.log` 日志功能.
macro_rules! log {
    ( $( $t:tt )* ) => {
        web_sys::console::log_1(&format!( $( $t )* ).into());
    }
}
#}

现在,我们可以在 Rust 代码中,插入log调用,开始 console 的信息记录。例如,要记录每个单元(cell)的状态,实时邻居计数,以及下一个状态,我们可以修改wasm-game-of-life/src/lib.rs像这样:

diff --git a/src/lib.rs b/src/lib.rs
index f757641..a30e107 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -123,6 +122,14 @@ impl Universe {
                 let cell = self.cells[idx];
                 let live_neighbors = self.live_neighbor_count(row, col);

+                log!(
+                    "cell[{}, {}] is initially {:?} and has {} live neighbors",
+                    row,
+                    col,
+                    cell,
+                    live_neighbors
+                );
+
                 let next_cell = match (cell, live_neighbors) {
                     // Rule 1: Any live cell with fewer than two live neighbours
                     // dies, as if caused by underpopulation.
@@ -140,6 +147,8 @@ impl Universe {
                     (otherwise, _) => otherwise,
                 };

+                log!("    it becomes {:?}", next_cell);
+
                 next[idx] = next_cell;
             }
         }

在每个 Tick 之间,能用调试器暂停下

浏览器的步进调试器,对检查 Rust 生成的 WebAssembly 与 JavaScript 的交互 非常有用。

例如,我们可用调试器,在我们的renderLoop函数每次迭代时,暂停。只需要放置一个 JavaScript 的 debugger;声明,位于我们的universe.tick()之上。

const renderLoop = () => {
  debugger;
  universe.tick();

  drawGrid();
  drawCells();

  requestAnimationFrame(renderLoop);
};

这为我们提供了一个方便的检查点,用于检查记录的消息,并将当前渲染的’帧’与前一’帧’进行比较。

Screenshot of debugging the Game of Life

练习

  • 添加日志记录到tick函数,这样就可以记录每个单元格的行和列,知道其状态是从活转换为死,或是相反。

  • 声明一个panic!()Universe::new方法里面。在 Web 浏览器的 JavaScript 调试器中,检查恐慌的回溯。要做到禁用调试符号,只用不带console_error_panic_hook可选依赖项,重新构建就好了,并再次检查堆栈跟踪。可以吧!?