on master help sign in
Add an "strace"-style DOM tracing feature

This is really helpful for investigating the quality of change lists we emit and comparing them to how other virtual DOM libraries apply changes to the physical DOM.

commit2c6e730a87472d822438999e49254089e93d58a1 authorNick Fitzgerald <fitzgen@gmail.com> dateMon 22 Apr 2019, 11:32 (8 years ago) parent08ccdbb
5 files changed+104
Cargo.toml+8 js/strace.js+71 src/lib.rs+1 src/strace.rs+22 src/vdom.rs+2
Cargo.toml+8
@@ -29,6 +29,14 @@ harness = false
2929required-features = ["xxx-unstable-internal-use-only"]
3030
3131[features]
32+# Don't rely on this feature! Its behavior will change and it could go away at
33+# any time. This enables monkey-patching DOM methods so that we wrap them and
34+# trace what DOM methods are used and how many times they are invoked when
35+# applying our emitted change lists to the physical DOM. This is useful for
36+# evaluating the differences between how Dodrio applies changes from virtual
37+# nodes to the physical DOM versus how other virtual DOM libraries do the same.
38+xxx-unstable-strace = []
39+
3240# Don't use this feature! It only exists for exposing otherwise-internal APIs
3341# for testing and benchmarks.
3442xxx-unstable-internal-use-only = []
js/strace.jsnew file+71
@@ -0,0 +1,71 @@
1+export function initStrace() {
2+ instrumentAll(Element.prototype);
3+ instrumentAll(Document.prototype);
4+ instrumentAll(Node.prototype);
5+ instrumentAll(EventTarget.prototype);
6+}
7+
8+const counts = new Map;
9+let timer = null;
10+
11+function log(ctorName, kind, key) {
12+ const entry = `${kind} ${ctorName}#${key}`
13+ const count = counts.get(entry);
14+ if (count === undefined) {
15+ counts.set(entry, 1);
16+ } else {
17+ counts.set(entry, count + 1);
18+ }
19+
20+ if (timer !== null) {
21+ clearTimeout(timer);
22+ }
23+ timer = setTimeout(dumpAndReset, 100);
24+}
25+
26+function dumpAndReset() {
27+ const data = [...counts]
28+ .sort((a, b) => b[1] - a[1])
29+ .map(a => ({ "DOM Method": a[0], "Count": a[1] }));
30+ console.table(data, ["DOM Method", "Count"]);
31+ counts.clear();
32+}
33+
34+function instrument(proto, key, desc) {
35+ const ctorName = proto.constructor.name;
36+
37+ if (typeof desc.value == "function") {
38+ const f = desc.value;
39+ desc.value = function (...args) {
40+ log(ctorName, "call", key);
41+ return f.apply(this, args);
42+ };
43+ }
44+
45+ if (typeof desc.get == "function") {
46+ const get = desc.get;
47+ desc.get = function (...args) {
48+ log(ctorName, "get", key);
49+ return get.apply(this, args);
50+ };
51+ }
52+
53+ if (typeof desc.set == "function") {
54+ const set = desc.set;
55+ desc.set = function (...args) {
56+ log(ctorName, "set", key);
57+ return set.apply(this, args);
58+ };
59+ }
60+
61+ Object.defineProperty(proto, key, desc);
62+}
63+
64+function instrumentAll(proto) {
65+ for (const key of Object.keys(proto)) {
66+ const desc = Object.getOwnPropertyDescriptor(proto, key);
67+ if (desc.configurable) {
68+ instrument(proto, key, desc);
69+ }
70+ }
71+}
src/lib.rs+1
@@ -92,6 +92,7 @@ mod events;
9292mod node;
9393mod render;
9494mod render_context;
95+mod strace;
9596mod vdom;
9697
9798pub mod builder;
src/strace.rsnew file+22
@@ -0,0 +1,22 @@
1+cfg_if::cfg_if! {
2+ if #[cfg(feature = "xxx-unstable-strace")] {
3+ use wasm_bindgen::prelude::*;
4+
5+ #[wasm_bindgen(module = "/js/strace.js")]
6+ extern "C" {
7+ #[wasm_bindgen(js_name = initStrace)]
8+ fn really_init_strace();
9+ }
10+
11+ pub fn init_strace() {
12+ use std::sync::Once;
13+ static STRACE: Once = Once::new();
14+ STRACE.call_once(|| {
15+ really_init_strace();
16+ });
17+ }
18+ } else {
19+ #[inline]
20+ pub fn init_strace() {}
21+ }
22+}
src/vdom.rs+2
@@ -161,6 +161,8 @@ impl Vdom {
161161 /// Construct a `Vdom` with the already-boxed-as-a-trait-object root
162162 /// rendering component.
163163 pub fn with_boxed_root_render(container: &crate::Element, component: Box<RootRender>) -> Vdom {
164+ crate::strace::init_strace();
165+
164166 let dom_buffers = [Bump::new(), Bump::new()];
165167 let change_list = ManuallyDrop::new(ChangeListPersistentState::new(container));
166168