@ogygia/devtools 0.0.0-stage → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +25 -2
- package/dist/client.d.ts +3 -0
- package/dist/client.js +24 -0
- package/dist/devtools/BoundaryOverlay.svelte +396 -0
- package/dist/devtools/BoundaryOverlay.svelte.d.ts +19 -0
- package/dist/devtools/Devtools.svelte +663 -0
- package/dist/devtools/Devtools.svelte.d.ts +15 -0
- package/dist/devtools/HubTab.svelte +206 -0
- package/dist/devtools/HubTab.svelte.d.ts +11 -0
- package/dist/devtools/HydrationTab.svelte +237 -0
- package/dist/devtools/HydrationTab.svelte.d.ts +13 -0
- package/dist/devtools/IslandDetail.svelte +615 -0
- package/dist/devtools/IslandDetail.svelte.d.ts +15 -0
- package/dist/devtools/LedgerTab.svelte +287 -0
- package/dist/devtools/LedgerTab.svelte.d.ts +11 -0
- package/dist/devtools/LensTab.svelte +290 -0
- package/dist/devtools/LensTab.svelte.d.ts +19 -0
- package/dist/devtools/NavTab.svelte +229 -0
- package/dist/devtools/NavTab.svelte.d.ts +11 -0
- package/dist/devtools/PageTab.svelte +1122 -0
- package/dist/devtools/PageTab.svelte.d.ts +15 -0
- package/dist/devtools/ProfilerTab.svelte +755 -0
- package/dist/devtools/ProfilerTab.svelte.d.ts +15 -0
- package/dist/devtools/PropsTree.svelte +155 -0
- package/dist/devtools/PropsTree.svelte.d.ts +15 -0
- package/dist/devtools/SessionTab.svelte +796 -0
- package/dist/devtools/SessionTab.svelte.d.ts +11 -0
- package/dist/devtools/TimelineTab.svelte +267 -0
- package/dist/devtools/TimelineTab.svelte.d.ts +11 -0
- package/dist/devtools/WireTab.svelte +149 -0
- package/dist/devtools/WireTab.svelte.d.ts +11 -0
- package/dist/devtools/bytes.d.ts +70 -0
- package/dist/devtools/bytes.js +82 -0
- package/dist/devtools/cache-headers.d.ts +20 -0
- package/dist/devtools/cache-headers.js +81 -0
- package/dist/devtools/highlight.d.ts +7 -0
- package/dist/devtools/highlight.js +83 -0
- package/dist/devtools/hydration.d.ts +48 -0
- package/dist/devtools/hydration.js +93 -0
- package/dist/devtools/index.d.ts +3 -0
- package/dist/devtools/index.js +3 -0
- package/dist/devtools/ledger-dom.d.ts +7 -0
- package/dist/devtools/ledger-dom.js +151 -0
- package/dist/devtools/leftovers.d.ts +77 -0
- package/dist/devtools/leftovers.js +319 -0
- package/dist/devtools/names.d.ts +1 -0
- package/dist/devtools/names.js +34 -0
- package/dist/devtools/page.d.ts +96 -0
- package/dist/devtools/page.js +957 -0
- package/dist/devtools/profile-anchors.d.ts +22 -0
- package/dist/devtools/profile-anchors.js +135 -0
- package/dist/devtools/profile-store.d.ts +175 -0
- package/dist/devtools/profile-store.js +239 -0
- package/dist/devtools/regions.d.ts +77 -0
- package/dist/devtools/regions.js +132 -0
- package/dist/devtools/session.d.ts +31 -0
- package/dist/devtools/session.js +455 -0
- package/dist/devtools/since-load.d.ts +50 -0
- package/dist/devtools/since-load.js +89 -0
- package/dist/devtools/source-lines.d.ts +24 -0
- package/dist/devtools/source-lines.js +106 -0
- package/dist/devtools/styles.d.ts +52 -0
- package/dist/devtools/styles.js +248 -0
- package/dist/devtools/testing-engine.d.ts +83 -0
- package/dist/devtools/testing-engine.js +220 -0
- package/dist/devtools/ui-app.d.ts +5 -0
- package/dist/devtools/ui-app.js +69 -0
- package/dist/devtools/ui.d.ts +50 -0
- package/dist/devtools/ui.js +175 -0
- package/dist/profiler/alloc.d.ts +75 -0
- package/dist/profiler/alloc.js +76 -0
- package/dist/profiler/analyze.d.ts +437 -0
- package/dist/profiler/analyze.js +1941 -0
- package/dist/profiler/app-path.d.ts +7 -0
- package/dist/profiler/app-path.js +26 -0
- package/dist/profiler/async-io.d.ts +58 -0
- package/dist/profiler/async-io.js +187 -0
- package/dist/profiler/beacon-standalone.d.ts +4 -0
- package/dist/profiler/beacon-standalone.js +409 -0
- package/dist/profiler/browser-findings.d.ts +25 -0
- package/dist/profiler/browser-findings.js +260 -0
- package/dist/profiler/byte-strip.d.ts +58 -0
- package/dist/profiler/byte-strip.js +228 -0
- package/dist/profiler/client-files.d.ts +16 -0
- package/dist/profiler/client-files.js +60 -0
- package/dist/profiler/client-windows.d.ts +49 -0
- package/dist/profiler/client-windows.js +95 -0
- package/dist/profiler/client.d.ts +40 -0
- package/dist/profiler/client.js +82 -0
- package/dist/profiler/compare.d.ts +308 -0
- package/dist/profiler/compare.js +733 -0
- package/dist/profiler/compress.d.ts +10 -0
- package/dist/profiler/compress.js +36 -0
- package/dist/profiler/contention.d.ts +60 -0
- package/dist/profiler/contention.js +86 -0
- package/dist/profiler/crypto.d.ts +19 -0
- package/dist/profiler/crypto.js +101 -0
- package/dist/profiler/doc-diff.d.ts +47 -0
- package/dist/profiler/doc-diff.js +214 -0
- package/dist/profiler/dom-diff.d.ts +43 -0
- package/dist/profiler/dom-diff.js +121 -0
- package/dist/profiler/drill.d.ts +89 -0
- package/dist/profiler/drill.js +553 -0
- package/dist/profiler/embedded-maps.d.ts +37 -0
- package/dist/profiler/embedded-maps.js +135 -0
- package/dist/profiler/env.d.ts +6 -0
- package/dist/profiler/env.js +10 -0
- package/dist/profiler/forecast.d.ts +76 -0
- package/dist/profiler/forecast.js +225 -0
- package/dist/profiler/frames.d.ts +11 -0
- package/dist/profiler/frames.js +42 -0
- package/dist/profiler/gc.d.ts +144 -0
- package/dist/profiler/gc.js +271 -0
- package/dist/profiler/hole-slots.d.ts +31 -0
- package/dist/profiler/hole-slots.js +90 -0
- package/dist/profiler/index.d.ts +288 -0
- package/dist/profiler/index.js +4921 -0
- package/dist/profiler/insights.d.ts +96 -0
- package/dist/profiler/insights.js +239 -0
- package/dist/profiler/ledger.d.ts +121 -0
- package/dist/profiler/ledger.js +440 -0
- package/dist/profiler/lineage.d.ts +81 -0
- package/dist/profiler/lineage.js +359 -0
- package/dist/profiler/module-map.d.ts +12 -0
- package/dist/profiler/module-map.js +94 -0
- package/dist/profiler/net.d.ts +169 -0
- package/dist/profiler/net.js +586 -0
- package/dist/profiler/on-demand.d.ts +11 -0
- package/dist/profiler/on-demand.js +15 -0
- package/dist/profiler/own-requests.d.ts +9 -0
- package/dist/profiler/own-requests.js +17 -0
- package/dist/profiler/patterns.d.ts +476 -0
- package/dist/profiler/patterns.js +2203 -0
- package/dist/profiler/profiler-router.d.ts +245 -0
- package/dist/profiler/profiler-router.js +113 -0
- package/dist/profiler/refresh-stub.d.ts +2 -0
- package/dist/profiler/refresh-stub.js +37 -0
- package/dist/profiler/report.d.ts +1404 -0
- package/dist/profiler/report.js +2638 -0
- package/dist/profiler/river.d.ts +65 -0
- package/dist/profiler/river.js +298 -0
- package/dist/profiler/route-files.d.ts +22 -0
- package/dist/profiler/route-files.js +55 -0
- package/dist/profiler/route-imports.d.ts +14 -0
- package/dist/profiler/route-imports.js +83 -0
- package/dist/profiler/saving.d.ts +22 -0
- package/dist/profiler/saving.js +42 -0
- package/dist/profiler/score.d.ts +109 -0
- package/dist/profiler/score.js +261 -0
- package/dist/profiler/session-cookie.d.ts +28 -0
- package/dist/profiler/session-cookie.js +45 -0
- package/dist/profiler/sink.d.ts +81 -0
- package/dist/profiler/sink.js +124 -0
- package/dist/profiler/site-fixes.d.ts +25 -0
- package/dist/profiler/site-fixes.js +62 -0
- package/dist/profiler/source-scan.d.ts +210 -0
- package/dist/profiler/source-scan.js +1724 -0
- package/dist/profiler/span.d.ts +90 -0
- package/dist/profiler/span.js +109 -0
- package/dist/profiler/stacks.d.ts +43 -0
- package/dist/profiler/stacks.js +107 -0
- package/dist/profiler/standalone.d.ts +27 -0
- package/dist/profiler/standalone.js +98 -0
- package/dist/profiler/steps.d.ts +39 -0
- package/dist/profiler/steps.js +74 -0
- package/dist/profiler/storage/index.d.ts +87 -0
- package/dist/profiler/storage/index.js +76 -0
- package/dist/profiler/storage/postgres.d.ts +12 -0
- package/dist/profiler/storage/postgres.js +94 -0
- package/dist/profiler/storage/redis.d.ts +19 -0
- package/dist/profiler/storage/redis.js +148 -0
- package/dist/profiler/storage/sqlite.d.ts +2 -0
- package/dist/profiler/storage/sqlite.js +85 -0
- package/dist/profiler/storage/visits.d.ts +4 -0
- package/dist/profiler/storage/visits.js +6 -0
- package/dist/profiler/stream-tail.d.ts +26 -0
- package/dist/profiler/stream-tail.js +74 -0
- package/dist/profiler/timeline.d.ts +279 -0
- package/dist/profiler/timeline.js +690 -0
- package/dist/profiler/ui/AllocStrip.svelte +146 -0
- package/dist/profiler/ui/AllocStrip.svelte.d.ts +17 -0
- package/dist/profiler/ui/Awaits.svelte +363 -0
- package/dist/profiler/ui/Awaits.svelte.d.ts +16 -0
- package/dist/profiler/ui/ByteStrip.svelte +182 -0
- package/dist/profiler/ui/ByteStrip.svelte.d.ts +18 -0
- package/dist/profiler/ui/Compare.svelte +24 -0
- package/dist/profiler/ui/Compare.svelte.d.ts +16 -0
- package/dist/profiler/ui/CompareBody.svelte +429 -0
- package/dist/profiler/ui/CompareBody.svelte.d.ts +8 -0
- package/dist/profiler/ui/ComponentsTable.svelte +265 -0
- package/dist/profiler/ui/ComponentsTable.svelte.d.ts +20 -0
- package/dist/profiler/ui/Dashboard.svelte +492 -0
- package/dist/profiler/ui/Dashboard.svelte.d.ts +31 -0
- package/dist/profiler/ui/DataRiver.svelte +206 -0
- package/dist/profiler/ui/DataRiver.svelte.d.ts +13 -0
- package/dist/profiler/ui/DomTravel.svelte +207 -0
- package/dist/profiler/ui/DomTravel.svelte.d.ts +9 -0
- package/dist/profiler/ui/DownloadHtml.svelte +52 -0
- package/dist/profiler/ui/DownloadHtml.svelte.d.ts +10 -0
- package/dist/profiler/ui/Drill.svelte +276 -0
- package/dist/profiler/ui/Drill.svelte.d.ts +12 -0
- package/dist/profiler/ui/ExportButton.svelte +59 -0
- package/dist/profiler/ui/ExportButton.svelte.d.ts +8 -0
- package/dist/profiler/ui/Flame.svelte +222 -0
- package/dist/profiler/ui/Flame.svelte.d.ts +12 -0
- package/dist/profiler/ui/FrameDetails.svelte +308 -0
- package/dist/profiler/ui/FrameDetails.svelte.d.ts +16 -0
- package/dist/profiler/ui/FunctionsTable.svelte +156 -0
- package/dist/profiler/ui/FunctionsTable.svelte.d.ts +17 -0
- package/dist/profiler/ui/IslandHeatmap.svelte +151 -0
- package/dist/profiler/ui/IslandHeatmap.svelte.d.ts +16 -0
- package/dist/profiler/ui/IslandsTable.svelte +448 -0
- package/dist/profiler/ui/IslandsTable.svelte.d.ts +16 -0
- package/dist/profiler/ui/KeepLocal.svelte +46 -0
- package/dist/profiler/ui/KeepLocal.svelte.d.ts +8 -0
- package/dist/profiler/ui/Ledger.svelte +196 -0
- package/dist/profiler/ui/Ledger.svelte.d.ts +18 -0
- package/dist/profiler/ui/LocalCompare.svelte +48 -0
- package/dist/profiler/ui/LocalCompare.svelte.d.ts +8 -0
- package/dist/profiler/ui/LocalReports.svelte +68 -0
- package/dist/profiler/ui/LocalReports.svelte.d.ts +7 -0
- package/dist/profiler/ui/Login.svelte +13 -0
- package/dist/profiler/ui/Login.svelte.d.ts +12 -0
- package/dist/profiler/ui/LoginForm.svelte +42 -0
- package/dist/profiler/ui/LoginForm.svelte.d.ts +7 -0
- package/dist/profiler/ui/Message.svelte +10 -0
- package/dist/profiler/ui/Message.svelte.d.ts +12 -0
- package/dist/profiler/ui/OnThisPage.svelte +137 -0
- package/dist/profiler/ui/OnThisPage.svelte.d.ts +3 -0
- package/dist/profiler/ui/OneClock.svelte +297 -0
- package/dist/profiler/ui/OneClock.svelte.d.ts +27 -0
- package/dist/profiler/ui/Panel.svelte +10 -0
- package/dist/profiler/ui/Panel.svelte.d.ts +16 -0
- package/dist/profiler/ui/Paths.svelte +269 -0
- package/dist/profiler/ui/Paths.svelte.d.ts +15 -0
- package/dist/profiler/ui/Patterns.svelte +364 -0
- package/dist/profiler/ui/Patterns.svelte.d.ts +15 -0
- package/dist/profiler/ui/PermalinkGate.svelte +143 -0
- package/dist/profiler/ui/PermalinkGate.svelte.d.ts +10 -0
- package/dist/profiler/ui/RenderSteps.svelte +218 -0
- package/dist/profiler/ui/RenderSteps.svelte.d.ts +13 -0
- package/dist/profiler/ui/Report.svelte +28 -0
- package/dist/profiler/ui/Report.svelte.d.ts +20 -0
- package/dist/profiler/ui/ReportBody.svelte +2592 -0
- package/dist/profiler/ui/ReportBody.svelte.d.ts +21 -0
- package/dist/profiler/ui/Run.svelte +17 -0
- package/dist/profiler/ui/Run.svelte.d.ts +14 -0
- package/dist/profiler/ui/RunView.svelte +146 -0
- package/dist/profiler/ui/RunView.svelte.d.ts +10 -0
- package/dist/profiler/ui/ScoreCard.svelte +197 -0
- package/dist/profiler/ui/ScoreCard.svelte.d.ts +7 -0
- package/dist/profiler/ui/Scrub.svelte +293 -0
- package/dist/profiler/ui/Scrub.svelte.d.ts +18 -0
- package/dist/profiler/ui/SeedExplainer.svelte +193 -0
- package/dist/profiler/ui/SeedExplainer.svelte.d.ts +16 -0
- package/dist/profiler/ui/ShareLink.svelte +148 -0
- package/dist/profiler/ui/ShareLink.svelte.d.ts +8 -0
- package/dist/profiler/ui/Shell.svelte +50 -0
- package/dist/profiler/ui/Shell.svelte.d.ts +27 -0
- package/dist/profiler/ui/Sidebar.svelte +312 -0
- package/dist/profiler/ui/Sidebar.svelte.d.ts +7 -0
- package/dist/profiler/ui/Site.svelte +20 -0
- package/dist/profiler/ui/Site.svelte.d.ts +23 -0
- package/dist/profiler/ui/SiteView.svelte +385 -0
- package/dist/profiler/ui/SiteView.svelte.d.ts +27 -0
- package/dist/profiler/ui/ThemeSwitch.svelte +78 -0
- package/dist/profiler/ui/ThemeSwitch.svelte.d.ts +3 -0
- package/dist/profiler/ui/Timeline.svelte +672 -0
- package/dist/profiler/ui/Timeline.svelte.d.ts +22 -0
- package/dist/profiler/ui/Treemap.svelte +278 -0
- package/dist/profiler/ui/Treemap.svelte.d.ts +20 -0
- package/dist/profiler/ui/Upload.svelte +19 -0
- package/dist/profiler/ui/Upload.svelte.d.ts +10 -0
- package/dist/profiler/ui/UploadForm.svelte +59 -0
- package/dist/profiler/ui/UploadForm.svelte.d.ts +6 -0
- package/dist/profiler/ui/WaitingTable.svelte +55 -0
- package/dist/profiler/ui/WaitingTable.svelte.d.ts +14 -0
- package/dist/profiler/ui/Waterfall.svelte +371 -0
- package/dist/profiler/ui/Waterfall.svelte.d.ts +7 -0
- package/dist/profiler/ui/WhatIf.svelte +87 -0
- package/dist/profiler/ui/WhatIf.svelte.d.ts +18 -0
- package/dist/profiler/ui/fake-progress.svelte.d.ts +15 -0
- package/dist/profiler/ui/fake-progress.svelte.js +38 -0
- package/dist/profiler/ui/format.d.ts +20 -0
- package/dist/profiler/ui/format.js +71 -0
- package/dist/profiler/ui/permalink.d.ts +9 -0
- package/dist/profiler/ui/permalink.js +108 -0
- package/dist/profiler/ui/press.d.ts +5 -0
- package/dist/profiler/ui/press.js +19 -0
- package/dist/profiler/ui/profiler.css +650 -0
- package/dist/profiler/ui/report-data.d.ts +189 -0
- package/dist/profiler/ui/report-data.js +393 -0
- package/dist/profiler/ui/report-request.d.ts +6 -0
- package/dist/profiler/ui/report-request.js +50 -0
- package/dist/profiler/ui/row-anchor.svelte.d.ts +12 -0
- package/dist/profiler/ui/row-anchor.svelte.js +44 -0
- package/dist/profiler/ui/sort.svelte.d.ts +14 -0
- package/dist/profiler/ui/sort.svelte.js +37 -0
- package/dist/profiler/ui/store.d.ts +45 -0
- package/dist/profiler/ui/store.js +92 -0
- package/dist/profiler/visit.d.ts +389 -0
- package/dist/profiler/visit.js +1002 -0
- package/dist/report.d.ts +5 -0
- package/dist/report.js +4 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +38 -0
- package/dist/vite.d.ts +12 -0
- package/dist/vite.js +78 -0
- package/package.json +103 -3
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
<script>/**
|
|
2
|
+
* The profiler dashboard (`/__profiler`): live request log, slowest routes, saved reports, and the
|
|
3
|
+
* "profile a page" form. Static server render — no island; it's a data snapshot. Ports report.ts's
|
|
4
|
+
* render_dashboard.
|
|
5
|
+
*/
|
|
6
|
+
import { fmt_ms, label_of } from "./format.js";
|
|
7
|
+
import Shell from "./Shell.svelte";
|
|
8
|
+
import LocalReports from "./LocalReports.svelte" with { wake: "load" };
|
|
9
|
+
let { data } = $props();
|
|
10
|
+
const { base, recent, routes, reports, top_fix, site_fixes, holes, recording, dev, rss_mb, inflight, history, by, tag_keys, trap, sampled, background_note } = $derived(data);
|
|
11
|
+
const sampled_max = $derived(Math.max(...sampled?.functions.map((f) => f.self_ms) ?? [0], .01));
|
|
12
|
+
const time = (ms) => new Date(ms).toLocaleTimeString();
|
|
13
|
+
let recent_desc = $derived(recent.slice(-40).reverse());
|
|
14
|
+
let top_routes = $derived(routes.slice(0, 20));
|
|
15
|
+
// the previous page-mode report of the same page, for each report — the "compare" link
|
|
16
|
+
const prev_of = $derived.by(() => {
|
|
17
|
+
const m = new Map();
|
|
18
|
+
for (const h of history) for (let i = 1; i < h.points.length; i++) m.set(h.points[i].id, h.points[i - 1].id);
|
|
19
|
+
return m;
|
|
20
|
+
});
|
|
21
|
+
const tracked = $derived(history.filter((h) => h.points.length >= 2));
|
|
22
|
+
const spark = (pts) => {
|
|
23
|
+
const w = 160, h = 28, pad = 3;
|
|
24
|
+
const max = Math.max(...pts.map((p) => p.median), 1);
|
|
25
|
+
const step = pts.length > 1 ? (w - 2 * pad) / (pts.length - 1) : 0;
|
|
26
|
+
return {
|
|
27
|
+
w,
|
|
28
|
+
h,
|
|
29
|
+
poly: pts.map((p, i) => `${pad + i * step},${h - pad - p.median / max * (h - 2 * pad)}`).join(" ")
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const delta_pct = (pts) => {
|
|
33
|
+
const a = pts[pts.length - 2].median;
|
|
34
|
+
const b = pts[pts.length - 1].median;
|
|
35
|
+
return a > 0 ? (b - a) / a * 100 : 0;
|
|
36
|
+
};
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<svelte:head><title>profiler — dashboard</title></svelte:head>
|
|
40
|
+
|
|
41
|
+
<Shell {base}>
|
|
42
|
+
<div class="head">
|
|
43
|
+
<h1>
|
|
44
|
+
SSR profiler
|
|
45
|
+
<small>live since server start</small>
|
|
46
|
+
</h1>
|
|
47
|
+
<div class="actions">
|
|
48
|
+
<a
|
|
49
|
+
class="btn"
|
|
50
|
+
href="{base}/site"
|
|
51
|
+
title="Every request as a dot, and where the site's time goes per route — from this instance, or from a sink over days"
|
|
52
|
+
>The whole site</a
|
|
53
|
+
>
|
|
54
|
+
<a class="btn" href="{base}/view">Import<span class="sub">.ogp</span></a>
|
|
55
|
+
<a
|
|
56
|
+
class="btn danger"
|
|
57
|
+
class:recording
|
|
58
|
+
href="{base}/reset"
|
|
59
|
+
title="Stop any running or stuck recording and clear the profiler's state.">Reset</a
|
|
60
|
+
>
|
|
61
|
+
{#if !dev}<a class="btn" href="{base}/logout">Lock</a>{/if}
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div class="summary">
|
|
66
|
+
<div class="stat"><b>{rss_mb} MB</b><span>memory (rss)</span></div>
|
|
67
|
+
<div class="stat"><b>{inflight}</b><span>requests in flight</span></div>
|
|
68
|
+
<div class="stat"><b>{reports.length}</b><span>reports held</span></div>
|
|
69
|
+
<div class="stat"><b>{routes.length}</b><span>routes seen</span></div>
|
|
70
|
+
{#if tracked.length}<div class="stat">
|
|
71
|
+
<b>{tracked.length}</b><span>pages tracked over time</span>
|
|
72
|
+
</div>{/if}
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
{#if recording}
|
|
76
|
+
<p class="verdict">
|
|
77
|
+
A profile is running right now. Refresh in a moment — or hit <b>Reset</b> above if a run got stuck.
|
|
78
|
+
</p>
|
|
79
|
+
{/if}
|
|
80
|
+
|
|
81
|
+
<div class="grid">
|
|
82
|
+
<section class="panel">
|
|
83
|
+
<h2>Profile a page</h2>
|
|
84
|
+
<p class="hint">
|
|
85
|
+
A path on this site. It renders through your real server a few times and shows exactly where
|
|
86
|
+
the time went — components, functions, allocations, and outbound calls.
|
|
87
|
+
</p>
|
|
88
|
+
<form class="inline" action="{base}/run" method="get">
|
|
89
|
+
<label>path <input name="p" placeholder="/some/slow/page" size="24" required /></label>
|
|
90
|
+
<label>renders <input name="runs" value="5" size="3" /></label>
|
|
91
|
+
<label
|
|
92
|
+
class="ogp-opt"
|
|
93
|
+
title="Recommended on serverless: the report can't be kept in memory across invocations. Download the encrypted .ogp, then open it via Import."
|
|
94
|
+
>
|
|
95
|
+
<input type="checkbox" name="format" value="ogp" /> <code>.ogp</code>
|
|
96
|
+
</label>
|
|
97
|
+
<button class="primary">Profile</button>
|
|
98
|
+
</form>
|
|
99
|
+
<p class="hint">
|
|
100
|
+
On a <b>serverless</b> host, tick <code>.ogp</code> — the profile streams back as an
|
|
101
|
+
encrypted file, then <a href="{base}/view">open it here</a>. Or profile one live request
|
|
102
|
+
with the <code>x-profile: <secret></code> header.
|
|
103
|
+
</p>
|
|
104
|
+
</section>
|
|
105
|
+
<section class="panel">
|
|
106
|
+
<h2>Getting started</h2>
|
|
107
|
+
<p class="hint">
|
|
108
|
+
Type a path and hit <b>Profile</b> to record a page. Every recording shows up in the sidebar
|
|
109
|
+
and, with a store configured, is shared across instances. Watch a route's median over time
|
|
110
|
+
under
|
|
111
|
+
<b>Pages over time</b>; a jump is a regression you can open a compare on.
|
|
112
|
+
</p>
|
|
113
|
+
<p class="hint">
|
|
114
|
+
The <b>whole site</b> view plots every request and where each route spends its time.
|
|
115
|
+
<b>Reset</b>
|
|
116
|
+
clears a stuck recording if the site drags after profiling.
|
|
117
|
+
</p>
|
|
118
|
+
</section>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<LocalReports {base} server_ids={reports.map((r) => r.id)} />
|
|
122
|
+
|
|
123
|
+
{#if trap || sampled || background_note}
|
|
124
|
+
<h2>In the background</h2>
|
|
125
|
+
{#if background_note}<p class="verdict">{background_note}</p>{/if}
|
|
126
|
+
{#if trap}
|
|
127
|
+
<p class="hint">
|
|
128
|
+
<b>Catch the slow one:</b>
|
|
129
|
+
{trap.armed ? 'armed' : 'done'} — a coarse sampler runs in {Math.round(
|
|
130
|
+
trap.window_ms / 1000
|
|
131
|
+
)} s windows and keeps the window when a request crosses <b>{trap.over} ms</b>. Caught {trap.caught}
|
|
132
|
+
of {trap.keep}.{#if trap.caught}
|
|
133
|
+
The reports are in the list below, labelled "caught".{/if}
|
|
134
|
+
</p>
|
|
135
|
+
{/if}
|
|
136
|
+
{#if sampled}
|
|
137
|
+
<p class="hint">
|
|
138
|
+
<b>Always-on sampling:</b> one {Math.round((sampled.window_ms / 1000) * 10) / 10} s window every
|
|
139
|
+
{sampled.every_s} s, folded into the table below — {sampled.windows} window{sampled.windows ===
|
|
140
|
+
1
|
|
141
|
+
? ''
|
|
142
|
+
: 's'} so far{#if sampled.since}, since {time(sampled.since)}{/if},
|
|
143
|
+
{fmt_ms(sampled.busy_ms)} ms busy of {fmt_ms(sampled.sampled_ms)} ms sampled. Accuracy from volume:
|
|
144
|
+
a function that is hot here is hot in real traffic, whatever one recording says.
|
|
145
|
+
</p>
|
|
146
|
+
{#if sampled.functions.length}
|
|
147
|
+
<table>
|
|
148
|
+
<thead
|
|
149
|
+
><tr
|
|
150
|
+
><th>function</th><th>where</th><th class="num">self ms (summed)</th><th class="num"
|
|
151
|
+
>in windows</th
|
|
152
|
+
></tr
|
|
153
|
+
></thead
|
|
154
|
+
>
|
|
155
|
+
<tbody>
|
|
156
|
+
{#each sampled.functions.slice(0, 25) as f (f.key)}
|
|
157
|
+
<tr>
|
|
158
|
+
<td class="fn"
|
|
159
|
+
><b>{f.name}</b>{#if f.pkg}<span class="hint"> · {f.pkg}</span>{/if}</td
|
|
160
|
+
>
|
|
161
|
+
<td class="file"
|
|
162
|
+
>{f.url}{#if f.line > 0}:{f.line}{/if}</td
|
|
163
|
+
>
|
|
164
|
+
<td class="num bar-cell"
|
|
165
|
+
><div class="bar" style="width:{(f.self_ms / sampled_max) * 100}%"></div>
|
|
166
|
+
<b>{fmt_ms(f.self_ms)}</b></td
|
|
167
|
+
>
|
|
168
|
+
<td class="num">{f.windows}</td>
|
|
169
|
+
</tr>
|
|
170
|
+
{/each}
|
|
171
|
+
</tbody>
|
|
172
|
+
</table>
|
|
173
|
+
{:else}
|
|
174
|
+
<p class="hint">No window sampled yet.</p>
|
|
175
|
+
{/if}
|
|
176
|
+
{/if}
|
|
177
|
+
{/if}
|
|
178
|
+
|
|
179
|
+
<div class="grid">
|
|
180
|
+
{#if tracked.length}
|
|
181
|
+
<section class="panel wide">
|
|
182
|
+
<h2>Pages over time</h2>
|
|
183
|
+
<p class="hint">
|
|
184
|
+
Every page profiled more than once: its median render per run, oldest to newest, and its
|
|
185
|
+
page score then and now. A jump is a regression — open the compare to see which component
|
|
186
|
+
or function moved.
|
|
187
|
+
</p>
|
|
188
|
+
<table>
|
|
189
|
+
<thead>
|
|
190
|
+
<tr
|
|
191
|
+
><th>page</th><th>runs</th><th class="num">latest</th><th class="num">vs previous</th
|
|
192
|
+
><th class="num">score</th><th><span class="sr-only">actions</span></th></tr
|
|
193
|
+
>
|
|
194
|
+
</thead>
|
|
195
|
+
<tbody>
|
|
196
|
+
{#each tracked as h (h.page)}
|
|
197
|
+
{@const g = spark(h.points)}
|
|
198
|
+
{@const d = delta_pct(h.points)}
|
|
199
|
+
{@const last = h.points[h.points.length - 1]}
|
|
200
|
+
{@const before = h.points[h.points.length - 2]}
|
|
201
|
+
<tr>
|
|
202
|
+
<td class="fn">{h.page}</td>
|
|
203
|
+
<td
|
|
204
|
+
><svg width={g.w} height={g.h} viewBox="0 0 {g.w} {g.h}"
|
|
205
|
+
><polyline
|
|
206
|
+
points={g.poly}
|
|
207
|
+
fill="none"
|
|
208
|
+
stroke="var(--c-blue)"
|
|
209
|
+
stroke-width="1.5"
|
|
210
|
+
/></svg
|
|
211
|
+
></td
|
|
212
|
+
>
|
|
213
|
+
<td class="num"><a href="{base}/report/{last.id}">{fmt_ms(last.median)} ms</a></td>
|
|
214
|
+
<td
|
|
215
|
+
class="num"
|
|
216
|
+
style="color:{d > 5 ? 'var(--bad)' : d < -5 ? 'var(--good)' : 'inherit'}"
|
|
217
|
+
>{d > 0 ? '+' : ''}{d.toFixed(0)}%</td
|
|
218
|
+
>
|
|
219
|
+
<td class="num" title="page score, before → now"
|
|
220
|
+
>{#if last.score !== undefined}{#if before.score !== undefined && before.score !== last.score}<span
|
|
221
|
+
class="hint">{before.score} →</span
|
|
222
|
+
>
|
|
223
|
+
{/if}<b
|
|
224
|
+
style="color:{before.score !== undefined && last.score < before.score
|
|
225
|
+
? 'var(--bad)'
|
|
226
|
+
: before.score !== undefined && last.score > before.score
|
|
227
|
+
? 'var(--good)'
|
|
228
|
+
: 'inherit'}">{last.score}</b
|
|
229
|
+
>{/if}</td
|
|
230
|
+
>
|
|
231
|
+
<td><a href="{base}/compare/{before.id}/{last.id}">compare</a></td>
|
|
232
|
+
</tr>
|
|
233
|
+
{/each}
|
|
234
|
+
</tbody>
|
|
235
|
+
</table>
|
|
236
|
+
</section>
|
|
237
|
+
{/if}
|
|
238
|
+
|
|
239
|
+
{#if site_fixes?.length}
|
|
240
|
+
<!-- FIX ONCE, FASTER EVERYWHERE: lines the latest reports of two or more pages share -->
|
|
241
|
+
<section class="panel wide">
|
|
242
|
+
<h2>Fix once, faster on every page</h2>
|
|
243
|
+
<p class="hint">
|
|
244
|
+
Slow lines that show in the latest report of two or more pages. One change to each makes
|
|
245
|
+
all of them faster: what it gives back per render, per page and in all.
|
|
246
|
+
</p>
|
|
247
|
+
<table>
|
|
248
|
+
<thead>
|
|
249
|
+
<tr><th>line</th><th>what</th><th>pages</th><th class="num">in all</th></tr>
|
|
250
|
+
</thead>
|
|
251
|
+
<tbody>
|
|
252
|
+
{#each site_fixes as f (f.kind + f.where + f.line)}
|
|
253
|
+
<tr>
|
|
254
|
+
<td><code title={f.code}>{f.where}:{f.line}</code></td>
|
|
255
|
+
<td>{f.title}</td>
|
|
256
|
+
<td class="pages">
|
|
257
|
+
{#each f.pages as p, i (p.page)}{i ? ' · ' : ''}<a href="{base}/report/{p.report}"
|
|
258
|
+
>{p.page}</a
|
|
259
|
+
> <span class="save">~{fmt_ms(p.ms)} ms</span>{/each}
|
|
260
|
+
</td>
|
|
261
|
+
<td class="num"><b>~{fmt_ms(f.total_ms)} ms</b></td>
|
|
262
|
+
</tr>
|
|
263
|
+
{/each}
|
|
264
|
+
</tbody>
|
|
265
|
+
</table>
|
|
266
|
+
</section>
|
|
267
|
+
{/if}
|
|
268
|
+
|
|
269
|
+
{#if reports.length}
|
|
270
|
+
<section class="panel wide">
|
|
271
|
+
<h2>Reports</h2>
|
|
272
|
+
<table>
|
|
273
|
+
<thead>
|
|
274
|
+
<tr
|
|
275
|
+
><th>report</th><th>when</th><th class="num">window</th><th class="num">requests</th
|
|
276
|
+
><th>biggest fix</th><th><span class="sr-only">actions</span></th></tr
|
|
277
|
+
>
|
|
278
|
+
</thead>
|
|
279
|
+
<tbody>
|
|
280
|
+
{#each reports as r (r.id)}
|
|
281
|
+
<tr>
|
|
282
|
+
<td><a href="{base}/report/{r.id}">{label_of(r)}</a></td>
|
|
283
|
+
<td>{time(r.created)}</td>
|
|
284
|
+
<td class="num">{fmt_ms(r.duration_ms)} ms</td>
|
|
285
|
+
<td class="num">{r.requests.length}</td>
|
|
286
|
+
<td class="fix">
|
|
287
|
+
{#if top_fix?.[r.id]}
|
|
288
|
+
{@const t = top_fix[r.id]}
|
|
289
|
+
<a href="{base}/report/{r.id}#pattern-0">{t.title}</a>
|
|
290
|
+
{#if t.save_ms >= 0.1}<span class="save"
|
|
291
|
+
>~{fmt_ms(t.save_ms)} ms{t.wait ? ' of waiting' : ''}</span
|
|
292
|
+
>{/if}{#if t.more}<span class="more"> · {t.more} more</span>{/if}
|
|
293
|
+
{:else}—{/if}
|
|
294
|
+
</td>
|
|
295
|
+
<td
|
|
296
|
+
>{#if prev_of.has(r.id)}<a href="{base}/compare/{prev_of.get(r.id)}/{r.id}"
|
|
297
|
+
>compare with previous</a
|
|
298
|
+
>{/if}</td
|
|
299
|
+
>
|
|
300
|
+
</tr>
|
|
301
|
+
{/each}
|
|
302
|
+
</tbody>
|
|
303
|
+
</table>
|
|
304
|
+
</section>
|
|
305
|
+
{/if}
|
|
306
|
+
|
|
307
|
+
{#if holes}
|
|
308
|
+
<section class="panel wide" data-hole-slots>
|
|
309
|
+
<h2>
|
|
310
|
+
Holes on this server <span class="hint" style="font-weight:400"
|
|
311
|
+
>({holes.rendered} rendered, {holes.slots} render slots per process)</span
|
|
312
|
+
>
|
|
313
|
+
</h2>
|
|
314
|
+
{#if holes.summary}
|
|
315
|
+
<p class="warn">{holes.summary}</p>
|
|
316
|
+
{:else}
|
|
317
|
+
<p class="hint">
|
|
318
|
+
No hole waited for a render slot. Each render holds one of the {holes.slots} slots through
|
|
319
|
+
its awaits, so slow holes would make the next ones wait.
|
|
320
|
+
</p>
|
|
321
|
+
{/if}
|
|
322
|
+
<table>
|
|
323
|
+
<thead>
|
|
324
|
+
<tr
|
|
325
|
+
><th>hole</th><th class="num">requests</th><th class="num">cache hits</th><th
|
|
326
|
+
class="num">failed</th
|
|
327
|
+
><th class="num">render ms</th><th
|
|
328
|
+
class="num"
|
|
329
|
+
title="mean wait for a render slot per rendered request (worst in brackets)"
|
|
330
|
+
>slot wait ms</th
|
|
331
|
+
><th class="num" title="its part of all the time the render slots were held"
|
|
332
|
+
>slot time</th
|
|
333
|
+
></tr
|
|
334
|
+
>
|
|
335
|
+
</thead>
|
|
336
|
+
<tbody>
|
|
337
|
+
{#each holes.rows as h (h.id)}
|
|
338
|
+
<tr>
|
|
339
|
+
<td class="fn">{h.name}</td>
|
|
340
|
+
<td class="num">{h.requests}</td>
|
|
341
|
+
<td class="num">{h.hits}</td>
|
|
342
|
+
<td class="num">{h.failed}</td>
|
|
343
|
+
<td class="num">{h.requests - h.hits - h.failed ? fmt_ms(h.render_ms) : '—'}</td>
|
|
344
|
+
<td class="num"
|
|
345
|
+
>{h.queue_ms ? fmt_ms(h.queue_ms) : '0'}{#if h.queue_max_ms >= 50}
|
|
346
|
+
<span class="hint">({fmt_ms(h.queue_max_ms)})</span>{/if}</td
|
|
347
|
+
>
|
|
348
|
+
<td class="num">{Math.round(h.slot_share * 100)}%</td>
|
|
349
|
+
</tr>
|
|
350
|
+
{/each}
|
|
351
|
+
</tbody>
|
|
352
|
+
</table>
|
|
353
|
+
</section>
|
|
354
|
+
{/if}
|
|
355
|
+
|
|
356
|
+
<section class="panel wide">
|
|
357
|
+
<h2>Slowest routes</h2>
|
|
358
|
+
<p class="hint">
|
|
359
|
+
Wall-clock per request since server start. p95 is the slow tail. "net p50" is time inside
|
|
360
|
+
outbound calls — when it tracks the total, the route is waiting on other services, not
|
|
361
|
+
computing.
|
|
362
|
+
</p>
|
|
363
|
+
{#if tag_keys.length}
|
|
364
|
+
<p class="hint">
|
|
365
|
+
Split by tag (<code>tag()</code> from @ogygia/devtools/profiler):
|
|
366
|
+
<a href={base} class:active={!by}>none</a>
|
|
367
|
+
{#each tag_keys as k (k)}· <a
|
|
368
|
+
href="{base}?by={encodeURIComponent(k)}"
|
|
369
|
+
class:active={by === k}>{k}</a
|
|
370
|
+
>{/each}
|
|
371
|
+
</p>
|
|
372
|
+
{/if}
|
|
373
|
+
{#if top_routes.length}
|
|
374
|
+
<table>
|
|
375
|
+
<thead>
|
|
376
|
+
<tr
|
|
377
|
+
><th>route</th><th class="num">hits</th><th class="num">p50 ms</th><th class="num"
|
|
378
|
+
>p95 ms</th
|
|
379
|
+
><th class="num">max ms</th><th class="num">net p50</th></tr
|
|
380
|
+
>
|
|
381
|
+
</thead>
|
|
382
|
+
<tbody>
|
|
383
|
+
{#each top_routes as r (r.route)}
|
|
384
|
+
<tr>
|
|
385
|
+
<td class="fn">{r.route}</td>
|
|
386
|
+
<td class="num">{r.count}</td>
|
|
387
|
+
<td class="num">{fmt_ms(r.p50)}</td>
|
|
388
|
+
<td class="num">{fmt_ms(r.p95)}</td>
|
|
389
|
+
<td class="num">{fmt_ms(r.max)}</td>
|
|
390
|
+
<td class="num">{fmt_ms(r.net_p50)}</td>
|
|
391
|
+
</tr>
|
|
392
|
+
{/each}
|
|
393
|
+
</tbody>
|
|
394
|
+
</table>
|
|
395
|
+
{:else}
|
|
396
|
+
<p class="hint">No requests seen yet — load some pages, then refresh.</p>
|
|
397
|
+
{/if}
|
|
398
|
+
</section>
|
|
399
|
+
|
|
400
|
+
<section class="panel wide">
|
|
401
|
+
<h2>Recent requests</h2>
|
|
402
|
+
{#if recent_desc.length}
|
|
403
|
+
<table>
|
|
404
|
+
<thead>
|
|
405
|
+
<tr
|
|
406
|
+
><th>when</th><th>method</th><th>path</th><th>route</th>{#if tag_keys.length}<th
|
|
407
|
+
>tags</th
|
|
408
|
+
>{/if}<th class="num">status</th><th class="num">net ms</th><th class="num"
|
|
409
|
+
>total ms</th
|
|
410
|
+
></tr
|
|
411
|
+
>
|
|
412
|
+
</thead>
|
|
413
|
+
<tbody>
|
|
414
|
+
{#each recent_desc as e (e.ts + e.path)}
|
|
415
|
+
<tr>
|
|
416
|
+
<td>{time(e.ts)}</td>
|
|
417
|
+
<td>{e.method}</td>
|
|
418
|
+
<td class="fn"
|
|
419
|
+
>{e.path}{#if e.internal}<span class="warn"> (profiler)</span>{/if}</td
|
|
420
|
+
>
|
|
421
|
+
<td class="file">{e.route ?? '—'}</td>
|
|
422
|
+
{#if tag_keys.length}<td class="file"
|
|
423
|
+
>{e.tags
|
|
424
|
+
? Object.entries(e.tags)
|
|
425
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
426
|
+
.join(' ')
|
|
427
|
+
: '—'}</td
|
|
428
|
+
>{/if}
|
|
429
|
+
<td class="num">{e.status || '—'}</td>
|
|
430
|
+
<td class="num"
|
|
431
|
+
>{#if e.net_count}{fmt_ms(e.net_ms)}
|
|
432
|
+
<span class="hint">({e.net_count})</span>{:else}—{/if}</td
|
|
433
|
+
>
|
|
434
|
+
<td class="num"><b>{fmt_ms(e.ms)}</b></td>
|
|
435
|
+
</tr>
|
|
436
|
+
{/each}
|
|
437
|
+
</tbody>
|
|
438
|
+
</table>
|
|
439
|
+
{:else}
|
|
440
|
+
<p class="hint">Nothing yet.</p>
|
|
441
|
+
{/if}
|
|
442
|
+
</section>
|
|
443
|
+
</div>
|
|
444
|
+
</Shell>
|
|
445
|
+
|
|
446
|
+
<style>
|
|
447
|
+
td.fix {
|
|
448
|
+
font-size: 12.5px;
|
|
449
|
+
max-width: 360px;
|
|
450
|
+
}
|
|
451
|
+
td.fix .save {
|
|
452
|
+
color: var(--c-orange);
|
|
453
|
+
font-weight: 600;
|
|
454
|
+
margin-left: 6px;
|
|
455
|
+
white-space: nowrap;
|
|
456
|
+
}
|
|
457
|
+
td.fix .more {
|
|
458
|
+
color: var(--text-faint);
|
|
459
|
+
}
|
|
460
|
+
.head {
|
|
461
|
+
display: flex;
|
|
462
|
+
align-items: flex-start;
|
|
463
|
+
justify-content: space-between;
|
|
464
|
+
gap: 16px;
|
|
465
|
+
flex-wrap: wrap;
|
|
466
|
+
}
|
|
467
|
+
.head .actions {
|
|
468
|
+
margin-top: 4px;
|
|
469
|
+
}
|
|
470
|
+
.ogp-opt {
|
|
471
|
+
display: inline-flex;
|
|
472
|
+
align-items: center;
|
|
473
|
+
gap: 5px;
|
|
474
|
+
font-size: 12.5px;
|
|
475
|
+
color: var(--text-dim);
|
|
476
|
+
}
|
|
477
|
+
.bar-cell {
|
|
478
|
+
position: relative;
|
|
479
|
+
min-width: 120px;
|
|
480
|
+
}
|
|
481
|
+
.bar-cell .bar {
|
|
482
|
+
position: absolute;
|
|
483
|
+
left: 4px;
|
|
484
|
+
right: 4px;
|
|
485
|
+
bottom: 3px;
|
|
486
|
+
height: 3px;
|
|
487
|
+
max-width: calc(100% - 8px);
|
|
488
|
+
background: var(--accent);
|
|
489
|
+
border-radius: 2px;
|
|
490
|
+
opacity: 0.7;
|
|
491
|
+
}
|
|
492
|
+
</style>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
declare const Dashboard: import("svelte").Component<{
|
|
2
|
+
data: {
|
|
3
|
+
base: string;
|
|
4
|
+
recent: import("../report.js").RequestEntry[];
|
|
5
|
+
routes: import("../report.js").RouteAgg[];
|
|
6
|
+
by: string | null;
|
|
7
|
+
tag_keys: string[];
|
|
8
|
+
reports: import("../report.js").ReportMeta[];
|
|
9
|
+
top_fix: Record<string, {
|
|
10
|
+
title: string;
|
|
11
|
+
save_ms: number;
|
|
12
|
+
wait: boolean;
|
|
13
|
+
more: number;
|
|
14
|
+
}>;
|
|
15
|
+
site_fixes: import("../site-fixes.js").SiteFix[];
|
|
16
|
+
holes: import("../hole-slots.js").HoleSlots | null;
|
|
17
|
+
recording: boolean;
|
|
18
|
+
dev: boolean;
|
|
19
|
+
rss_mb: number;
|
|
20
|
+
inflight: number;
|
|
21
|
+
history: import("../profiler-router.js").PageHistory[];
|
|
22
|
+
trap: import("../index.js").TrapStatus | null;
|
|
23
|
+
sampled: import("../index.js").SampledSummary | null;
|
|
24
|
+
background_note: string | null;
|
|
25
|
+
};
|
|
26
|
+
form: null;
|
|
27
|
+
params: {};
|
|
28
|
+
search: {};
|
|
29
|
+
}, {}, "">;
|
|
30
|
+
type Dashboard = ReturnType<typeof Dashboard>;
|
|
31
|
+
export default Dashboard;
|