mirror of
https://github.com/lexmount/moli.git
synced 2026-09-28 08:01:37 +00:00
949 lines
40 KiB
Python
949 lines
40 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import gzip
|
|
import hashlib
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import tarfile
|
|
import tempfile
|
|
import time
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from urllib.parse import quote
|
|
|
|
from .artifacts import write_csv, write_json
|
|
from .config import REPO_ROOT, clear_proxy_env
|
|
from .process import run_process
|
|
from .sampling import snapshot_resources
|
|
from .serve import start_moli_serve, stop_moli_serve
|
|
from .stats import summarize
|
|
from .synthetic import SyntheticServer
|
|
from .versions import sha256_file
|
|
|
|
|
|
STARTUP_PROFILES = ("smoke", "formal")
|
|
FORMAL_STARTUP_RUNS = 10
|
|
FORMAL_STARTUP_WARM_PAGES = 10
|
|
FORMAL_STARTUP_IDLE_SECONDS = (1.0, 5.0, 30.0)
|
|
|
|
|
|
def _hash_output(data: bytes) -> str:
|
|
return hashlib.sha256(data).hexdigest()
|
|
|
|
|
|
def _cache_mode_fields(process_cache_mode: str, kernel_cache_mode: str) -> dict[str, str]:
|
|
return {
|
|
"process_cache_mode": process_cache_mode,
|
|
"kernel_cache_mode": kernel_cache_mode,
|
|
}
|
|
|
|
|
|
def _drop_os_cache_artifact(suite_dir: Path, case: str, run_id: int) -> dict[str, Any]:
|
|
artifact_dir = suite_dir / "cache" / f"{case}-run-{run_id}"
|
|
artifact_dir.mkdir(parents=True, exist_ok=True)
|
|
artifact_path = artifact_dir / "drop-caches.txt"
|
|
if os.geteuid() != 0:
|
|
artifact_path.write_text("unavailable: root privileges required for drop_caches\n", encoding="utf-8")
|
|
return {
|
|
"requested": True,
|
|
"ok": False,
|
|
"mode": "warm-kernel",
|
|
"error": "root privileges required for drop_caches",
|
|
"artifact_path": str(artifact_path),
|
|
}
|
|
try:
|
|
subprocess.run(["sync"], check=False)
|
|
Path("/proc/sys/vm/drop_caches").write_text("3\n", encoding="utf-8")
|
|
except OSError as error:
|
|
artifact_path.write_text(f"unavailable: {error}\n", encoding="utf-8")
|
|
return {
|
|
"requested": True,
|
|
"ok": False,
|
|
"mode": "warm-kernel",
|
|
"error": str(error),
|
|
"artifact_path": str(artifact_path),
|
|
}
|
|
artifact_path.write_text("ok: wrote 3 to /proc/sys/vm/drop_caches\n", encoding="utf-8")
|
|
return {
|
|
"requested": True,
|
|
"ok": True,
|
|
"mode": "cold-os-cache",
|
|
"artifact_path": str(artifact_path),
|
|
}
|
|
|
|
|
|
def _compressed_binary_size(binary: Path) -> int:
|
|
with tempfile.TemporaryDirectory(prefix="moli-benchmark-size-") as temp_dir:
|
|
archive = Path(temp_dir) / "moli.tar.gz"
|
|
with tarfile.open(archive, "w:gz") as tar:
|
|
tar.add(binary, arcname=binary.name)
|
|
return archive.stat().st_size
|
|
|
|
|
|
def _ldd_dependency_paths(binary: Path) -> tuple[list[Path], str | None]:
|
|
ldd_bin = shutil.which("ldd")
|
|
if ldd_bin is None:
|
|
return [], "ldd executable unavailable"
|
|
result = subprocess.run(
|
|
[ldd_bin, str(binary)],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
output = result.stdout + result.stderr
|
|
if result.returncode != 0 and "not a dynamic executable" not in output:
|
|
return [], output.strip() or f"ldd exited with {result.returncode}"
|
|
|
|
paths: list[Path] = []
|
|
seen: set[Path] = set()
|
|
for raw_line in output.splitlines():
|
|
line = raw_line.strip()
|
|
if not line or line.startswith("linux-vdso"):
|
|
continue
|
|
candidate = ""
|
|
if "=>" in line:
|
|
candidate = line.split("=>", 1)[1].strip().split(" ", 1)[0]
|
|
else:
|
|
candidate = line.split(" ", 1)[0]
|
|
if not candidate.startswith("/"):
|
|
continue
|
|
path = Path(candidate)
|
|
if path.exists() and path not in seen:
|
|
seen.add(path)
|
|
paths.append(path)
|
|
return paths, None
|
|
|
|
|
|
def _add_path_to_tar(tar: tarfile.TarFile, source: Path, arcname: str) -> None:
|
|
tar.add(source, arcname=arcname, recursive=False)
|
|
if source.is_symlink():
|
|
resolved = source.resolve()
|
|
if resolved.exists():
|
|
tar.add(resolved, arcname=str(resolved).lstrip("/"), recursive=False)
|
|
|
|
|
|
def _build_minimal_rootfs_image_artifacts(binary: Path, suite_dir: Path) -> dict[str, Any]:
|
|
image_dir = suite_dir / "image-size"
|
|
image_dir.mkdir(parents=True, exist_ok=True)
|
|
dependencies, dependency_error = _ldd_dependency_paths(binary)
|
|
rootfs_tar = image_dir / "moli-rootfs.tar"
|
|
rootfs_targz = image_dir / "moli-rootfs.tar.gz"
|
|
manifest = {
|
|
"format": "minimal-rootfs-tar",
|
|
"binary": str(binary),
|
|
"binary_arcname": "usr/local/bin/moli",
|
|
"dependencies": [str(path) for path in dependencies],
|
|
"dependency_error": dependency_error,
|
|
"note": "Daemonless startup/deploy size artifact. Container image measurement is intentionally out of scope.",
|
|
}
|
|
write_json(image_dir / "manifest.json", manifest)
|
|
with tarfile.open(rootfs_tar, "w") as tar:
|
|
_add_path_to_tar(tar, binary, "usr/local/bin/moli")
|
|
for dependency in dependencies:
|
|
_add_path_to_tar(tar, dependency, str(dependency).lstrip("/"))
|
|
with rootfs_tar.open("rb") as source, rootfs_targz.open("wb") as target:
|
|
with gzip.GzipFile(fileobj=target, mode="wb", compresslevel=1, mtime=0) as gzip_file:
|
|
shutil.copyfileobj(source, gzip_file)
|
|
return {
|
|
"format": manifest["format"],
|
|
"uncompressed_bytes": rootfs_tar.stat().st_size,
|
|
"compressed_bytes": rootfs_targz.stat().st_size,
|
|
"dependency_count": len(dependencies),
|
|
"dependency_error": dependency_error,
|
|
"rootfs_tar_path": str(rootfs_tar),
|
|
"rootfs_targz_path": str(rootfs_targz),
|
|
"manifest_path": str(image_dir / "manifest.json"),
|
|
}
|
|
|
|
|
|
def _stripped_binary_size(binary: Path) -> tuple[int | None, str | None]:
|
|
strip_bin = shutil.which("strip")
|
|
if strip_bin is None:
|
|
return None, "strip executable unavailable"
|
|
with tempfile.TemporaryDirectory(prefix="moli-benchmark-strip-") as temp_dir:
|
|
candidate = Path(temp_dir) / binary.name
|
|
shutil.copy2(binary, candidate)
|
|
result = subprocess.run(
|
|
[strip_bin, "--strip-unneeded", str(candidate)],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
check=False,
|
|
)
|
|
if result.returncode != 0:
|
|
stderr = result.stderr.decode("utf-8", errors="replace").strip()
|
|
return None, stderr or f"strip exited with {result.returncode}"
|
|
return candidate.stat().st_size, None
|
|
|
|
|
|
def _time_verbose_path(suite_dir: Path, case: str, run_id: int) -> Path:
|
|
return suite_dir / "time" / f"{case}-run-{run_id}.time.txt"
|
|
|
|
|
|
def _cgroup_artifact_dir(suite_dir: Path, case: str, run_id: int) -> Path:
|
|
return suite_dir / "cgroup" / f"{case}-run-{run_id}"
|
|
|
|
|
|
def _time_verbose_row_fields(time_verbose: dict[str, Any] | None) -> dict[str, Any]:
|
|
if not time_verbose:
|
|
return {
|
|
"time_max_rss_bytes": None,
|
|
"time_elapsed_seconds": None,
|
|
"time_user_seconds": None,
|
|
"time_system_seconds": None,
|
|
"time_raw_path": None,
|
|
}
|
|
return {
|
|
"time_max_rss_bytes": time_verbose.get("max_rss_bytes"),
|
|
"time_elapsed_seconds": time_verbose.get("elapsed_seconds"),
|
|
"time_user_seconds": time_verbose.get("user_seconds"),
|
|
"time_system_seconds": time_verbose.get("system_seconds"),
|
|
"time_raw_path": time_verbose.get("raw_path"),
|
|
}
|
|
|
|
|
|
def _startup_formal_gate_rows(
|
|
*,
|
|
profile: str,
|
|
runs: int,
|
|
include_cdp_first_page: bool,
|
|
include_cdp_warm_pages: bool,
|
|
cdp_warm_pages: int,
|
|
idle_seconds: tuple[float, ...],
|
|
total_failures: int,
|
|
) -> list[dict[str, Any]]:
|
|
idle_set = {float(seconds) for seconds in idle_seconds}
|
|
requirements = [
|
|
{
|
|
"gate": "profile",
|
|
"ok": profile == "formal",
|
|
"actual": profile,
|
|
"required": "formal",
|
|
"failure_kind": "profile-requirement",
|
|
},
|
|
{
|
|
"gate": "runs",
|
|
"ok": runs >= FORMAL_STARTUP_RUNS,
|
|
"actual": runs,
|
|
"required": FORMAL_STARTUP_RUNS,
|
|
"failure_kind": "profile-requirement",
|
|
},
|
|
{
|
|
"gate": "cdp-first-page",
|
|
"ok": include_cdp_first_page,
|
|
"actual": include_cdp_first_page,
|
|
"required": True,
|
|
"failure_kind": "profile-requirement",
|
|
},
|
|
{
|
|
"gate": "cdp-warm-pages",
|
|
"ok": include_cdp_warm_pages and cdp_warm_pages >= FORMAL_STARTUP_WARM_PAGES,
|
|
"actual": {"enabled": include_cdp_warm_pages, "pages": cdp_warm_pages if include_cdp_warm_pages else 0},
|
|
"required": {"enabled": True, "pages": FORMAL_STARTUP_WARM_PAGES},
|
|
"failure_kind": "profile-requirement",
|
|
},
|
|
{
|
|
"gate": "idle-footprint",
|
|
"ok": all(required in idle_set for required in FORMAL_STARTUP_IDLE_SECONDS),
|
|
"actual": sorted(idle_set),
|
|
"required": list(FORMAL_STARTUP_IDLE_SECONDS),
|
|
"failure_kind": "profile-requirement",
|
|
},
|
|
{
|
|
"gate": "workload-failures",
|
|
"ok": total_failures == 0,
|
|
"actual": total_failures,
|
|
"required": 0,
|
|
"failure_kind": "workload-failure",
|
|
},
|
|
]
|
|
return requirements
|
|
|
|
|
|
async def _cdp_first_page_flow(endpoint: str, timeout_seconds: float) -> dict[str, Any]:
|
|
from .raw_cdp import connect_raw_cdp
|
|
|
|
client = await connect_raw_cdp(endpoint)
|
|
try:
|
|
target_command_id = await client.send("Target.createTarget", {"url": "about:blank"})
|
|
target_response, seen = await client.recv_until_id(target_command_id, timeout=timeout_seconds)
|
|
target_id = target_response.get("result", {}).get("targetId")
|
|
if not isinstance(target_id, str) or not target_id:
|
|
raise RuntimeError(f"missing targetId in {target_response}")
|
|
|
|
attach_id = await client.send("Target.attachToTarget", {"targetId": target_id, "flatten": True})
|
|
attach_response, attach_seen = await client.recv_until_id(attach_id, timeout=timeout_seconds)
|
|
seen.extend(attach_seen)
|
|
session_id = attach_response.get("result", {}).get("sessionId")
|
|
if not isinstance(session_id, str) or not session_id:
|
|
raise RuntimeError(f"missing sessionId in {attach_response}")
|
|
|
|
for method in ("Page.enable", "Runtime.enable"):
|
|
command_id = await client.send(method, session_id=session_id)
|
|
_, method_seen = await client.recv_until_id(command_id, timeout=timeout_seconds)
|
|
seen.extend(method_seen)
|
|
|
|
html = "<!doctype html><title>cdp-first-page</title><body data-benchmark-status='ok'>ok</body>"
|
|
navigate_id = await client.send(
|
|
"Page.navigate",
|
|
{"url": "data:text/html;charset=utf-8," + quote(html, safe=":/;=,'")},
|
|
session_id=session_id,
|
|
)
|
|
_, navigate_seen = await client.recv_until_id(navigate_id, timeout=timeout_seconds)
|
|
seen.extend(navigate_seen)
|
|
|
|
expression = """
|
|
new Promise(resolve => {
|
|
const deadline = Date.now() + 10000;
|
|
function tick() {
|
|
if (document.body && document.body.dataset.benchmarkStatus === 'ok') {
|
|
resolve(document.title + ':' + document.body.textContent.trim());
|
|
} else if (Date.now() > deadline) {
|
|
resolve('timeout');
|
|
} else {
|
|
setTimeout(tick, 10);
|
|
}
|
|
}
|
|
tick();
|
|
})
|
|
"""
|
|
evaluate_id = await client.send(
|
|
"Runtime.evaluate",
|
|
{"expression": expression, "awaitPromise": True, "returnByValue": True},
|
|
session_id=session_id,
|
|
)
|
|
evaluate_response, evaluate_seen = await client.recv_until_id(evaluate_id, timeout=timeout_seconds + 1)
|
|
seen.extend(evaluate_seen)
|
|
value = evaluate_response.get("result", {}).get("result", {}).get("value")
|
|
return {
|
|
"value": value,
|
|
"ok": value == "cdp-first-page:ok",
|
|
"messages": len(seen),
|
|
"command_count": client.command_count,
|
|
}
|
|
finally:
|
|
await client.websocket.close()
|
|
|
|
|
|
async def _cdp_create_page(client: Any, timeout_seconds: float, page_index: int) -> dict[str, Any]:
|
|
started = time.perf_counter()
|
|
seen: list[dict[str, Any]] = []
|
|
html = f"<!doctype html><title>cdp-warm-page-{page_index}</title><body data-benchmark-status='ok'>ok</body>"
|
|
|
|
target_command_id = await client.send("Target.createTarget", {"url": "about:blank"})
|
|
target_response, target_seen = await client.recv_until_id(target_command_id, timeout=timeout_seconds)
|
|
seen.extend(target_seen)
|
|
target_id = target_response.get("result", {}).get("targetId")
|
|
if not isinstance(target_id, str) or not target_id:
|
|
raise RuntimeError(f"missing targetId in {target_response}")
|
|
|
|
try:
|
|
attach_id = await client.send("Target.attachToTarget", {"targetId": target_id, "flatten": True})
|
|
attach_response, attach_seen = await client.recv_until_id(attach_id, timeout=timeout_seconds)
|
|
seen.extend(attach_seen)
|
|
session_id = attach_response.get("result", {}).get("sessionId")
|
|
if not isinstance(session_id, str) or not session_id:
|
|
raise RuntimeError(f"missing sessionId in {attach_response}")
|
|
|
|
for method in ("Page.enable", "Runtime.enable"):
|
|
command_id = await client.send(method, session_id=session_id)
|
|
_, method_seen = await client.recv_until_id(command_id, timeout=timeout_seconds)
|
|
seen.extend(method_seen)
|
|
|
|
navigate_id = await client.send(
|
|
"Page.navigate",
|
|
{"url": "data:text/html;charset=utf-8," + quote(html, safe=":/;=,'")},
|
|
session_id=session_id,
|
|
)
|
|
_, navigate_seen = await client.recv_until_id(navigate_id, timeout=timeout_seconds)
|
|
seen.extend(navigate_seen)
|
|
|
|
evaluate_id = await client.send(
|
|
"Runtime.evaluate",
|
|
{
|
|
"expression": "document.body && document.body.dataset.benchmarkStatus === 'ok'",
|
|
"returnByValue": True,
|
|
},
|
|
session_id=session_id,
|
|
)
|
|
evaluate_response, evaluate_seen = await client.recv_until_id(evaluate_id, timeout=timeout_seconds)
|
|
seen.extend(evaluate_seen)
|
|
ok = evaluate_response.get("result", {}).get("result", {}).get("value") is True
|
|
return {
|
|
"page_index": page_index,
|
|
"ok": ok,
|
|
"elapsed_ms": (time.perf_counter() - started) * 1000.0,
|
|
"target_id": target_id,
|
|
"messages": len(seen),
|
|
}
|
|
finally:
|
|
try:
|
|
close_id = await client.send("Target.closeTarget", {"targetId": target_id})
|
|
await client.recv_until_id(close_id, timeout=3)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
async def _cdp_warm_pages_flow(endpoint: str, timeout_seconds: float, pages: int) -> dict[str, Any]:
|
|
from .raw_cdp import connect_raw_cdp
|
|
|
|
client = await connect_raw_cdp(endpoint)
|
|
try:
|
|
page_results = []
|
|
for page_index in range(1, pages + 1):
|
|
page_results.append(await _cdp_create_page(client, timeout_seconds, page_index))
|
|
return {
|
|
"ok": all(bool(page.get("ok")) for page in page_results),
|
|
"pages": page_results,
|
|
"command_count": client.command_count,
|
|
}
|
|
finally:
|
|
await client.websocket.close()
|
|
|
|
|
|
def _run_cdp_first_page(
|
|
moli_bin: Path,
|
|
timeout_seconds: float,
|
|
*,
|
|
time_verbose_path: Path | None = None,
|
|
cgroup_artifact_dir: Path | None = None,
|
|
) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
handle = None
|
|
started = time.perf_counter()
|
|
try:
|
|
handle = start_moli_serve(
|
|
moli_bin,
|
|
timeout_seconds,
|
|
time_verbose_path=time_verbose_path,
|
|
cgroup_artifact_dir=cgroup_artifact_dir,
|
|
)
|
|
result = asyncio.run(_cdp_first_page_flow(handle.endpoint, timeout_seconds))
|
|
elapsed_ms = (time.perf_counter() - started) * 1000.0
|
|
stop_details = stop_moli_serve(handle)
|
|
row = {
|
|
"case": "cdp-first-page",
|
|
"ok": bool(result.get("ok")),
|
|
"elapsed_ms": elapsed_ms,
|
|
"serve_ready_ms": handle.ready_ms,
|
|
"peak_pss_bytes": stop_details.get("resources", {}).get("peak_pss_bytes"),
|
|
"peak_rss_bytes": stop_details.get("resources", {}).get("peak_rss_bytes"),
|
|
"peak_cpu_percent": stop_details.get("resources", {}).get("peak_cpu_percent"),
|
|
"peak_process_count": stop_details.get("resources", {}).get("peak_process_count"),
|
|
"peak_thread_count": stop_details.get("resources", {}).get("peak_thread_count"),
|
|
"peak_fd_count": stop_details.get("resources", {}).get("peak_fd_count"),
|
|
"cdp_value": result.get("value"),
|
|
"cdp_messages": result.get("messages"),
|
|
"cdp_command_count": result.get("command_count"),
|
|
**_time_verbose_row_fields(stop_details.get("time_verbose")),
|
|
}
|
|
return row, {**row, "serve": stop_details}
|
|
except Exception as error:
|
|
elapsed_ms = (time.perf_counter() - started) * 1000.0
|
|
stop_details = stop_moli_serve(handle)
|
|
row = {
|
|
"case": "cdp-first-page",
|
|
"ok": False,
|
|
"elapsed_ms": elapsed_ms,
|
|
"error": str(error),
|
|
**_time_verbose_row_fields(stop_details.get("time_verbose")),
|
|
}
|
|
return row, {**row, "serve": stop_details}
|
|
|
|
|
|
def _run_cdp_warm_pages(
|
|
moli_bin: Path,
|
|
timeout_seconds: float,
|
|
*,
|
|
pages: int,
|
|
time_verbose_path: Path | None = None,
|
|
cgroup_artifact_dir: Path | None = None,
|
|
) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
handle = None
|
|
started = time.perf_counter()
|
|
try:
|
|
handle = start_moli_serve(
|
|
moli_bin,
|
|
timeout_seconds,
|
|
time_verbose_path=time_verbose_path,
|
|
cgroup_artifact_dir=cgroup_artifact_dir,
|
|
)
|
|
result = asyncio.run(_cdp_warm_pages_flow(handle.endpoint, timeout_seconds, pages))
|
|
elapsed_ms = (time.perf_counter() - started) * 1000.0
|
|
page_results = [page for page in result.get("pages", []) if isinstance(page, dict)]
|
|
page_elapsed = [
|
|
float(page["elapsed_ms"])
|
|
for page in page_results
|
|
if page.get("ok") and page.get("elapsed_ms") is not None
|
|
]
|
|
page_elapsed_summary = summarize(page_elapsed)
|
|
stop_details = stop_moli_serve(handle)
|
|
row = {
|
|
"case": "cdp-warm-pages",
|
|
"ok": bool(result.get("ok")) and len(page_results) == pages,
|
|
"elapsed_ms": elapsed_ms,
|
|
"serve_ready_ms": handle.ready_ms,
|
|
"cdp_pages": pages,
|
|
"cdp_page_passes": sum(1 for page in page_results if page.get("ok")),
|
|
"cdp_page_elapsed_p50_ms": page_elapsed_summary.get("p50"),
|
|
"cdp_page_elapsed_p95_ms": page_elapsed_summary.get("p95"),
|
|
"peak_pss_bytes": stop_details.get("resources", {}).get("peak_pss_bytes"),
|
|
"peak_rss_bytes": stop_details.get("resources", {}).get("peak_rss_bytes"),
|
|
"peak_cpu_percent": stop_details.get("resources", {}).get("peak_cpu_percent"),
|
|
"peak_process_count": stop_details.get("resources", {}).get("peak_process_count"),
|
|
"peak_thread_count": stop_details.get("resources", {}).get("peak_thread_count"),
|
|
"peak_fd_count": stop_details.get("resources", {}).get("peak_fd_count"),
|
|
"cdp_command_count": result.get("command_count"),
|
|
**_time_verbose_row_fields(stop_details.get("time_verbose")),
|
|
}
|
|
return row, {**row, "serve": stop_details, "pages": page_results}
|
|
except Exception as error:
|
|
elapsed_ms = (time.perf_counter() - started) * 1000.0
|
|
stop_details = stop_moli_serve(handle)
|
|
row = {
|
|
"case": "cdp-warm-pages",
|
|
"ok": False,
|
|
"elapsed_ms": elapsed_ms,
|
|
"cdp_pages": pages,
|
|
"error": str(error),
|
|
**_time_verbose_row_fields(stop_details.get("time_verbose")),
|
|
}
|
|
return row, {**row, "serve": stop_details}
|
|
|
|
|
|
def run_startup_suite(
|
|
*,
|
|
moli_bin: Path,
|
|
output_dir: Path,
|
|
profile: str = "smoke",
|
|
runs: int,
|
|
timeout_seconds: float,
|
|
include_cdp_first_page: bool = False,
|
|
include_cdp_warm_pages: bool = False,
|
|
cdp_warm_pages: int = 10,
|
|
idle_seconds: tuple[float, ...] = (),
|
|
drop_os_cache: bool = False,
|
|
) -> dict[str, Any]:
|
|
if profile not in STARTUP_PROFILES:
|
|
raise RuntimeError(f"startup profile must be one of: {', '.join(STARTUP_PROFILES)}")
|
|
if include_cdp_warm_pages and cdp_warm_pages < 1:
|
|
raise RuntimeError("cdp_warm_pages must be at least 1 when warm page startup is enabled")
|
|
suite_dir = output_dir / "startup"
|
|
rows: list[dict[str, Any]] = []
|
|
details: list[dict[str, Any]] = []
|
|
cache_events: list[dict[str, Any]] = []
|
|
|
|
def prepare_cache(case: str, run_id: int) -> dict[str, str]:
|
|
if not drop_os_cache:
|
|
return _cache_mode_fields("cold-process", "warm-kernel")
|
|
event = _drop_os_cache_artifact(suite_dir, case, run_id)
|
|
cache_events.append({"case": case, "run": run_id, **event})
|
|
return _cache_mode_fields("cold-process", str(event["mode"]))
|
|
|
|
stripped_bytes, stripped_error = _stripped_binary_size(moli_bin)
|
|
binary_row = {
|
|
"case": "binary-size",
|
|
"run": 1,
|
|
"ok": True,
|
|
"elapsed_ms": 0.0,
|
|
"binary_bytes": moli_bin.stat().st_size,
|
|
"stripped_binary_bytes": stripped_bytes,
|
|
"stripped_available": stripped_bytes is not None,
|
|
"stripped_error": stripped_error,
|
|
"tar_gz_bytes": _compressed_binary_size(moli_bin),
|
|
"sha256": sha256_file(moli_bin),
|
|
**_cache_mode_fields("artifact", "not-applicable"),
|
|
}
|
|
rows.append(binary_row)
|
|
details.append(binary_row)
|
|
|
|
image_started = time.perf_counter()
|
|
image_artifacts = _build_minimal_rootfs_image_artifacts(moli_bin, suite_dir)
|
|
image_row = {
|
|
"case": "image-size",
|
|
"run": 1,
|
|
"ok": True,
|
|
"elapsed_ms": (time.perf_counter() - image_started) * 1000.0,
|
|
"image_format": image_artifacts["format"],
|
|
"image_uncompressed_bytes": image_artifacts["uncompressed_bytes"],
|
|
"image_compressed_bytes": image_artifacts["compressed_bytes"],
|
|
"image_dependency_count": image_artifacts["dependency_count"],
|
|
"image_dependency_error": image_artifacts["dependency_error"],
|
|
"image_rootfs_tar_path": image_artifacts["rootfs_tar_path"],
|
|
"image_rootfs_targz_path": image_artifacts["rootfs_targz_path"],
|
|
"image_manifest_path": image_artifacts["manifest_path"],
|
|
**_cache_mode_fields("artifact", "not-applicable"),
|
|
}
|
|
rows.append(image_row)
|
|
details.append({**image_row, "image": image_artifacts})
|
|
|
|
for run_id in range(1, runs + 1):
|
|
handle = None
|
|
cache_fields = prepare_cache("serve-ready", run_id)
|
|
try:
|
|
handle = start_moli_serve(
|
|
moli_bin,
|
|
timeout_seconds,
|
|
time_verbose_path=_time_verbose_path(suite_dir, "serve-ready", run_id),
|
|
cgroup_artifact_dir=_cgroup_artifact_dir(suite_dir, "serve-ready", run_id),
|
|
)
|
|
stop_details = stop_moli_serve(handle)
|
|
row = {
|
|
"case": "serve-ready",
|
|
"run": run_id,
|
|
"ok": True,
|
|
"elapsed_ms": handle.ready_ms,
|
|
"peak_pss_bytes": stop_details.get("resources", {}).get("peak_pss_bytes"),
|
|
"peak_rss_bytes": stop_details.get("resources", {}).get("peak_rss_bytes"),
|
|
"peak_cpu_percent": stop_details.get("resources", {}).get("peak_cpu_percent"),
|
|
"peak_process_count": stop_details.get("resources", {}).get("peak_process_count"),
|
|
"peak_thread_count": stop_details.get("resources", {}).get("peak_thread_count"),
|
|
"peak_fd_count": stop_details.get("resources", {}).get("peak_fd_count"),
|
|
**cache_fields,
|
|
**_time_verbose_row_fields(stop_details.get("time_verbose")),
|
|
}
|
|
rows.append(row)
|
|
details.append({**row, "serve": stop_details})
|
|
except Exception as error:
|
|
stop_details = stop_moli_serve(handle)
|
|
row = {
|
|
"case": "serve-ready",
|
|
"run": run_id,
|
|
"ok": False,
|
|
"elapsed_ms": None,
|
|
"error": str(error),
|
|
**cache_fields,
|
|
**_time_verbose_row_fields(stop_details.get("time_verbose")),
|
|
}
|
|
rows.append(row)
|
|
details.append({**row, "serve": stop_details})
|
|
|
|
for run_id in range(1, runs + 1):
|
|
cache_fields = prepare_cache("cli-fetch-aboutblank", run_id)
|
|
result = run_process(
|
|
[
|
|
str(moli_bin),
|
|
"fetch",
|
|
"--dump",
|
|
"html",
|
|
"--wait-until",
|
|
"load",
|
|
"--timeout",
|
|
str(int(timeout_seconds * 1000)),
|
|
"about:blank",
|
|
],
|
|
cwd=REPO_ROOT,
|
|
timeout_seconds=timeout_seconds,
|
|
env=clear_proxy_env(os.environ),
|
|
time_verbose_path=_time_verbose_path(suite_dir, "cli-fetch-aboutblank", run_id),
|
|
cgroup_artifact_dir=_cgroup_artifact_dir(suite_dir, "cli-fetch-aboutblank", run_id),
|
|
)
|
|
row = {
|
|
"case": "cli-fetch-aboutblank",
|
|
"run": run_id,
|
|
"ok": result.returncode == 0 and not result.timed_out,
|
|
"elapsed_ms": result.elapsed_ms,
|
|
"returncode": result.returncode,
|
|
"timed_out": result.timed_out,
|
|
"peak_pss_bytes": result.resources.get("peak_pss_bytes"),
|
|
"peak_rss_bytes": result.resources.get("peak_rss_bytes"),
|
|
"peak_cpu_percent": result.resources.get("peak_cpu_percent"),
|
|
"peak_process_count": result.resources.get("peak_process_count"),
|
|
"peak_thread_count": result.resources.get("peak_thread_count"),
|
|
"peak_fd_count": result.resources.get("peak_fd_count"),
|
|
"output_sha256": _hash_output(result.output_digest_material()),
|
|
**cache_fields,
|
|
**_time_verbose_row_fields(result.time_verbose),
|
|
}
|
|
rows.append(row)
|
|
details.append({**row, "process": result.json_summary(include_output=True)})
|
|
|
|
if include_cdp_first_page:
|
|
for run_id in range(1, runs + 1):
|
|
cache_fields = prepare_cache("cdp-first-page", run_id)
|
|
row, detail = _run_cdp_first_page(
|
|
moli_bin,
|
|
timeout_seconds,
|
|
time_verbose_path=_time_verbose_path(suite_dir, "cdp-first-page", run_id),
|
|
cgroup_artifact_dir=_cgroup_artifact_dir(suite_dir, "cdp-first-page", run_id),
|
|
)
|
|
row["run"] = run_id
|
|
row.update(cache_fields)
|
|
detail["run"] = run_id
|
|
detail.update(cache_fields)
|
|
rows.append(row)
|
|
details.append(detail)
|
|
|
|
if include_cdp_warm_pages:
|
|
for run_id in range(1, runs + 1):
|
|
cache_fields = prepare_cache("cdp-warm-pages", run_id)
|
|
row, detail = _run_cdp_warm_pages(
|
|
moli_bin,
|
|
timeout_seconds,
|
|
pages=cdp_warm_pages,
|
|
time_verbose_path=_time_verbose_path(suite_dir, "cdp-warm-pages", run_id),
|
|
cgroup_artifact_dir=_cgroup_artifact_dir(suite_dir, "cdp-warm-pages", run_id),
|
|
)
|
|
row["run"] = run_id
|
|
row.update(cache_fields)
|
|
detail["run"] = run_id
|
|
detail.update(cache_fields)
|
|
rows.append(row)
|
|
details.append(detail)
|
|
|
|
for seconds in idle_seconds:
|
|
for run_id in range(1, runs + 1):
|
|
handle = None
|
|
started = time.perf_counter()
|
|
cache_fields = prepare_cache(f"idle-footprint-{seconds:g}s", run_id)
|
|
try:
|
|
handle = start_moli_serve(
|
|
moli_bin,
|
|
timeout_seconds,
|
|
time_verbose_path=_time_verbose_path(suite_dir, f"idle-footprint-{seconds:g}s", run_id),
|
|
cgroup_artifact_dir=_cgroup_artifact_dir(suite_dir, f"idle-footprint-{seconds:g}s", run_id),
|
|
)
|
|
time.sleep(seconds)
|
|
snapshot = snapshot_resources(handle.process.pid)
|
|
elapsed_ms = (time.perf_counter() - started) * 1000.0
|
|
stop_details = stop_moli_serve(handle)
|
|
row = {
|
|
"case": f"idle-footprint-{seconds:g}s",
|
|
"run": run_id,
|
|
"ok": True,
|
|
"elapsed_ms": elapsed_ms,
|
|
"idle_seconds": seconds,
|
|
"serve_ready_ms": handle.ready_ms,
|
|
"pss_bytes": snapshot.get("pss_bytes"),
|
|
"rss_bytes": snapshot.get("rss_bytes"),
|
|
"cpu_percent": snapshot.get("cpu_percent"),
|
|
"process_count": snapshot.get("process_count"),
|
|
"thread_count": snapshot.get("thread_count"),
|
|
"fd_count": snapshot.get("fd_count"),
|
|
"peak_pss_bytes": stop_details.get("resources", {}).get("peak_pss_bytes"),
|
|
"peak_rss_bytes": stop_details.get("resources", {}).get("peak_rss_bytes"),
|
|
"peak_cpu_percent": stop_details.get("resources", {}).get("peak_cpu_percent"),
|
|
"peak_process_count": stop_details.get("resources", {}).get("peak_process_count"),
|
|
"peak_thread_count": stop_details.get("resources", {}).get("peak_thread_count"),
|
|
"peak_fd_count": stop_details.get("resources", {}).get("peak_fd_count"),
|
|
**cache_fields,
|
|
**_time_verbose_row_fields(stop_details.get("time_verbose")),
|
|
}
|
|
rows.append(row)
|
|
details.append({**row, "snapshot": snapshot, "serve": stop_details})
|
|
except Exception as error:
|
|
elapsed_ms = (time.perf_counter() - started) * 1000.0
|
|
stop_details = stop_moli_serve(handle)
|
|
row = {
|
|
"case": f"idle-footprint-{seconds:g}s",
|
|
"run": run_id,
|
|
"ok": False,
|
|
"elapsed_ms": elapsed_ms,
|
|
"idle_seconds": seconds,
|
|
"error": str(error),
|
|
**cache_fields,
|
|
**_time_verbose_row_fields(stop_details.get("time_verbose")),
|
|
}
|
|
rows.append(row)
|
|
details.append({**row, "serve": stop_details})
|
|
|
|
with SyntheticServer() as server:
|
|
for run_id in range(1, runs + 1):
|
|
cache_fields = prepare_cache("cli-fetch-local-js", run_id)
|
|
result = run_process(
|
|
[
|
|
str(moli_bin),
|
|
"fetch",
|
|
"--dump",
|
|
"html",
|
|
"--wait-until",
|
|
"done",
|
|
"--wait-script",
|
|
"document.querySelector('[data-benchmark-status=\"ok\"]') !== null",
|
|
"--timeout",
|
|
str(int(timeout_seconds * 1000)),
|
|
f"{server.base_url}/dynamic-script",
|
|
],
|
|
cwd=REPO_ROOT,
|
|
timeout_seconds=timeout_seconds + 1,
|
|
env=clear_proxy_env(os.environ),
|
|
time_verbose_path=_time_verbose_path(suite_dir, "cli-fetch-local-js", run_id),
|
|
cgroup_artifact_dir=_cgroup_artifact_dir(suite_dir, "cli-fetch-local-js", run_id),
|
|
)
|
|
row = {
|
|
"case": "cli-fetch-local-js",
|
|
"run": run_id,
|
|
"ok": result.returncode == 0 and not result.timed_out,
|
|
"elapsed_ms": result.elapsed_ms,
|
|
"returncode": result.returncode,
|
|
"timed_out": result.timed_out,
|
|
"peak_pss_bytes": result.resources.get("peak_pss_bytes"),
|
|
"peak_rss_bytes": result.resources.get("peak_rss_bytes"),
|
|
"peak_cpu_percent": result.resources.get("peak_cpu_percent"),
|
|
"peak_process_count": result.resources.get("peak_process_count"),
|
|
"peak_thread_count": result.resources.get("peak_thread_count"),
|
|
"peak_fd_count": result.resources.get("peak_fd_count"),
|
|
"output_sha256": _hash_output(result.output_digest_material()),
|
|
**cache_fields,
|
|
**_time_verbose_row_fields(result.time_verbose),
|
|
}
|
|
rows.append(row)
|
|
details.append({**row, "process": result.json_summary(include_output=not row["ok"])})
|
|
|
|
cases = sorted({str(row["case"]) for row in rows})
|
|
elapsed_by_case: dict[str, list[float]] = {case: [] for case in cases}
|
|
pss_by_case: dict[str, list[float]] = {case: [] for case in cases}
|
|
rss_by_case: dict[str, list[float]] = {case: [] for case in cases}
|
|
process_by_case: dict[str, list[float]] = {case: [] for case in cases}
|
|
thread_by_case: dict[str, list[float]] = {case: [] for case in cases}
|
|
fd_by_case: dict[str, list[float]] = {case: [] for case in cases}
|
|
time_rss_by_case: dict[str, list[float]] = {case: [] for case in cases}
|
|
for row in rows:
|
|
case = str(row["case"])
|
|
if row.get("ok") and row.get("elapsed_ms") is not None:
|
|
elapsed_by_case.setdefault(case, []).append(float(row["elapsed_ms"]))
|
|
for target, keys in (
|
|
(pss_by_case, ("pss_bytes", "peak_pss_bytes")),
|
|
(rss_by_case, ("rss_bytes", "peak_rss_bytes")),
|
|
(process_by_case, ("process_count", "peak_process_count")),
|
|
(thread_by_case, ("thread_count", "peak_thread_count")),
|
|
(fd_by_case, ("fd_count", "peak_fd_count")),
|
|
(time_rss_by_case, ("time_max_rss_bytes",)),
|
|
):
|
|
for key in keys:
|
|
if row.get("ok") and row.get(key) is not None:
|
|
target.setdefault(case, []).append(float(row[key]))
|
|
break
|
|
|
|
time_verbose_entries = []
|
|
cgroup_entries = []
|
|
for detail in details:
|
|
process_time = (
|
|
detail.get("process", {}).get("time_verbose")
|
|
if isinstance(detail.get("process"), dict)
|
|
else None
|
|
)
|
|
process_cgroup = detail.get("process", {}).get("cgroup") if isinstance(detail.get("process"), dict) else None
|
|
serve_time = (
|
|
detail.get("serve", {}).get("time_verbose")
|
|
if isinstance(detail.get("serve"), dict)
|
|
else None
|
|
)
|
|
serve_cgroup = detail.get("serve", {}).get("cgroup") if isinstance(detail.get("serve"), dict) else None
|
|
for entry in (process_time, serve_time):
|
|
if isinstance(entry, dict):
|
|
time_verbose_entries.append(entry)
|
|
for entry in (process_cgroup, serve_cgroup):
|
|
if isinstance(entry, dict):
|
|
cgroup_entries.append(entry)
|
|
|
|
total_failures = sum(1 for row in rows if not row.get("ok"))
|
|
formal_gate_rows = _startup_formal_gate_rows(
|
|
profile=profile,
|
|
runs=runs,
|
|
include_cdp_first_page=include_cdp_first_page,
|
|
include_cdp_warm_pages=include_cdp_warm_pages,
|
|
cdp_warm_pages=cdp_warm_pages,
|
|
idle_seconds=tuple(float(seconds) for seconds in idle_seconds),
|
|
total_failures=total_failures,
|
|
)
|
|
gate_failures = sum(1 for row in formal_gate_rows if row.get("ok") is not True)
|
|
summary = {
|
|
"suite": "startup",
|
|
"profile": profile,
|
|
"runs": runs,
|
|
"timeout_seconds": timeout_seconds,
|
|
"include_cdp_first_page": include_cdp_first_page,
|
|
"include_cdp_warm_pages": include_cdp_warm_pages,
|
|
"cdp_warm_pages": cdp_warm_pages if include_cdp_warm_pages else 0,
|
|
"idle_seconds": list(idle_seconds),
|
|
"drop_os_cache": drop_os_cache,
|
|
"cache_events": cache_events,
|
|
"cache_artifacts": [
|
|
event.get("artifact_path") for event in cache_events if isinstance(event.get("artifact_path"), str)
|
|
],
|
|
"time_verbose_available": any(entry.get("available") for entry in time_verbose_entries),
|
|
"time_verbose_artifacts": [
|
|
entry.get("raw_path") for entry in time_verbose_entries if isinstance(entry.get("raw_path"), str)
|
|
],
|
|
"cgroup_available": any(entry.get("available") for entry in cgroup_entries),
|
|
"cgroup_artifacts": [
|
|
artifact
|
|
for entry in cgroup_entries
|
|
for artifact in entry.get("artifacts", [])
|
|
if isinstance(artifact, str)
|
|
],
|
|
"cases": {
|
|
case: {
|
|
"elapsed_ms": summarize(values),
|
|
"pss_bytes": summarize(pss_by_case.get(case, [])),
|
|
"rss_bytes": summarize(rss_by_case.get(case, [])),
|
|
"process_count": summarize(process_by_case.get(case, [])),
|
|
"thread_count": summarize(thread_by_case.get(case, [])),
|
|
"fd_count": summarize(fd_by_case.get(case, [])),
|
|
"time_max_rss_bytes": summarize(time_rss_by_case.get(case, [])),
|
|
"binary_bytes": next((row.get("binary_bytes") for row in rows if row.get("case") == case), None),
|
|
"stripped_binary_bytes": next(
|
|
(row.get("stripped_binary_bytes") for row in rows if row.get("case") == case),
|
|
None,
|
|
),
|
|
"stripped_available": next(
|
|
(row.get("stripped_available") for row in rows if row.get("case") == case),
|
|
None,
|
|
),
|
|
"tar_gz_bytes": next((row.get("tar_gz_bytes") for row in rows if row.get("case") == case), None),
|
|
"image_format": next((row.get("image_format") for row in rows if row.get("case") == case), None),
|
|
"image_uncompressed_bytes": next(
|
|
(row.get("image_uncompressed_bytes") for row in rows if row.get("case") == case),
|
|
None,
|
|
),
|
|
"image_compressed_bytes": next(
|
|
(row.get("image_compressed_bytes") for row in rows if row.get("case") == case),
|
|
None,
|
|
),
|
|
"image_dependency_count": next(
|
|
(row.get("image_dependency_count") for row in rows if row.get("case") == case),
|
|
None,
|
|
),
|
|
"cdp_page_elapsed_p50_ms": next(
|
|
(row.get("cdp_page_elapsed_p50_ms") for row in rows if row.get("case") == case),
|
|
None,
|
|
),
|
|
"cdp_page_elapsed_p95_ms": next(
|
|
(row.get("cdp_page_elapsed_p95_ms") for row in rows if row.get("case") == case),
|
|
None,
|
|
),
|
|
"cdp_pages": next((row.get("cdp_pages") for row in rows if row.get("case") == case), None),
|
|
"cdp_page_passes": next(
|
|
(row.get("cdp_page_passes") for row in rows if row.get("case") == case),
|
|
None,
|
|
),
|
|
"process_cache_modes": sorted(
|
|
{
|
|
str(row.get("process_cache_mode"))
|
|
for row in rows
|
|
if row.get("case") == case and row.get("process_cache_mode") is not None
|
|
}
|
|
),
|
|
"kernel_cache_modes": sorted(
|
|
{
|
|
str(row.get("kernel_cache_mode"))
|
|
for row in rows
|
|
if row.get("case") == case and row.get("kernel_cache_mode") is not None
|
|
}
|
|
),
|
|
"failures": sum(1 for row in rows if row.get("case") == case and not row.get("ok")),
|
|
}
|
|
for case, values in elapsed_by_case.items()
|
|
},
|
|
"formal_gate_rows": formal_gate_rows,
|
|
"gate_failures": gate_failures,
|
|
"total_failures": total_failures,
|
|
}
|
|
write_csv(suite_dir / "runs.csv", rows)
|
|
write_json(suite_dir / "runs.json", details)
|
|
write_json(suite_dir / "gate-rows.json", formal_gate_rows)
|
|
write_json(suite_dir / "summary.json", summary)
|
|
return summary
|