diff --git a/README.md b/README.md index 7e3a9d3..7bec569 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ the same row, always labeled: Requirements: Linux, Python 3.9+, a `/proc` filesystem. No dependencies, no root, no agent installed inside your apps. +Want to see it without pointing it at your machine? `python3 server.py --demo` +serves a canned snapshot at the same URL, which is how the screenshot above was +made. + ```bash git clone https://forge.binaryranch.com/sastian/vitals.git cd vitals diff --git a/demo/demo-snapshot.json b/demo/demo-snapshot.json new file mode 100644 index 0000000..6c9eb76 --- /dev/null +++ b/demo/demo-snapshot.json @@ -0,0 +1,1460 @@ +{ + "snapshot": { + "t": 0, + "cores": 12, + "host": "workstation", + "total_busy": 11.42, + "total_pct": 95, + "workloads": [ + { + "key": "headless", + "name": "Headless browser automation", + "cpu": 6.1, + "pct": 50.8, + "rss_mb": 5024, + "procs": 90, + "age_h": 170.5, + "instances": 10, + "roles": { + "gpu": 4.76, + "renderer": 1.31, + "headless browser": 0.03 + }, + "flags": [ + "leaked" + ], + "tabs": [ + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9551", + "instance": "scoped_dir01", + "instance_age": 170.5, + "proc_cores": 0.49 + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9552", + "instance": "scoped_dir02", + "instance_age": 170.5, + "proc_cores": 0.51 + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9553", + "instance": "scoped_dir03", + "instance_age": 170.5, + "proc_cores": 0.54 + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9554", + "instance": "scoped_dir04", + "instance_age": 170.5, + "proc_cores": 0.57 + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9555", + "instance": "scoped_dir05", + "instance_age": 170.5, + "proc_cores": 0.6 + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9556", + "instance": "scoped_dir06", + "instance_age": 170.5, + "proc_cores": 0.62 + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9557", + "instance": "scoped_dir07", + "instance_age": 170.5, + "proc_cores": 0.65 + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9558", + "instance": "scoped_dir08", + "instance_age": 170.5, + "proc_cores": 0.68 + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9559", + "instance": "scoped_dir09", + "instance_age": 170.5, + "proc_cores": 0.71 + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9560", + "instance": "scoped_dir10", + "instance_age": 170.5, + "proc_cores": 0.73 + } + ], + "verdict": "pages are idle (0.02 cores of page work) \u2014 6.92 cores burned by browser internals (compositor / raster / media), not by a tab", + "instances_detail": [ + { + "instance": "scoped_dir01", + "cpu": 0.49, + "port": "9551", + "tab": "Staging site", + "url": "https://staging.example.com/" + }, + { + "instance": "scoped_dir02", + "cpu": 0.51, + "port": "9552", + "tab": "Staging site", + "url": "https://staging.example.com/" + }, + { + "instance": "scoped_dir03", + "cpu": 0.54, + "port": "9553", + "tab": "Staging site", + "url": "https://staging.example.com/" + }, + { + "instance": "scoped_dir04", + "cpu": 0.57, + "port": "9554", + "tab": "Staging site", + "url": "https://staging.example.com/" + }, + { + "instance": "scoped_dir05", + "cpu": 0.6, + "port": "9555", + "tab": "Staging site", + "url": "https://staging.example.com/" + }, + { + "instance": "scoped_dir06", + "cpu": 0.62, + "port": "9556", + "tab": "Staging site", + "url": "https://staging.example.com/" + }, + { + "instance": "scoped_dir07", + "cpu": 0.65, + "port": "9557", + "tab": "Staging site", + "url": "https://staging.example.com/" + }, + { + "instance": "scoped_dir08", + "cpu": 0.68, + "port": "9558", + "tab": "Staging site", + "url": "https://staging.example.com/" + }, + { + "instance": "scoped_dir09", + "cpu": 0.71, + "port": "9559", + "tab": "Staging site", + "url": "https://staging.example.com/" + }, + { + "instance": "scoped_dir10", + "cpu": 0.73, + "port": "9560", + "tab": "Staging site", + "url": "https://staging.example.com/" + } + ], + "has_cdp": true, + "series": [ + 5.86, + 5.69, + 6.18, + 5.62, + 6.07, + 5.9, + 5.6, + 6.04, + 5.59, + 5.97, + 5.61, + 5.64, + 5.96, + 6.34, + 5.67, + 5.76, + 6.15, + 6.46, + 6.11, + 5.93, + 6.49, + 5.6, + 6.38, + 5.83, + 5.68, + 5.67, + 5.85, + 6.33, + 5.73, + 6.11, + 6.16, + 5.91, + 6.07, + 5.6, + 5.6, + 5.74, + 6.2, + 5.96, + 5.85, + 6.12, + 5.99, + 5.84, + 6.32, + 6.22, + 5.79, + 6.1, + 6.06, + 6.4, + 6.25, + 5.82, + 6.49, + 5.67, + 5.95, + 6.28, + 5.69, + 6.02, + 5.59, + 6.2, + 6.28, + 6.1 + ] + }, + { + "key": "app:com.brave.Browser", + "name": "Brave", + "cpu": 2.95, + "pct": 24.6, + "rss_mb": 10860, + "procs": 52, + "age_h": 0.0, + "instances": 0, + "roles": { + "call (WebRTC)": 2.12, + "gpu": 0.4, + "tab renderer": 0.3, + "main": 0.13 + }, + "flags": [ + "on a call" + ], + "tabs": [ + { + "id": "video", + "title": "Video call \u2014 3 participants", + "url": "https://talk.example.com/call", + "task_pct": 221.0, + "script_share": 40, + "heap_mb": 812, + "nodes": 4200, + "port": "9222", + "instance": "9222", + "instance_age": 0.4, + "proc_cores": 3.1 + }, + { + "id": "q3for", + "title": "Q3 forecast \u2014 Sheets", + "url": "https://sheets.example.com/d/1q3", + "task_pct": 9.4, + "script_share": 40, + "heap_mb": 268, + "nodes": 4200, + "port": "9222", + "instance": "9222", + "instance_age": 0.4, + "proc_cores": 3.1 + }, + { + "id": "produc", + "title": "Product docs \u2014 draft", + "url": "https://docs.example.com/draft", + "task_pct": 3.1, + "script_share": 40, + "heap_mb": 121, + "nodes": 4200, + "port": "9222", + "instance": "9222", + "instance_age": 5.2, + "proc_cores": 3.1 + }, + { + "id": "news(", + "title": "News (background)", + "url": "https://news.example.com/", + "task_pct": 0.8, + "script_share": 40, + "heap_mb": 96, + "nodes": 4200, + "port": "9222", + "instance": "9222", + "instance_age": 26.0, + "proc_cores": 3.1 + } + ], + "verdict": "page work: 2.35 cores across 4 tabs", + "instances_detail": [], + "has_cdp": true, + "series": [ + 2.53, + 2.33, + 2.48, + 2.45, + 2.45, + 2.41, + 2.56, + 2.61, + 2.44, + 2.52, + 2.3, + 2.54, + 2.54, + 2.67, + 2.62, + 2.42, + 2.46, + 2.58, + 2.34, + 2.51, + 2.41, + 2.4, + 2.39, + 2.66, + 2.43, + 2.48, + 2.54, + 2.73, + 2.44, + 2.59, + 2.63, + 2.77, + 2.75, + 2.78, + 2.56, + 2.62, + 2.6, + 2.81, + 2.85, + 2.55, + 2.57, + 2.6, + 2.6, + 2.71, + 2.75, + 2.64, + 2.55, + 2.72, + 2.7, + 2.78, + 2.94, + 2.85, + 2.79, + 2.84, + 2.87, + 2.63, + 2.97, + 2.93, + 2.97, + 2.95 + ] + }, + { + "key": "docker:postgres", + "name": "postgres \u00b7 container", + "cpu": 0.62, + "pct": 5.2, + "rss_mb": 412, + "procs": 9, + "age_h": 340.2, + "instances": 0, + "roles": { + "container": 0.62 + }, + "flags": [], + "tabs": [], + "verdict": "", + "instances_detail": [], + "has_cdp": false, + "series": [ + 0.59, + 0.59, + 0.56, + 0.61, + 0.56, + 0.56, + 0.57, + 0.57, + 0.59, + 0.56, + 0.56, + 0.57, + 0.57, + 0.59, + 0.56, + 0.65, + 0.62, + 0.58, + 0.59, + 0.6, + 0.61, + 0.58, + 0.65, + 0.67, + 0.62, + 0.62, + 0.59, + 0.59, + 0.61, + 0.61, + 0.66, + 0.6, + 0.59, + 0.68, + 0.63, + 0.6, + 0.64, + 0.59, + 0.64, + 0.68, + 0.68, + 0.66, + 0.62, + 0.63, + 0.61, + 0.67, + 0.65, + 0.68, + 0.63, + 0.62, + 0.68, + 0.7, + 0.69, + 0.68, + 0.69, + 0.68, + 0.63, + 0.66, + 0.65, + 0.62 + ] + }, + { + "key": "agents:research", + "name": "Hermes agent \u00b7 research", + "cpu": 0.58, + "pct": 4.8, + "rss_mb": 2210, + "procs": 13, + "age_h": 0.3, + "instances": 0, + "roles": { + "agent": 0.58 + }, + "flags": [], + "tabs": [], + "verdict": "", + "instances_detail": [], + "has_cdp": false, + "series": [ + 0.42, + 0.43, + 0.43, + 0.47, + 0.49, + 0.45, + 0.49, + 0.5, + 0.5, + 0.46, + 0.45, + 0.45, + 0.45, + 0.45, + 0.49, + 0.51, + 0.5, + 0.49, + 0.5, + 0.51, + 0.46, + 0.5, + 0.52, + 0.52, + 0.52, + 0.5, + 0.48, + 0.52, + 0.5, + 0.53, + 0.55, + 0.5, + 0.51, + 0.55, + 0.54, + 0.5, + 0.5, + 0.5, + 0.55, + 0.55, + 0.51, + 0.56, + 0.57, + 0.55, + 0.53, + 0.55, + 0.52, + 0.51, + 0.59, + 0.56, + 0.55, + 0.59, + 0.55, + 0.59, + 0.59, + 0.54, + 0.55, + 0.55, + 0.55, + 0.58 + ] + }, + { + "key": "app:com.slack.Slack", + "name": "Slack", + "cpu": 0.36, + "pct": 3.0, + "rss_mb": 1980, + "procs": 21, + "age_h": 12.4, + "instances": 0, + "roles": { + "main": 0.16, + "tab renderer": 0.2 + }, + "flags": [], + "tabs": [], + "verdict": "", + "instances_detail": [], + "has_cdp": false, + "series": [ + 0.32, + 0.33, + 0.31, + 0.36, + 0.33, + 0.34, + 0.34, + 0.36, + 0.33, + 0.36, + 0.34, + 0.34, + 0.34, + 0.31, + 0.34, + 0.32, + 0.31, + 0.35, + 0.32, + 0.34, + 0.35, + 0.34, + 0.33, + 0.34, + 0.34, + 0.35, + 0.31, + 0.34, + 0.32, + 0.32, + 0.35, + 0.34, + 0.34, + 0.35, + 0.36, + 0.34, + 0.34, + 0.34, + 0.34, + 0.34, + 0.34, + 0.34, + 0.34, + 0.36, + 0.34, + 0.36, + 0.36, + 0.32, + 0.34, + 0.36, + 0.35, + 0.31, + 0.31, + 0.34, + 0.31, + 0.32, + 0.31, + 0.34, + 0.35, + 0.36 + ] + }, + { + "key": "cmd:cinnamon", + "name": "Cinnamon desktop", + "cpu": 0.31, + "pct": 2.6, + "rss_mb": 381, + "procs": 1, + "age_h": 415.0, + "instances": 0, + "roles": { + "process": 0.31 + }, + "flags": [], + "tabs": [], + "verdict": "", + "instances_detail": [], + "has_cdp": false, + "series": [ + 0.27, + 0.29, + 0.29, + 0.27, + 0.3, + 0.31, + 0.27, + 0.31, + 0.28, + 0.29, + 0.31, + 0.3, + 0.27, + 0.29, + 0.29, + 0.28, + 0.27, + 0.28, + 0.29, + 0.26, + 0.29, + 0.29, + 0.26, + 0.28, + 0.29, + 0.29, + 0.26, + 0.31, + 0.3, + 0.31, + 0.27, + 0.28, + 0.26, + 0.3, + 0.28, + 0.27, + 0.29, + 0.3, + 0.3, + 0.28, + 0.27, + 0.3, + 0.29, + 0.29, + 0.27, + 0.26, + 0.29, + 0.29, + 0.26, + 0.31, + 0.29, + 0.3, + 0.27, + 0.3, + 0.26, + 0.3, + 0.29, + 0.28, + 0.29, + 0.31 + ] + }, + { + "key": "unit:backup.service", + "name": "Nightly backup", + "cpu": 0.24, + "pct": 2.0, + "rss_mb": 88, + "procs": 3, + "age_h": 6.1, + "instances": 0, + "roles": { + "service": 0.24 + }, + "flags": [], + "tabs": [], + "verdict": "", + "instances_detail": [], + "has_cdp": false, + "series": [ + 0.22, + 0.21, + 0.22, + 0.22, + 0.21, + 0.21, + 0.21, + 0.22, + 0.22, + 0.22, + 0.23, + 0.22, + 0.22, + 0.22, + 0.22, + 0.21, + 0.22, + 0.21, + 0.23, + 0.22, + 0.22, + 0.22, + 0.24, + 0.21, + 0.23, + 0.22, + 0.22, + 0.24, + 0.22, + 0.22, + 0.23, + 0.24, + 0.22, + 0.24, + 0.23, + 0.23, + 0.22, + 0.22, + 0.21, + 0.21, + 0.21, + 0.23, + 0.22, + 0.21, + 0.21, + 0.24, + 0.24, + 0.23, + 0.22, + 0.22, + 0.22, + 0.22, + 0.21, + 0.22, + 0.22, + 0.24, + 0.24, + 0.22, + 0.22, + 0.24 + ] + }, + { + "key": "cmd:nextcloud", + "name": "Nextcloud desktop", + "cpu": 0.18, + "pct": 1.5, + "rss_mb": 219, + "procs": 1, + "age_h": 415.0, + "instances": 0, + "roles": { + "process": 0.18 + }, + "flags": [], + "tabs": [], + "verdict": "", + "instances_detail": [], + "has_cdp": false, + "series": [ + 0.17, + 0.18, + 0.16, + 0.18, + 0.18, + 0.18, + 0.17, + 0.18, + 0.16, + 0.17, + 0.17, + 0.18, + 0.16, + 0.16, + 0.17, + 0.17, + 0.18, + 0.18, + 0.19, + 0.19, + 0.19, + 0.19, + 0.18, + 0.17, + 0.2, + 0.17, + 0.19, + 0.18, + 0.16, + 0.19, + 0.19, + 0.18, + 0.19, + 0.19, + 0.17, + 0.18, + 0.18, + 0.19, + 0.19, + 0.19, + 0.18, + 0.19, + 0.19, + 0.19, + 0.17, + 0.16, + 0.17, + 0.18, + 0.17, + 0.19, + 0.18, + 0.18, + 0.18, + 0.19, + 0.18, + 0.16, + 0.19, + 0.19, + 0.18, + 0.18 + ] + }, + { + "key": "kernel", + "name": "Kernel", + "cpu": 0.08, + "pct": 0.7, + "rss_mb": 0, + "procs": 80, + "age_h": 415.0, + "instances": 0, + "roles": { + "kernel": 0.08 + }, + "flags": [], + "tabs": [], + "verdict": "", + "instances_detail": [], + "has_cdp": false, + "series": [ + 0.09, + 0.08, + 0.09, + 0.09, + 0.08, + 0.09, + 0.09, + 0.09, + 0.09, + 0.1, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.08, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.08, + 0.08, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.1, + 0.09, + 0.1, + 0.1, + 0.08, + 0.09, + 0.09, + 0.1, + 0.09, + 0.09, + 0.09, + 0.1, + 0.09, + 0.09, + 0.09, + 0.09, + 0.1, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.09, + 0.1, + 0.09, + 0.08 + ] + } + ], + "flags": [ + { + "key": "headless", + "name": "Headless browser automation", + "flag": "leaked", + "cpu": 6.1 + }, + { + "key": "app:com.brave.Browser", + "name": "Brave", + "flag": "on a call", + "cpu": 2.95 + } + ], + "tabs": [ + { + "id": "video", + "title": "Video call \u2014 3 participants", + "url": "https://talk.example.com/call", + "task_pct": 221.0, + "script_share": 40, + "heap_mb": 812, + "nodes": 4200, + "port": "9222", + "instance": "9222", + "instance_age": 0.4, + "proc_cores": 3.1, + "browser": "Brave", + "key": "app:com.brave.Browser" + }, + { + "id": "q3for", + "title": "Q3 forecast \u2014 Sheets", + "url": "https://sheets.example.com/d/1q3", + "task_pct": 9.4, + "script_share": 40, + "heap_mb": 268, + "nodes": 4200, + "port": "9222", + "instance": "9222", + "instance_age": 0.4, + "proc_cores": 3.1, + "browser": "Brave", + "key": "app:com.brave.Browser" + }, + { + "id": "produc", + "title": "Product docs \u2014 draft", + "url": "https://docs.example.com/draft", + "task_pct": 3.1, + "script_share": 40, + "heap_mb": 121, + "nodes": 4200, + "port": "9222", + "instance": "9222", + "instance_age": 5.2, + "proc_cores": 3.1, + "browser": "Brave", + "key": "app:com.brave.Browser" + }, + { + "id": "news(", + "title": "News (background)", + "url": "https://news.example.com/", + "task_pct": 0.8, + "script_share": 40, + "heap_mb": 96, + "nodes": 4200, + "port": "9222", + "instance": "9222", + "instance_age": 26.0, + "proc_cores": 3.1, + "browser": "Brave", + "key": "app:com.brave.Browser" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9551", + "instance": "scoped_dir01", + "instance_age": 170.5, + "proc_cores": 0.49, + "browser": "Headless browser automation", + "key": "headless" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9552", + "instance": "scoped_dir02", + "instance_age": 170.5, + "proc_cores": 0.51, + "browser": "Headless browser automation", + "key": "headless" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9553", + "instance": "scoped_dir03", + "instance_age": 170.5, + "proc_cores": 0.54, + "browser": "Headless browser automation", + "key": "headless" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9554", + "instance": "scoped_dir04", + "instance_age": 170.5, + "proc_cores": 0.57, + "browser": "Headless browser automation", + "key": "headless" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9555", + "instance": "scoped_dir05", + "instance_age": 170.5, + "proc_cores": 0.6, + "browser": "Headless browser automation", + "key": "headless" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9556", + "instance": "scoped_dir06", + "instance_age": 170.5, + "proc_cores": 0.62, + "browser": "Headless browser automation", + "key": "headless" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9557", + "instance": "scoped_dir07", + "instance_age": 170.5, + "proc_cores": 0.65, + "browser": "Headless browser automation", + "key": "headless" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9558", + "instance": "scoped_dir08", + "instance_age": 170.5, + "proc_cores": 0.68, + "browser": "Headless browser automation", + "key": "headless" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9559", + "instance": "scoped_dir09", + "instance_age": 170.5, + "proc_cores": 0.71, + "browser": "Headless browser automation", + "key": "headless" + }, + { + "id": "stagin", + "title": "Staging site \u2014 product page", + "url": "https://staging.example.com/", + "task_pct": 0.2, + "script_share": 40, + "heap_mb": 0.7, + "nodes": 4200, + "port": "9560", + "instance": "scoped_dir10", + "instance_age": 170.5, + "proc_cores": 0.73, + "browser": "Headless browser automation", + "key": "headless" + } + ], + "browsers": [ + { + "port": 9222, + "tabs": 4, + "name": "Brave", + "error": "" + } + ], + "brave_cdp": true, + "self_cost": 0.05, + "sample_ms": 118 + }, + "details": { + "headless": { + "key": "headless", + "series": [ + 5.86, + 5.69, + 6.18, + 5.62, + 6.07, + 5.9, + 5.6, + 6.04, + 5.59, + 5.97, + 5.61, + 5.64, + 5.96, + 6.34, + 5.67, + 5.76, + 6.15, + 6.46, + 6.11, + 5.93, + 6.49, + 5.6, + 6.38, + 5.83, + 5.68, + 5.67, + 5.85, + 6.33, + 5.73, + 6.11, + 6.16, + 5.91, + 6.07, + 5.6, + 5.6, + 5.74, + 6.2, + 5.96, + 5.85, + 6.12, + 5.99, + 5.84, + 6.32, + 6.22, + 5.79, + 6.1, + 6.06, + 6.4, + 6.25, + 5.82, + 6.49, + 5.67, + 5.95, + 6.28, + 5.69, + 6.02, + 5.59, + 6.2, + 6.28, + 6.1 + ], + "processes": [ + { + "pid": 41210, + "role": "gpu", + "rss_mb": 82, + "cpu": 0.71, + "age_h": 170.5, + "cmd": "/usr/lib/chromium/chromium --type=gpu-process --use-gl=disabled", + "cgroup": "/user.slice/user-1000.slice/user@1000.service/app.slice/run-41210.scope", + "windows": [], + "ports": [], + "extra": { + "instance": "scoped_dir01", + "port": "9551" + }, + "threads": [ + { + "tid": "41211", + "name": "VizCompositorTh", + "ticks": 528599 + } + ], + "why": "headless, reparented to PID 1" + }, + { + "pid": 41208, + "role": "headless browser", + "rss_mb": 99, + "cpu": 0.0, + "age_h": 170.5, + "cmd": "/usr/lib/chromium/chromium --headless=new --remote-debugging-port=9551", + "cgroup": "", + "windows": [], + "ports": [ + 9551 + ], + "extra": { + "port": "9551" + }, + "threads": [ + { + "tid": "41209", + "name": "brave", + "ticks": 402 + } + ] + } + ] + }, + "app:com.brave.Browser": { + "key": "app:com.brave.Browser", + "series": [ + 2.53, + 2.33, + 2.48, + 2.45, + 2.45, + 2.41, + 2.56, + 2.61, + 2.44, + 2.52, + 2.3, + 2.54, + 2.54, + 2.67, + 2.62, + 2.42, + 2.46, + 2.58, + 2.34, + 2.51, + 2.41, + 2.4, + 2.39, + 2.66, + 2.43, + 2.48, + 2.54, + 2.73, + 2.44, + 2.59, + 2.63, + 2.77, + 2.75, + 2.78, + 2.56, + 2.62, + 2.6, + 2.81, + 2.85, + 2.55, + 2.57, + 2.6, + 2.6, + 2.71, + 2.75, + 2.64, + 2.55, + 2.72, + 2.7, + 2.78, + 2.94, + 2.85, + 2.79, + 2.84, + 2.87, + 2.63, + 2.97, + 2.93, + 2.97, + 2.95 + ], + "processes": [ + { + "pid": 228353, + "role": "call (WebRTC)", + "rss_mb": 811, + "cpu": 2.21, + "age_h": 168.7, + "cmd": "/app/brave/brave --type=renderer", + "cgroup": "app.slice/app-flatpak-com.brave.Browser-1.scope", + "windows": [ + "Video call \u2014 3 participants" + ], + "ports": [], + "extra": {}, + "threads": [ + { + "tid": "2240151", + "name": "WebRTC_W_and_N", + "ticks": 442288 + }, + { + "tid": "233305", + "name": "ThreadPoolForeg", + "ticks": 122985 + } + ] + }, + { + "pid": 3213897, + "role": "gpu", + "rss_mb": 331, + "cpu": 0.42, + "age_h": 180.2, + "cmd": "/app/brave/brave --type=gpu-process", + "cgroup": "", + "windows": [], + "ports": [], + "extra": {}, + "threads": [ + { + "tid": "3213898", + "name": "VizCompositorTh", + "ticks": 22360 + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/docs/screenshot.png b/docs/screenshot.png index c48d243..51f41a2 100644 Binary files a/docs/screenshot.png and b/docs/screenshot.png differ diff --git a/server.py b/server.py index 31613ec..7d30a29 100644 --- a/server.py +++ b/server.py @@ -602,6 +602,16 @@ class Sampler(threading.Thread): SAMPLER = Sampler() +DEMO = {"data": None} + + +def load_demo(): + path = os.path.join(HERE, "demo", "demo-snapshot.json") + with open(path) as f: + DEMO["data"] = json.load(f) + return DEMO["data"] + + class Handler(BaseHTTPRequestHandler): protocol_version = "HTTP/1.1" @@ -626,12 +636,20 @@ class Handler(BaseHTTPRequestHandler): except OSError: return self._send("index.html missing", "text/plain", 500) if u.path == "/api/snapshot": + if DEMO["data"]: + snap = dict(DEMO["data"]["snapshot"]) + snap["t"] = time.time() + snap["demo"] = True + return self._send(json.dumps(snap)) with SAMPLER.lock: return self._send(json.dumps(SAMPLER.latest)) if u.path == "/api/detail": key = parse_qs(u.query).get("key", [""])[0] if not key: return self._send('{"error":"key required"}', code=400) + if DEMO["data"]: + return self._send(json.dumps( + DEMO["data"]["details"].get(key, {"key": key, "processes": [], "series": []}))) return self._send(json.dumps(SAMPLER.detail(key))) return self._send("not found", "text/plain", 404) @@ -640,8 +658,14 @@ def main(): ap = argparse.ArgumentParser() ap.add_argument("--port", type=int, default=int(os.environ.get("VITALS_PORT", 8791))) ap.add_argument("--host", default="127.0.0.1") + ap.add_argument("--demo", action="store_true", + help="serve the canned demo snapshot (documentation, screenshots, UI work)") args = ap.parse_args() - SAMPLER.start() + if args.demo: + load_demo() + print("Vitals in DEMO mode — canned data, no /proc sampling, no CDP", flush=True) + else: + SAMPLER.start() time.sleep(1.2) try: srv = ThreadingHTTPServer((args.host, args.port), Handler)