#!/usr/bin/env python3
"""Local CEF startup harness with synthetic fixtures (stdlib only).
Scenarios:
process-warm one unrecorded warm-up, then new processes sharing an
isolated previously initialized CEF root-cache profile
profile-fresh new process, new profile each sample
relay-window a real long-lived owner browser holds the relay; samples
launch browse with DUMBER_BROWSER_FRESH_WINDOW=1 in that
same isolated profile
reopen-window like relay-window, but each sample first closes all owner
windows through the diagnostic relay action, then reopens
through the relay; verifies the same owner process serves
the reopen without a second CEF initialization
Second-window contract (relay-window, reopen-window): the owner captures
its own log, readiness is its first presentation (never a fixed sleep),
and every sample records owner_cef_init_count, which must stay at 1.
Cold-process numbers (process-warm, profile-fresh) and resident-reopen
observations are reported separately and never averaged together.
--idle-timeout-ms forwards the product residency knob into generated
configs (default 0 = exit with last window; reopen-window only reuses
the same process with a nonzero timeout). Second-window latency is
first_document_observation_ms (spawn to first fixture document request
on the harness clock); paint-level timing stays open until page
beacons are delivered.
Fixtures (loopback only): static, delayed, redirect, cache.
Protocol: fixture pages report PerformanceObserver results through a
token-authenticated loopback beacon (/__beacon with run/nav/event/value
parameters) served by the harness HTTP server. Beacon arrivals carry the
harness monotonic timestamp. External spawn-to-arrival is recorded as an
explicitly named observation duration, including output-delivery overhead;
it is never presented as an exact child event timestamp.
Clock domains are kept separate: child t_ms values are never subtracted from
the harness wall clock. GTK after-paint is labeled GTK paint, never compositor
presentation. Missing target FCP remains incomplete; the harness never stops
on a blank GTK summary.
"""
import argparse
import hashlib
import json
import os
import re
import secrets
import shutil
import socket
import subprocess
import sys
import tempfile
import threading
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs, urlparse
TOKEN_RE = re.compile(r"^[0-9a-f]{16}$")
NAV_RE = re.compile(r"^[0-9a-f]{16}$")
BEACON_EVENTS = ("first-contentful-paint", "largest-contentful-paint", "fixture-load")
DEFAULT_POST_NAV_CUTOFF_SECONDS = 5.0
# Deterministic 1x1 RGBA PNG so image endpoints exercise a successful decode
# and paint instead of the decode-failure path.
FIXTURE_PNG = bytes.fromhex(
"89504e470d0a1a0a"
"0000000d49484452000000010000000108060000001f15c489"
"0000000b4944415478da6360000300000700012122db13"
"0000000049454e44ae426082"
)
def post_nav_cutoff_seconds():
try:
value = float(os.environ.get("DUMBER_MEASURE_CUTOFF_SECONDS", DEFAULT_POST_NAV_CUTOFF_SECONDS))
except ValueError:
return DEFAULT_POST_NAV_CUTOFF_SECONDS
return min(30.0, max(0.1, value))
def navigation_deadline_seconds():
# Bounded wait for the correlated document after spawn when no
# document has arrived yet. Overridable for fast unit tests.
try:
value = float(os.environ.get("DUMBER_MEASURE_NAV_DEADLINE_SECONDS", ""))
if value > 0:
return min(60.0, value)
except ValueError:
pass
return post_nav_cutoff_seconds() + 5.0
def is_residency_valid(result, scenario):
# Fixture completeness (document count, beacons) is not residency
# validity. A resident sample is valid only when the launcher handed
# off cleanly to a live, unchanged owner that initialized CEF exactly
# once, the launcher itself never initialized CEF (a fallback cold
# start would hide in the dead owner's log), and a reopen additionally
# completed its close. Returns True/False, or None outside resident
# scenarios.
if scenario not in ("relay-window", "reopen-window"):
return None
if result.get("fallback_spawn") or not result.get("relayed"):
return False
if result.get("launcher_returncode") != 0:
return False
if result.get("owner_cef_init_count") != 1:
return False
if result.get("launcher_cef_init_count", 0) != 0:
return False
if scenario == "reopen-window":
reopen = result.get("reopen") or {}
if not (reopen.get("close_acknowledged")
and reopen.get("quiescence_settled", False)
and reopen.get("same_process")
and reopen.get("owner_alive_after")):
return False
return True
class FixtureState:
def __init__(self):
self.lock = threading.Lock()
self.document_requests = []
self.subresource_requests = []
self.beacons = []
self.first_document_monotonic = None
def build_fixture_html(kind, run_token, nav_token):
observer = (
"" % (run_token, nav_token)
)
if kind == "static":
return (
"
"
''
+ observer
+ ""
)
raise ValueError("unknown fixture: " + kind)
class Handler(BaseHTTPRequestHandler):
state = None
fixture = "static"
run_token = ""
nav_token = ""
delay_seconds = 0.3
def log_message(self, *args):
pass
def _record(self, document):
with self.state.lock:
entry = {"path": self.path, "time": time.monotonic()}
if document:
self.state.document_requests.append(entry)
if self.state.first_document_monotonic is None:
self.state.first_document_monotonic = entry["time"]
else:
self.state.subresource_requests.append(entry)
def _document_tokens_valid(self, parsed):
# Document routes carry the sample tokens by construction; an
# uncorrelated request (stale tab, foreign client, port reuse)
# must neither establish the first-document timestamp nor be
# served the current sample's tokens.
query = parse_qs(parsed.query)
run = query.get("run", [None])[0]
nav = query.get("nav", [None])[0]
return (
isinstance(run, str) and TOKEN_RE.fullmatch(run) and run == self.run_token
and isinstance(nav, str) and NAV_RE.fullmatch(nav) and nav == self.nav_token
)
def do_GET(self):
parsed = urlparse(self.path)
path = parsed.path
if path == "/__beacon":
self._handle_beacon(parsed)
return
if path in ("/", "/index.html", "/target", "/redirect"):
if not self._document_tokens_valid(parsed):
self.send_response(404)
self.end_headers()
return
self._record(True)
if path == "/redirect":
target = "/target?run=%s&nav=%s" % (self.run_token, self.nav_token)
self.send_response(302)
self.send_header("Location", target)
self.end_headers()
return
if self.fixture == "delayed" and path == "/":
time.sleep(self.delay_seconds)
body = build_fixture_html(
"static" if self.fixture == "redirect" and path == "/" else self.fixture,
self.run_token,
self.nav_token,
).encode()
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
return
if path in ("/app.css", "/pixel.png", "/cacheable.js", "/cacheable.png"):
self._record(False)
if path.endswith(".png"):
body = FIXTURE_PNG
else:
body = b"/* fixture */"
self.send_response(200)
if path.endswith(".css"):
self.send_header("Content-Type", "text/css")
elif path.endswith(".js"):
self.send_header("Content-Type", "application/javascript")
else:
self.send_header("Content-Type", "image/png")
if self.fixture == "cache":
self.send_header("Cache-Control", "private, max-age=3600")
else:
self.send_header("Cache-Control", "no-store")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
return
self._record(False)
self.send_response(404)
self.end_headers()
def _handle_beacon(self, parsed):
query = parse_qs(parsed.query)
run = query.get("run", [None])[0]
nav = query.get("nav", [None])[0]
event = query.get("event", [None])[0]
value_raw = query.get("value", [None])[0]
valid = (
isinstance(run, str) and TOKEN_RE.fullmatch(run) and run == self.run_token
and isinstance(nav, str) and NAV_RE.fullmatch(nav) and nav == self.nav_token
and event in BEACON_EVENTS
)
try:
value = int(value_raw) if valid else None
valid = valid and value is not None and value >= 0
except (TypeError, ValueError):
valid = False
if not valid:
self.send_response(400)
self.end_headers()
return
with self.state.lock:
self.state.beacons.append({
"event": event,
"value_ms": value,
"arrival_monotonic": time.monotonic(),
})
self.send_response(204)
self.end_headers()
def make_sample_handler(state, fixture, run_token, nav_token):
# Per-sample handler state: the base Handler only carries defaults.
# Binding state on a fresh subclass per sample keeps surviving request
# threads of a previous sample from adopting a later sample's tokens.
return type("SampleHandler", (Handler,), {
"state": state,
"fixture": fixture,
"run_token": run_token,
"nav_token": nav_token,
})
def parse_child_output(lines):
milestones = []
for line in lines:
try:
event = json.loads(line)
except (json.JSONDecodeError, ValueError):
continue
if isinstance(event, dict) and event.get("message") == "startup_trace: milestone":
milestones.append(event)
return milestones
DISPLAY_PASSTHROUGH_VARS = ("DBUS_SESSION_BUS_ADDRESS", "XAUTHORITY")
def _escape_toml_basic(value):
# Full TOML basic-string escaping: backslash, quote, and every
# control character (raw newlines/tabs/other C0 controls are invalid
# in basic strings). Paths with such characters stay representable
# instead of producing broken configuration.
out = []
for char in value:
code = ord(char)
if char == "\\":
out.append("\\\\")
elif char == '"':
out.append('\\"')
elif char == "\n":
out.append("\\n")
elif char == "\t":
out.append("\\t")
elif char == "\r":
out.append("\\r")
elif char == "\b":
out.append("\\b")
elif char == "\f":
out.append("\\f")
elif code < 0x20 or code == 0x7F:
out.append("\\u%04X" % code)
else:
out.append(char)
return "".join(out)
def write_child_config(config_home, cef_dir, idle_timeout_ms=0):
"""Force JSON debug logging so milestone parsing works.
Without this, isolated XDG homes fall back to text logs and
parse_child_output finds zero milestones. Idempotent: relay owners
share one config across samples. idle_timeout_ms selects the product
residency knob (0 = product default, exit with last window)."""
config_dir = os.path.join(config_home, "dumber")
os.makedirs(config_dir, exist_ok=True)
path = os.path.join(config_dir, "config.toml")
content = (
'[logging]\nlevel = "debug"\nformat = "json"\n'
'enable_file_log = false\n\n[engine.cef]\ncef_dir = "%s"\n'
'idle_runtime_timeout_ms = %d\n'
% (_escape_toml_basic(cef_dir), idle_timeout_ms)
)
with open(path, "w") as handle:
handle.write(content)
return path
def ensure_wayland_socket(runtime_dir, env):
"""Point a relative Wayland display at the real compositor socket.
Isolating XDG_RUNTIME_DIR breaks relative WAYLAND_DISPLAY values
(the compositor socket lives in the real runtime dir). Symlinking the
socket into the isolated dir is not an option: isolated test paths
easily exceed the 108-byte unix-socket limit and CEF aborts with
"File name too long". Instead, when the display is a relative name
whose socket exists in the real runtime dir, override WAYLAND_DISPLAY
with its absolute path, which Wayland clients use as-is. The isolated
runtime dir stays untouched. Returns True when rendering connectivity
is preserved or Wayland is not in use."""
display = env.get("WAYLAND_DISPLAY", "")
if not display or "/" in display:
return True
os.makedirs(runtime_dir, exist_ok=True)
real_runtime = os.environ.get("XDG_RUNTIME_DIR", "/run/user/%d" % os.getuid())
source = os.path.join(real_runtime, display)
if os.path.exists(source):
env["WAYLAND_DISPLAY"] = source
return True
return False
def count_cef_inits(lines):
"""Count CEF initialization completions in child output lines.
Each process initializes CEF at most once: a resident owner serving
N second windows must still show exactly one cef_initialized
milestone. Only the milestone event counts; the InitWithApp log line
describes the same init and is ignored."""
count = 0
for line in lines:
try:
event = json.loads(line)
except (json.JSONDecodeError, ValueError):
continue
if (
isinstance(event, dict)
and event.get("message") == "startup_trace: milestone"
and event.get("milestone") == "cef_initialized"
):
count += 1
return count
def wait_for_owner_log_line(log_path, needle, timeout_seconds=30.0):
"""Wait until the owner log contains needle. Bounded; False on timeout."""
end = time.monotonic() + timeout_seconds
while time.monotonic() < end:
try:
with open(log_path, errors="replace") as handle:
for line in handle:
if needle in line:
return True
except OSError:
pass
time.sleep(0.2)
return False
def scenario_env(cef_dir, dirs, extra=None, idle_timeout_ms=0):
env = {
"HOME": os.environ.get("HOME", ""),
"PATH": os.environ.get("PATH", ""),
"LANG": os.environ.get("LANG", "C.UTF-8"),
"DISPLAY": os.environ.get("DISPLAY", ""),
"WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
"XDG_CONFIG_HOME": dirs["config"],
"XDG_DATA_HOME": dirs["data"],
"XDG_STATE_HOME": dirs["state"],
"XDG_CACHE_HOME": dirs["cache"],
"XDG_RUNTIME_DIR": dirs["runtime"],
"CEF_DIR": cef_dir,
"DUMBER_CEF_DIR": cef_dir,
"DUMBER_CEF_ROOT_CACHE_PATH": dirs["root_cache"],
}
if extra:
env.update(extra)
for name in DISPLAY_PASSTHROUGH_VARS:
value = os.environ.get(name)
if value:
env[name] = value
for key in ("config", "data", "state", "cache", "runtime"):
os.makedirs(dirs[key], exist_ok=True)
os.makedirs(dirs["root_cache"], exist_ok=True)
write_child_config(dirs["config"], cef_dir, idle_timeout_ms)
ensure_wayland_socket(dirs["runtime"], env)
return env
def wait_for_relay_socket(search_root, timeout_seconds=30.0):
# The relay socket lives under XDG_STATE_HOME
# (/[dumber/]runtime//browser-launch.sock), not the
# XDG_RUNTIME dir: walk the state tree. Keep scenario outputs shallow:
# unix-socket paths are limited to 108 bytes and deep evidence dirs
# make the relay unreachable.
end = time.monotonic() + timeout_seconds
while time.monotonic() < end:
for root, _, files in os.walk(search_root):
if "browser-launch.sock" in files:
path = os.path.join(root, "browser-launch.sock")
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(2.0)
try:
sock.connect(path)
return path
except OSError:
pass
finally:
sock.close()
time.sleep(0.2)
return None
def start_relay_owner(binary, cef_dir, scenario_dir, diagnostic_close=False,
idle_timeout_ms=0):
dirs = {
"config": os.path.join(scenario_dir, "owner-config"),
"data": os.path.join(scenario_dir, "owner-data"),
"state": os.path.join(scenario_dir, "owner-state"),
"cache": os.path.join(scenario_dir, "owner-cache"),
"runtime": os.path.join(scenario_dir, "owner-runtime"),
"root_cache": os.path.join(scenario_dir, "shared-profile"),
"owner_log": os.path.join(scenario_dir, "owner.log"),
}
# scenario_env only reads the six XDG/root-cache keys; owner_log
# rides along for CEF-init counting without affecting the child env.
env = scenario_env(cef_dir, dirs, idle_timeout_ms=idle_timeout_ms)
if diagnostic_close:
# Owned window-close mechanism for the reopen scenario only: the
# relay honors it solely in processes carrying this variable. It is
# never a general unauthenticated shutdown command.
env = dict(env)
env["DUMBER_DIAGNOSTIC_WINDOW_CLOSE"] = "1"
log_file = open(dirs["owner_log"], "w")
try:
proc = subprocess.Popen(
[binary, "browse", "about:blank"],
stdout=log_file,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
env=env,
)
except OSError:
log_file.close()
return None, None, None
if proc.poll() is not None:
log_file.close()
return None, None, None
# Readiness is the owner's first presentation, not a fixed sleep:
# only then is the relay guaranteed to serve second windows.
ready = wait_for_owner_log_line(
dirs["owner_log"], "startup_trace: first presentation")
log_file.close()
if not ready or proc.poll() is not None:
stop_proc(proc)
return None, None, None
socket_path = wait_for_relay_socket(dirs["state"])
if socket_path is None:
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
return None, None, None
return proc, dirs, socket_path
def stop_proc(proc):
if proc is None or proc.poll() is not None:
return
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait(timeout=5)
def send_diagnostic_close(socket_path, timeout_seconds=10.0):
"""Ask the relay owner to close all windows. Returns True only on an
explicit acknowledgement. Never a general shutdown command: owners
honor it solely with DUMBER_DIAGNOSTIC_WINDOW_CLOSE=1."""
request = json.dumps({"request_id": secrets.token_hex(8), "action": "close-all-windows"}) + "\n"
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(timeout_seconds)
try:
sock.connect(socket_path)
sock.sendall(request.encode())
received = b""
while b"\n" not in received:
chunk = sock.recv(4096)
if not chunk:
break
received += chunk
try:
response = json.loads(received.decode())
except ValueError:
return False
return bool(response.get("accepted")) and not response.get("error")
except OSError:
return False
finally:
sock.close()
def child_pids(root_pid):
"""Direct child pids of root_pid via /proc (stdlib only)."""
children = []
try:
entries = os.listdir("/proc")
except OSError:
return children
for entry in entries:
if not entry.isdigit():
continue
try:
with open("/proc/%s/stat" % entry) as handle:
parts = handle.read().rsplit(")", 1)[1].split()
if int(parts[1]) == root_pid:
children.append(int(entry))
except (OSError, ValueError, IndexError):
continue
return children
def proc_starttime(pid):
"""Process start time (jiffies since boot) via /proc, or None.
Combined with the pid it forms an independently observed identity that
survives Popen-handle reuse and detects pid recycling."""
try:
with open("/proc/%d/stat" % pid) as handle:
parts = handle.read().rsplit(")", 1)[1].split()
return int(parts[19])
except (OSError, ValueError, IndexError):
return None
def wait_for_child_quiescence(owner_pid, settle_seconds=2.0, timeout_seconds=15.0):
"""Wait until the owner's child count is stable across settle_seconds.
Window close is asynchronous: the relay acknowledgement only means the
request was accepted, so reopening must wait for an observable steady
state instead of a fixed sleep. Returns (stable_count, settled):
settled is False on timeout, and a timeout must fail the reopen, not
silently pass. Child counts stay diagnostics only: CEF process
topology does not map one-to-one to windows, so stability alone never
proves zero windows or resident-idle entry."""
end = time.monotonic() + timeout_seconds
last_change = time.monotonic()
last_count = len(child_pids(owner_pid))
while time.monotonic() < end:
time.sleep(0.5)
count = len(child_pids(owner_pid))
if count != last_count:
last_count = count
last_change = time.monotonic()
elif time.monotonic() - last_change >= settle_seconds:
return count, True
return len(child_pids(owner_pid)), False
def proc_rss_kb(pid):
"""Resident memory of pid in KiB via /proc, or None."""
try:
with open("/proc/%d/status" % pid) as handle:
for line in handle:
if line.startswith("VmRSS:"):
return int(line.split()[1])
except (OSError, ValueError, IndexError):
pass
return None
def run_sample(binary, cef_dir, scenario, fixture, run_index, owner=None, owner_dirs=None,
shared_root_cache=None, owner_socket=None, idle_timeout_ms=0):
run_token = secrets.token_hex(8)
nav_token = secrets.token_hex(8)
cutoff = post_nav_cutoff_seconds()
state = FixtureState()
server = ThreadingHTTPServer(("127.0.0.1", 0),
make_sample_handler(state, fixture, run_token, nav_token))
port = server.server_address[1]
thread = threading.Thread(target=server.serve_forever, kwargs={"poll_interval": 0.05})
thread.daemon = True
thread.start()
run_dir = tempfile.mkdtemp(prefix="cef-startup-run-%02d-" % run_index)
try:
if fixture == "redirect":
url = "http://127.0.0.1:%d/redirect?run=%s&nav=%s" % (port, run_token, nav_token)
else:
url = "http://127.0.0.1:%d/?run=%s&nav=%s" % (port, run_token, nav_token)
if scenario in ("relay-window", "reopen-window") and owner_dirs is not None:
dirs = dict(owner_dirs)
extra = {"DUMBER_BROWSER_FRESH_WINDOW": "1"}
else:
dirs = {
"config": os.path.join(run_dir, "config"),
"data": os.path.join(run_dir, "data"),
"state": os.path.join(run_dir, "state"),
"cache": os.path.join(run_dir, "cache"),
"runtime": os.path.join(run_dir, "runtime"),
"root_cache": shared_root_cache or os.path.join(run_dir, "cef-root-cache"),
}
extra = {}
env = scenario_env(cef_dir, dirs, extra, idle_timeout_ms)
owner_alive_at_start = owner is not None and owner.poll() is None
reopen = None
if scenario == "reopen-window" and owner is not None and owner_socket:
owner_pid = owner.pid
owner_start = proc_starttime(owner_pid)
children_before = child_pids(owner_pid)
rss_before = proc_rss_kb(owner_pid)
acknowledged = send_diagnostic_close(owner_socket)
# The acknowledgement only means accepted: wait for an
# observable steady state before measuring the reopen.
# An unsettled timeout fails the reopen via quiescence_settled.
quiescent_children, settled = wait_for_child_quiescence(owner_pid)
reopen = {
"owner_pid": owner_pid,
"owner_starttime": owner_start,
"close_acknowledged": acknowledged,
"quiescence_settled": settled,
"browser_child_count_before": len(children_before),
"browser_child_count_quiescent": quiescent_children,
"owner_rss_kb_before": rss_before,
}
spawn_ts = time.monotonic()
proc = subprocess.Popen(
[binary, "browse", url],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
env=env,
)
arrivals = []
def pump():
try:
for line in proc.stdout:
arrivals.append((time.monotonic(), line))
except ValueError:
pass
pump_thread = threading.Thread(target=pump)
pump_thread.daemon = True
pump_thread.start()
nav_deadline = navigation_deadline_seconds()
# A live owner serves the navigation after the launcher exits:
# in that case launcher exit must not end collection before the
# correlated document arrives. Without a live owner the sample
# is its own browser and exit ends it.
defers_to_owner = bool(owner_alive_at_start and owner_dirs is not None
and scenario in ("relay-window", "reopen-window"))
hard_end = spawn_ts + cutoff + 25.0
while True:
now = time.monotonic()
with state.lock:
navigated_at = state.first_document_monotonic
fixture_done = any(
b["event"] == "fixture-load" for b in state.beacons
)
exited = proc.poll() is not None
if navigated_at is not None:
if fixture_done and now >= navigated_at + cutoff:
break
if now >= navigated_at + cutoff + 5.0:
break
elif now >= spawn_ts + nav_deadline or (exited and not defers_to_owner):
break
if now >= hard_end:
break
time.sleep(0.05)
self_exited = proc.poll() is not None
if proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait(timeout=5)
launcher_returncode = proc.returncode
pump_thread.join(timeout=5)
try:
proc.stdout.close()
except (AttributeError, ValueError):
pass
decision_ts = time.monotonic()
milestones = parse_child_output([line for _, line in arrivals])
with state.lock:
beacons = list(state.beacons)
document_count = len(state.document_requests)
subresource_count = len(state.subresource_requests)
first_document_at = state.first_document_monotonic
fcp = [b for b in beacons if b["event"] == "first-contentful-paint"]
lcp = [b for b in beacons if b["event"] == "largest-contentful-paint"]
if fcp:
observation_ms = int((fcp[0]["arrival_monotonic"] - spawn_ts) * 1000)
elif beacons:
observation_ms = int((beacons[-1]["arrival_monotonic"] - spawn_ts) * 1000)
else:
observation_ms = int((decision_ts - spawn_ts) * 1000)
expected_document_requests = 2 if fixture == "redirect" else 1
complete = document_count == expected_document_requests and len(fcp) > 0
if scenario in ("relay-window", "reopen-window"):
relayed = bool(owner_alive_at_start and self_exited)
else:
relayed = False
result = {
"run": run_index,
"scenario": scenario,
"fixture": fixture,
"run_token": run_token,
"nav_token": nav_token,
"observation_spawn_to_summary_ms": observation_ms,
"document_requests": document_count,
"subresource_requests": subresource_count,
"target_fcp_ms": fcp[0]["value_ms"] if fcp else None,
"target_lcp_through_cutoff_ms": lcp[-1]["value_ms"] if lcp else None,
"startup_milestones": len(milestones),
"launcher_returncode": launcher_returncode,
"launcher_cef_init_count": count_cef_inits([line for _, line in arrivals]),
"first_document_observation_ms": (
int((first_document_at - spawn_ts) * 1000)
if first_document_at is not None and first_document_at >= spawn_ts
else None
),
"complete": complete,
"relayed": relayed,
"fallback_spawn": scenario in ("relay-window", "reopen-window") and not relayed,
}
if scenario in ("relay-window", "reopen-window") and owner_dirs is not None:
# Residency proof: the owner must initialize CEF exactly once
# no matter how many second windows it serves. A second init
# here means the sample paid a full cold start, not a reopen.
try:
with open(owner_dirs.get("owner_log", ""), errors="replace") as owner_log:
result["owner_cef_init_count"] = count_cef_inits(owner_log)
except OSError:
result["owner_cef_init_count"] = None
if reopen is not None:
# Owner identity is verified independently of the Popen handle:
# the same pid with a different start time is a recycled pid,
# not the same process. Unverifiable identity counts as different.
start_now = proc_starttime(reopen["owner_pid"]) if owner is not None else None
owner_alive = owner is not None and owner.poll() is None
same = bool(
owner_alive
and reopen["owner_starttime"] is not None
and start_now == reopen["owner_starttime"]
)
reopen["owner_alive_after"] = owner_alive
reopen["same_process"] = same
reopen["browser_child_count_after"] = len(child_pids(owner.pid)) if owner_alive else 0
reopen["owner_rss_kb_after"] = proc_rss_kb(owner.pid) if owner_alive else None
result["reopen"] = reopen
result["residency_valid"] = is_residency_valid(result, scenario)
return result
finally:
server.shutdown()
server.server_close()
thread.join(timeout=5)
shutil.rmtree(run_dir, ignore_errors=True)
def main():
parser = argparse.ArgumentParser(description="Measure CEF startup against synthetic fixtures.")
parser.add_argument("--binary", required=True)
parser.add_argument("--cef-dir", required=True)
parser.add_argument(
"--scenario",
required=True,
choices=("process-warm", "profile-fresh", "relay-window", "reopen-window"),
)
parser.add_argument(
"--fixture",
required=True,
choices=("static", "delayed", "redirect", "cache"),
)
parser.add_argument("--runs", type=int, default=30)
parser.add_argument("--output", required=True)
parser.add_argument("--idle-timeout-ms", type=int, default=0,
help="resident CEF idle timeout for generated configs "
"(0..300000, 0 is the product default)")
args = parser.parse_args()
if args.idle_timeout_ms < 0 or args.idle_timeout_ms > 300000:
print("measure: --idle-timeout-ms must be 0..300000", file=sys.stderr)
raise SystemExit(2)
if args.runs < 1 or args.runs > 100:
print("measure: --runs must be 1..100", file=sys.stderr)
raise SystemExit(2)
if not os.path.isabs(args.output) or os.path.exists(args.output) or os.path.islink(args.output):
print("measure: --output must be a fresh absolute directory", file=sys.stderr)
raise SystemExit(2)
parent = os.path.dirname(args.output)
if not os.path.isdir(parent) or os.path.islink(parent):
print("measure: --output parent must be an existing directory", file=sys.stderr)
raise SystemExit(2)
if not (os.path.isfile(args.binary) and os.access(args.binary, os.X_OK)):
print("measure: --binary must be executable", file=sys.stderr)
raise SystemExit(2)
if not os.path.isdir(args.cef_dir):
print("measure: --cef-dir must be a directory", file=sys.stderr)
raise SystemExit(2)
with open(args.binary, "rb") as candidate:
# Chunked streaming hash: hashlib.file_digest needs 3.11+, this
# stays compatible with older 3.x interpreters.
digest = hashlib.sha256()
for chunk in iter(lambda: candidate.read(65536), b""):
digest.update(chunk)
binary_sha256 = digest.hexdigest()
os.makedirs(args.output)
# Privacy: only summary.json and run-*.json are safe to share (timings,
# counts, hashes). owner.log and generated configs are machine-local:
# raw child logs and the selected --cef-dir path may contain local
# usernames or paths. Never publish the whole directory.
owner = None
owner_dirs = None
owner_socket = None
shared_root_cache = None
warmup_discarded = False
if args.scenario == "process-warm":
shared_root_cache = os.path.join(args.output, "warm-profile", "cef-root-cache")
os.makedirs(shared_root_cache)
if args.scenario == "relay-window":
scenario_dir = os.path.join(args.output, "relay-scenario")
os.makedirs(scenario_dir)
owner, owner_dirs, owner_socket = start_relay_owner(
args.binary, args.cef_dir, scenario_dir,
idle_timeout_ms=args.idle_timeout_ms)
if args.scenario == "reopen-window":
scenario_dir = os.path.join(args.output, "reopen-scenario")
os.makedirs(scenario_dir)
owner, owner_dirs, owner_socket = start_relay_owner(
args.binary, args.cef_dir, scenario_dir, diagnostic_close=True,
idle_timeout_ms=args.idle_timeout_ms)
results = []
try:
if args.scenario == "process-warm":
# One unrecorded warm-up initializes the shared profile; only the
# recorded samples below count toward the report. A failed
# warm-up aborts the scenario: later runs must never use an
# uninitialized profile.
warmup = run_sample(args.binary, args.cef_dir, args.scenario, args.fixture,
0, shared_root_cache=shared_root_cache,
idle_timeout_ms=args.idle_timeout_ms)
if not warmup["complete"]:
print("measure: process-warm warm-up was incomplete", file=sys.stderr)
raise SystemExit(1)
warmup_discarded = True
for index in range(1, args.runs + 1):
result = run_sample(
args.binary, args.cef_dir, args.scenario, args.fixture, index,
owner=owner, owner_dirs=owner_dirs, shared_root_cache=shared_root_cache,
owner_socket=owner_socket, idle_timeout_ms=args.idle_timeout_ms,
)
results.append(result)
with open(os.path.join(args.output, "run-%02d.json" % index), "w") as handle:
json.dump(result, handle, indent=2, sort_keys=True)
finally:
stop_proc(owner)
summary = {
"binary_sha256": binary_sha256,
"scenario": args.scenario,
"fixture": args.fixture,
"runs": len(results),
"complete_runs": sum(1 for r in results if r["complete"]),
"incomplete_runs": sum(1 for r in results if not r["complete"]),
"duplicate_document_runs": sum(
1
for r in results
if r["document_requests"] != (2 if args.fixture == "redirect" else 1)
),
"warmup_discarded": warmup_discarded,
"relay_owner_ready": owner is not None,
"idle_timeout_ms": args.idle_timeout_ms,
}
if args.scenario in ("relay-window", "reopen-window") and owner_dirs is not None:
# Second-window contract, kept separate from cold-process numbers:
# one owner CEF init total, plus the request-to-FCP distribution
# of resident reopens. Missing FCP stays missing, never averaged in.
try:
with open(owner_dirs.get("owner_log", ""), errors="replace") as owner_log:
summary["owner_cef_init_count"] = count_cef_inits(owner_log)
except OSError:
summary["owner_cef_init_count"] = None
fcp_observations = sorted(
r["first_document_observation_ms"]
for r in results
if r.get("residency_valid") and r["first_document_observation_ms"] is not None
)
summary["resident_valid_runs"] = sum(1 for r in results if r.get("residency_valid"))
summary["resident_invalid_runs"] = sum(1 for r in results if r.get("residency_valid") is False)
summary["second_window_complete_runs"] = len(fcp_observations)
if fcp_observations:
# Spawn to first document request, same harness clock. This is
# request arrival, not document completion or paint; for the
# redirect fixture it measures the initial redirect request.
summary["second_window_spawn_to_first_document_observation_ms"] = {
"min": fcp_observations[0],
"median": fcp_observations[len(fcp_observations) // 2],
"max": fcp_observations[-1],
}
with open(os.path.join(args.output, "summary.json"), "w") as handle:
json.dump(summary, handle, indent=2, sort_keys=True)
print("measure artifacts: %s" % args.output)
if __name__ == "__main__":
main()