cpu.go - Katyusha's blog
mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
cpu.go
Go Source File · 77 lines
cpu.go
// Package detector 对采样指标做时序异常判定。
//
// 当前实现采用"持续高占用"规则(阈值 + 连续窗口数),保证零幻觉、可解释。
// 后续可在此扩展 EWMA / 3-sigma / Spectral Residual 等检测算法。
package detector
import (
"time"
"github.com/os2026/ebpf-rca/internal/collector"
)
// Signal 表示一次已确认的异常信号。
type Signal struct {
Sample collector.Sample
WindowStart time.Time
WindowEnd time.Time
}
// CPUDetector 检测持续的高 CPU 占用线程。
type CPUDetector struct {
Threshold float64 // CPU 占用阈值(单核占比)
SustainTicks int // 连续超过阈值多少个窗口才触发
counters map[uint32]int
firstSeen map[uint32]time.Time
fired map[uint32]bool
}
// NewCPUDetector 构造检测器。
func NewCPUDetector(threshold float64, sustain int) *CPUDetector {
if sustain < 1 {
sustain = 1
}
return &CPUDetector{
Threshold: threshold,
SustainTicks: sustain,
counters: make(map[uint32]int),
firstSeen: make(map[uint32]time.Time),
fired: make(map[uint32]bool),
}
}
// Detect 处理一个窗口的样本,返回本窗口新触发的异常信号。
func (d *CPUDetector) Detect(samples []collector.Sample, now time.Time) []Signal {
active := make(map[uint32]bool, len(samples))
var signals []Signal
for _, s := range samples {
if s.CPUUtil < d.Threshold {
continue
}
active[s.Pid] = true
if d.counters[s.Pid] == 0 {
d.firstSeen[s.Pid] = now
}
d.counters[s.Pid]++
if d.counters[s.Pid] >= d.SustainTicks && !d.fired[s.Pid] {
d.fired[s.Pid] = true
signals = append(signals, Signal{
Sample: s,
WindowStart: d.firstSeen[s.Pid],
WindowEnd: now,
})
}
}
// 本窗口回落到阈值以下的线程,重置其状态(允许下次重新触发)。
for pid := range d.counters {
if !active[pid] {
delete(d.counters, pid)
delete(d.firstSeen, pid)
delete(d.fired, pid)
}
}
return signals
}

目录