package statsengine import ( "slices" "time" "ior/internal/types" ) // TrendDirection is the direction of a time-window comparison. type TrendDirection string const ( // TrendStable indicates no meaningful movement between windows. TrendStable TrendDirection = "stable" // TrendRising indicates the most recent window is higher than the previous one. TrendRising TrendDirection = "rising" // TrendFalling indicates the most recent window is lower than the previous one. TrendFalling TrendDirection = "falling" ) // Trend describes movement between two equivalent time windows. type Trend struct { Direction TrendDirection DeltaPercent float64 } // Snapshot is an immutable point-in-time view of all aggregated statistics. type Snapshot struct { GeneratedAt time.Time Elapsed time.Duration TotalSyscalls uint64 TotalErrors uint64 TotalBytes uint64 TotalAddressSpaceBytes uint64 SyscallRatePerSec float64 ErrorRatePerSec float64 AddressSpaceBytesPerSec float64 ReadBytesPerSec float64 WriteBytesPerSec float64 LatencyMeanNs float64 GapMeanNs float64 LatencyTrend Trend GapTrend Trend ThroughputTrend Trend latencySeriesNs []float64 gapSeriesNs []float64 throughputSeriesB []float64 syscalls []SyscallSnapshot families []FamilySnapshot files []FileSnapshot processes []ProcessSnapshot LatencyHistogram HistogramSnapshot GapHistogram HistogramSnapshot } // SyscallSnapshot is the per-syscall view used by the syscall table. type SyscallSnapshot struct { TraceID types.TraceId Name string Count uint64 RatePerSec float64 Errors uint64 Bytes uint64 LatencyMinNs uint64 LatencyMaxNs uint64 LatencyMeanNs float64 TotalLatencyNs uint64 LatencyP50Ns uint64 LatencyP95Ns uint64 LatencyP99Ns uint64 } // FamilySnapshot is an aggregated syscall-family row. type FamilySnapshot struct { Family types.SyscallFamily Name string Count uint64 RatePerSec float64 Errors uint64 Bytes uint64 LatencyMinNs uint64 LatencyMaxNs uint64 LatencyMeanNs float64 TotalLatencyNs uint64 } // FileSnapshot is an aggregated per-file ranking entry. type FileSnapshot struct { Path string Accesses uint64 BytesRead uint64 BytesWritten uint64 AvgLatencyNs float64 MaxLatencyNs uint64 TotalLatencyNs uint64 } // ProcessSnapshot is an aggregated per-process entry. type ProcessSnapshot struct { PID uint32 Comm string Syscalls uint64 RatePerSec float64 Bytes uint64 AvgLatencyNs float64 TotalLatencyNs uint64 } // HistogramBucketSnapshot is one bucket of a histogram snapshot. type HistogramBucketSnapshot struct { Label string LowerNs uint64 UpperNs uint64 Count uint64 } // HistogramSnapshot is an immutable histogram view at snapshot time. type HistogramSnapshot struct { Total uint64 buckets []HistogramBucketSnapshot } // NewSnapshot creates a snapshot while defensively copying all slice-backed // inputs so callers cannot mutate shared snapshot state. func NewSnapshot( latencySeriesNs []float64, gapSeriesNs []float64, throughputSeriesB []float64, syscalls []SyscallSnapshot, files []FileSnapshot, processes []ProcessSnapshot, latencyHistogram HistogramSnapshot, gapHistogram HistogramSnapshot, ) Snapshot { return NewSnapshotWithFamilies( latencySeriesNs, gapSeriesNs, throughputSeriesB, syscalls, nil, files, processes, latencyHistogram, gapHistogram, ) } // NewSnapshotWithFamilies creates a snapshot including family aggregate rows. func NewSnapshotWithFamilies( latencySeriesNs []float64, gapSeriesNs []float64, throughputSeriesB []float64, syscalls []SyscallSnapshot, families []FamilySnapshot, files []FileSnapshot, processes []ProcessSnapshot, latencyHistogram HistogramSnapshot, gapHistogram HistogramSnapshot, ) Snapshot { return Snapshot{ latencySeriesNs: slices.Clone(latencySeriesNs), gapSeriesNs: slices.Clone(gapSeriesNs), throughputSeriesB: slices.Clone(throughputSeriesB), syscalls: slices.Clone(syscalls), families: slices.Clone(families), files: slices.Clone(files), processes: slices.Clone(processes), LatencyHistogram: latencyHistogram.Clone(), GapHistogram: gapHistogram.Clone(), } } // NewHistogramSnapshot creates an immutable histogram snapshot by copying // bucket storage. func NewHistogramSnapshot(total uint64, buckets []HistogramBucketSnapshot) HistogramSnapshot { return HistogramSnapshot{ Total: total, buckets: slices.Clone(buckets), } } // Clone returns a deep copy of the histogram snapshot. func (h HistogramSnapshot) Clone() HistogramSnapshot { return HistogramSnapshot{ Total: h.Total, buckets: slices.Clone(h.buckets), } } // LatencySeriesNs returns latency sparkline samples. // Callers must treat returned data as read-only. func (s Snapshot) LatencySeriesNs() []float64 { return s.latencySeriesNs } // GapSeriesNs returns inter-syscall gap sparkline samples. // Callers must treat returned data as read-only. func (s Snapshot) GapSeriesNs() []float64 { return s.gapSeriesNs } // ThroughputSeriesB returns throughput sparkline samples. // Callers must treat returned data as read-only. func (s Snapshot) ThroughputSeriesB() []float64 { return s.throughputSeriesB } // Syscalls returns per-syscall snapshot rows. // Callers must treat returned data as read-only. func (s Snapshot) Syscalls() []SyscallSnapshot { return s.syscalls } // SyscallsCount returns number of syscall rows without cloning backing slices. func (s Snapshot) SyscallsCount() int { return len(s.syscalls) } // Families returns per-syscall-family snapshot rows. // Callers must treat returned data as read-only. func (s Snapshot) Families() []FamilySnapshot { return s.families } // FamiliesCount returns number of syscall-family rows without cloning backing slices. func (s Snapshot) FamiliesCount() int { return len(s.families) } // TopNSyscalls returns at most n per-syscall rows in ranking order. // Callers must treat returned data as read-only. func (s Snapshot) TopNSyscalls(n int) []SyscallSnapshot { return topN(s.syscalls, n) } // Files returns per-file snapshot rows. // Callers must treat returned data as read-only. func (s Snapshot) Files() []FileSnapshot { return s.files } // FilesCount returns number of file rows without cloning backing slices. func (s Snapshot) FilesCount() int { return len(s.files) } // TopNFiles returns at most n file rows in ranking order. // Callers must treat returned data as read-only. func (s Snapshot) TopNFiles(n int) []FileSnapshot { return topN(s.files, n) } // Processes returns per-process snapshot rows. // Callers must treat returned data as read-only. func (s Snapshot) Processes() []ProcessSnapshot { return s.processes } // ProcessesCount returns number of process rows without cloning backing slices. func (s Snapshot) ProcessesCount() int { return len(s.processes) } // TopNProcesses returns at most n process rows in ranking order. // Callers must treat returned data as read-only. func (s Snapshot) TopNProcesses(n int) []ProcessSnapshot { return topN(s.processes, n) } // Buckets returns histogram buckets. // Callers must treat returned data as read-only. func (h HistogramSnapshot) Buckets() []HistogramBucketSnapshot { return h.buckets } func topN[T any](rows []T, n int) []T { if n <= 0 || len(rows) == 0 { return nil } if n > len(rows) { n = len(rows) } return rows[:n:n] }