Debugging
Before we write much more code, we will want to have some debugging tools in ourbelt for when things go wrong. Take a moment to review the reference pagelisting tools and approaches available for debugging Rust-generatedWebAssembly.
Enable Logging for Panics
If our code panics, we want informative error messages to appear in thedeveloper console.
Our wasm-pack-template
comes with an optional, enabled-by-default dependencyon the console_error_panic_hook
crate that is configured inwasm-game-of-life/src/utils.rs
. All we need to do is install the hook in aninitialization function or common code path. We can call it inside theUniverse::new
constructor in wasm-game-of-life/src/lib.rs
:
# #![allow(unused_variables)]
#fn main() {
pub fn new() -> Universe {
utils::set_panic_hook();
// ...
}
#}
Add Logging to our Game of Life
Let's use the console.log
function via the web-sys
crate to add somelogging about each cell in our Universe::tick
function.
First, add web-sys
as a dependency and enable its "console"
feature inwasm-game-of-life/Cargo.toml
:
[dependencies.web-sys]
version = "0.3"
features = [
"console",
]
For ergonomics, we'll wrap the console.log
function up in a println!
-stylemacro:
# #![allow(unused_variables)]
#fn main() {
extern crate web_sys;
// A macro to provide `println!(..)`-style syntax for `console.log` logging.
macro_rules! log {
( $( $t:tt )* ) => {
web_sys::console::log_1(&format!( $( $t )* ).into());
}
}
#}
Now, we can start logging messages to the console by inserting calls to log
inRust code. For example, to log each cell's state, live neighbors count, and nextstate, we could modify wasm-game-of-life/src/lib.rs
like this:
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;
}
}
Using a Debugger to Pause Between Each Tick
For example, we can use the debugger to pause on each iteration of ourrenderLoop
function by placing a JavaScript debugger;
statementabove our call to universe.tick()
.
const renderLoop = () => {
debugger;
universe.tick();
drawGrid();
drawCells();
requestAnimationFrame(renderLoop);
};
This provides us with a convenient checkpoint for inspecting logged messages,and comparing the currently rendered frame to the previous one.
Exercises
Add logging to the
tick
function that records the row and column of eachcell that transitioned states from live to dead or vice versa.Introduce a
panic!()
in theUniverse::new
method. Inspect the panic'sbacktrace in your Web browser's JavaScript debugger. Disable debug symbols,rebuild without theconsole_error_panic_hook
optional dependency, andinspect the stack trace again. Not as useful is it?