bbctrl.Trace records monotonic-anchored events from process start. Ctrl, Comm, the Web layer and __init__ are instrumented so a single GET /api/diag/timing returns a full timeline of import, controller init, AVR connection, first websocket, and first GET /. The restart-timing.js client posts performance.now() marks back so the browser side can be aligned in the same view. Used to drive the cold-boot optimisations that reduce listen latency on the Pi by ~8s.
81 lines
2.5 KiB
JavaScript
81 lines
2.5 KiB
JavaScript
// Lightweight UI-side restart/cold-load timing.
|
|
//
|
|
// Records a few key marks using performance.now(), then POSTs them to
|
|
// /api/diag/timing/ui once 'ui.first_state' has fired. Disabled by
|
|
// setting window.BBCTRL_TRACE = false before this module is loaded.
|
|
//
|
|
// Marks collected:
|
|
// script.load -- this module evaluated
|
|
// ws.open -- websocket onopen
|
|
// ws.first_msg -- first message from controller
|
|
// ui.first_state -- first message that contained controller state
|
|
// window.load -- window 'load' event
|
|
//
|
|
// Aligning these with /api/diag/timing on the server gives the full
|
|
// picture from systemd start -> bbctrl up -> WS open -> UI rendered.
|
|
"use strict";
|
|
|
|
const _enabled = typeof window !== "undefined" && window.BBCTRL_TRACE !== false;
|
|
const _t0 = (typeof performance !== "undefined" && performance.now)
|
|
? performance.now()
|
|
: Date.now();
|
|
const _navStart = (typeof performance !== "undefined" && performance.timeOrigin)
|
|
? performance.timeOrigin
|
|
: Date.now();
|
|
|
|
const marks = [];
|
|
let posted = false;
|
|
|
|
function _now() {
|
|
return (typeof performance !== "undefined" && performance.now)
|
|
? performance.now() - _t0
|
|
: Date.now() - _t0;
|
|
}
|
|
|
|
function mark(name, fields) {
|
|
if (!_enabled) return;
|
|
marks.push(Object.assign({ n: name, t: Math.round(_now()) }, fields || {}));
|
|
}
|
|
|
|
function _post() {
|
|
if (!_enabled || posted) return;
|
|
posted = true;
|
|
const body = JSON.stringify({
|
|
navStart: _navStart,
|
|
t0_perf: _t0,
|
|
href: typeof location !== "undefined" ? location.href : "",
|
|
ua: typeof navigator !== "undefined" ? navigator.userAgent : "",
|
|
marks: marks,
|
|
});
|
|
try {
|
|
if (typeof fetch === "function") {
|
|
fetch("/api/diag/timing/ui", {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: body,
|
|
keepalive: true,
|
|
}).catch(() => {});
|
|
}
|
|
} catch (e) { /* swallow */ }
|
|
}
|
|
|
|
// Record window load too; doesn't block posting.
|
|
if (_enabled && typeof window !== "undefined") {
|
|
window.addEventListener("load", () => mark("window.load"));
|
|
}
|
|
|
|
mark("script.load");
|
|
|
|
module.exports = {
|
|
enabled: _enabled,
|
|
mark: mark,
|
|
onWsOpen: () => mark("ws.open"),
|
|
onWsFirstMessage: () => mark("ws.first_msg"),
|
|
onFirstState: () => {
|
|
mark("ui.first_state");
|
|
// Defer slightly so any synchronous render finishes first.
|
|
setTimeout(_post, 100);
|
|
},
|
|
flush: _post,
|
|
};
|