diff --git a/src/py/bbctrl/Trace.py b/src/py/bbctrl/Trace.py index 56093a8..7232fee 100644 --- a/src/py/bbctrl/Trace.py +++ b/src/py/bbctrl/Trace.py @@ -37,6 +37,38 @@ _events = [] # list of dicts: {t, name, fields} _ui_timing = None # last timeline POSTed by the browser +def _read_kernel_anchors(): + """Return (btime_wall, uptime_at_anchor) so we can express bbctrl events + in seconds since kernel boot. + + btime_wall: wall-clock epoch seconds when the kernel booted (from + /proc/stat 'btime'). + uptime_at_anchor: monotonic offset (seconds since kernel boot) at the + moment Trace was imported. Equivalent to (Trace anchor) - btime + in wall time, but read directly from /proc/uptime so it isn't + sensitive to wall-clock skew. + """ + btime = None + uptime_at_anchor = None + try: + with open('/proc/stat') as f: + for line in f: + if line.startswith('btime '): + btime = int(line.split()[1]) + break + except Exception: + pass + try: + with open('/proc/uptime') as f: + uptime_at_anchor = float(f.read().split()[0]) + except Exception: + pass + return btime, uptime_at_anchor + + +_btime_wall, _uptime_at_anchor = _read_kernel_anchors() + + def now(): return time.monotonic() - _t0_monotonic @@ -91,6 +123,14 @@ def set_ui_timing(data): _ui_timing = data +def _current_uptime(): + try: + with open('/proc/uptime') as f: + return float(f.read().split()[0]) + except Exception: + return None + + def timeline(): with _lock: events = list(_events) @@ -102,6 +142,11 @@ def timeline(): 'pid': os.getpid(), 'events': events, 'ui': _ui_timing, + # Kernel-boot anchors so the timeline can be expressed in + # "seconds since power on". + 'btime_wall': _btime_wall, + 'uptime_at_anchor': _uptime_at_anchor, + 'uptime_now': _current_uptime(), } diff --git a/src/py/bbctrl/Web.py b/src/py/bbctrl/Web.py index d303055..b149e8c 100644 --- a/src/py/bbctrl/Web.py +++ b/src/py/bbctrl/Web.py @@ -999,6 +999,23 @@ class StaticFileHandler(tornado.web.StaticFileHandler): self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') + def prepare(self): + # Mark the first request for the index page so we can see when + # chromium actually started fetching the UI on cold boot. + try: + app = self.application + if not getattr(app, '_first_root_get', False): + # Treat any GET '/' or '/index.html' as the root fetch. + p = self.request.path + if p in ('/', '/index.html', ''): + app._first_root_get = True + import bbctrl.Trace as _T + _T.mark('web.first_root_get', + ip=self.request.remote_ip, + ua=(self.request.headers.get('User-Agent') or '')[:60]) + except Exception: pass + return super().prepare() + class Web(tornado.web.Application): def __init__(self, args, ioloop): self.args = args