diag: add startup-timing trace and /api/diag/timing endpoint

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.
This commit is contained in:
2026-05-03 14:06:17 +02:00
parent 94270e7725
commit 0b5ab2ff3b
6 changed files with 367 additions and 11 deletions

View File

@@ -36,6 +36,13 @@ import datetime
from pkg_resources import Requirement, resource_filename
# Trace must be imported before the rest of bbctrl so its monotonic
# anchor is the earliest reasonable point and so import-time costs of
# heavy submodules (camotics gplan.so, sockjs, tornado, etc.) are
# attributable in /api/diag/timing.
import bbctrl.Trace as Trace
Trace.mark('imports.bbctrl.start')
from bbctrl.RequestHandler import RequestHandler
from bbctrl.APIHandler import APIHandler
from bbctrl.FileHandler import FileHandler
@@ -64,6 +71,8 @@ import bbctrl.v4l2 as v4l2
import bbctrl.Log as log
import bbctrl.ObjGraph as ObjGraph
Trace.mark('imports.bbctrl.end')
ctrl = None
@@ -167,19 +176,28 @@ def parse_args():
def run():
global ctrl
Trace.mark('run.enter')
args = parse_args()
Trace.mark('args.parsed')
# Set signal handler
signal.signal(signal.SIGTERM, on_exit)
# Create ioloop
ioloop = tornado.ioloop.IOLoop.current()
Trace.mark('ioloop.created')
# Set ObjGraph signal handler
if args.debug: Debugger(ioloop, args.debug)
# Start server
web = Web(args, ioloop)
with Trace.span('web.init'):
web = Web(args, ioloop)
Trace.mark('listen', port=args.port, addr=args.addr)
# Notify systemd we are ready (no-op when not under systemd).
Trace.sd_notify('READY=1\nSTATUS=listening on %s:%d\n' %
(args.addr, args.port))
try:
ioloop.start()