summaryrefslogtreecommitdiff
path: root/internal/flamegraph/testfixture.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-06 13:36:51 +0200
committerPaul Buetow <paul@buetow.org>2026-03-06 13:36:51 +0200
commitef12ce837176bd21deb455eb50a6c839af02b510 (patch)
treec262ceeda0b419236a4b0b1826df8eb5e418b852 /internal/flamegraph/testfixture.go
parent10c5d48413afaef88626419d8c4bf9fbf6f1c902 (diff)
Add live flamegraph test modes and dynamic synthetic live feed
Diffstat (limited to 'internal/flamegraph/testfixture.go')
-rw-r--r--internal/flamegraph/testfixture.go120
1 files changed, 120 insertions, 0 deletions
diff --git a/internal/flamegraph/testfixture.go b/internal/flamegraph/testfixture.go
new file mode 100644
index 0000000..2774925
--- /dev/null
+++ b/internal/flamegraph/testfixture.go
@@ -0,0 +1,120 @@
+package flamegraph
+
+import (
+ "ior/internal/types"
+ "strings"
+)
+
+// SeedTestFlameData populates a deterministic static flamegraph fixture.
+// Intended for keyboard-navigation validation in TUI test-flame mode.
+func SeedTestFlameData(liveTrie *LiveTrie) {
+ if liveTrie == nil {
+ return
+ }
+ for _, record := range testFlameRecords() {
+ liveTrie.AddRecord(record)
+ }
+}
+
+// SeedTestLiveFlameData populates deterministic synthetic data for a given live tick.
+// The data shape stays navigable while branch weights shift by phase so the
+// terminal flamegraph visibly changes over time.
+func SeedTestLiveFlameData(liveTrie *LiveTrie, tick uint64) {
+ if liveTrie == nil {
+ return
+ }
+ phase := tick % 4
+ for _, base := range testFlameRecords() {
+ weight := liveTestWeight(base, phase)
+ liveTrie.AddRecord(withTestFlameWeight(base, weight))
+ }
+}
+
+func testFlameRecords() []IterRecord {
+ return []IterRecord{
+ newTestFlameRecord("api", "/srv/api/lib/http/client/read", 2001, 2201, types.SYS_ENTER_READ, 180),
+ newTestFlameRecord("api", "/srv/api/lib/json/encode/write", 2001, 2201, types.SYS_ENTER_WRITE, 120),
+ newTestFlameRecord("api", "/srv/api/storage/postgres/query/read", 2001, 2201, types.SYS_ENTER_READ, 240),
+ newTestFlameRecord("api", "/srv/api/storage/postgres/commit/fsync", 2001, 2201, types.SYS_ENTER_FSYNC, 70),
+ newTestFlameRecord("worker", "/srv/worker/queue/pop/read", 2002, 2202, types.SYS_ENTER_READ, 160),
+ newTestFlameRecord("worker", "/srv/worker/queue/push/write", 2002, 2202, types.SYS_ENTER_WRITE, 145),
+ newTestFlameRecord("worker", "/srv/worker/cache/redis/get/read", 2002, 2202, types.SYS_ENTER_READ, 95),
+ newTestFlameRecord("worker", "/srv/worker/cache/redis/set/write", 2002, 2202, types.SYS_ENTER_WRITE, 90),
+ newTestFlameRecord("ingest", "/srv/ingest/parser/csv/read", 2003, 2203, types.SYS_ENTER_READ, 110),
+ newTestFlameRecord("ingest", "/srv/ingest/parser/csv/normalize/write", 2003, 2203, types.SYS_ENTER_WRITE, 80),
+ newTestFlameRecord("ingest", "/srv/ingest/uploader/s3/put/writev", 2003, 2203, types.SYS_ENTER_WRITEV, 75),
+ newTestFlameRecord("batch", "/srv/batch/jobs/report/open", 2004, 2204, types.SYS_ENTER_OPENAT, 55),
+ newTestFlameRecord("batch", "/srv/batch/jobs/report/close", 2004, 2204, types.SYS_ENTER_CLOSE, 35),
+ newTestFlameRecord("batch", "/srv/batch/jobs/report/rename", 2004, 2204, types.SYS_ENTER_RENAMEAT, 20),
+ }
+}
+
+func newTestFlameRecord(comm, path string, pid, tid uint32, traceID types.TraceId, weight uint64) IterRecord {
+ return IterRecord{
+ Path: path,
+ TraceID: traceID,
+ Comm: comm,
+ Pid: pid,
+ Tid: tid,
+ Cnt: Counter{
+ Count: weight,
+ Duration: weight * 1000,
+ DurationToPrev: weight * 350,
+ Bytes: weight * 4096,
+ },
+ }
+}
+
+func withTestFlameWeight(record IterRecord, weight uint64) IterRecord {
+ record.Cnt = Counter{
+ Count: weight,
+ Duration: weight * 1000,
+ DurationToPrev: weight * 350,
+ Bytes: weight * 4096,
+ }
+ return record
+}
+
+func liveTestWeight(record IterRecord, phase uint64) uint64 {
+ base := record.Cnt.Count
+ multiplier := uint64(1)
+
+ switch phase {
+ case 0:
+ if record.Comm == "api" {
+ multiplier += 4
+ }
+ if strings.Contains(record.Path, "/lib/") {
+ multiplier += 2
+ }
+ case 1:
+ if record.Comm == "worker" {
+ multiplier += 4
+ }
+ if strings.Contains(record.Path, "/queue/") {
+ multiplier += 2
+ }
+ case 2:
+ if record.Comm == "ingest" {
+ multiplier += 4
+ }
+ if strings.Contains(record.Path, "/uploader/") || strings.Contains(record.Path, "/parser/") {
+ multiplier += 2
+ }
+ case 3:
+ if record.Comm == "batch" {
+ multiplier += 4
+ }
+ if strings.Contains(record.Path, "/report/") {
+ multiplier += 2
+ }
+ }
+
+ if strings.Contains(record.Path, "/storage/") && phase%2 == 0 {
+ multiplier++
+ }
+ if strings.Contains(record.Path, "/cache/") && phase%2 == 1 {
+ multiplier++
+ }
+ return base * multiplier
+}