ingress-nginx-helm/vendor/github.com/ncabatoff/process-exporter/proc/grouper.go
Manuel Alejandro de Brito Fontes 9278f0cad2
Update metric dependencies (#5023)
2020-02-06 09:50:13 -03:00

179 lines
4.9 KiB
Go

package proc
import (
"time"
seq "github.com/ncabatoff/go-seq/seq"
common "github.com/ncabatoff/process-exporter"
)
type (
// Grouper is the top-level interface to the process metrics. All tracked
// procs sharing the same group name are aggregated.
Grouper struct {
// groupAccum records the historical accumulation of a group so that
// we can avoid ever decreasing the counts we return.
groupAccum map[string]Counts
tracker *Tracker
threadAccum map[string]map[string]Threads
debug bool
}
// GroupByName maps group name to group metrics.
GroupByName map[string]Group
// Threads collects metrics for threads in a group sharing a thread name.
Threads struct {
Name string
NumThreads int
Counts
}
// Group describes the metrics of a single group.
Group struct {
Counts
States
Wchans map[string]int
Procs int
Memory
OldestStartTime time.Time
OpenFDs uint64
WorstFDratio float64
NumThreads uint64
Threads []Threads
}
)
// Returns true if x < y. Test designers should ensure they always have
// a unique name/numthreads combination for each group.
func lessThreads(x, y Threads) bool { return seq.Compare(x, y) < 0 }
// NewGrouper creates a grouper.
func NewGrouper(namer common.MatchNamer, trackChildren, trackThreads, alwaysRecheck, debug bool) *Grouper {
g := Grouper{
groupAccum: make(map[string]Counts),
threadAccum: make(map[string]map[string]Threads),
tracker: NewTracker(namer, trackChildren, trackThreads, alwaysRecheck, debug),
debug: debug,
}
return &g
}
func groupadd(grp Group, ts Update) Group {
var zeroTime time.Time
grp.Procs++
grp.Memory.ResidentBytes += ts.Memory.ResidentBytes
grp.Memory.VirtualBytes += ts.Memory.VirtualBytes
grp.Memory.VmSwapBytes += ts.Memory.VmSwapBytes
if ts.Filedesc.Open != -1 {
grp.OpenFDs += uint64(ts.Filedesc.Open)
}
openratio := float64(ts.Filedesc.Open) / float64(ts.Filedesc.Limit)
if grp.WorstFDratio < openratio {
grp.WorstFDratio = openratio
}
grp.NumThreads += ts.NumThreads
grp.Counts.Add(ts.Latest)
grp.States.Add(ts.States)
if grp.OldestStartTime == zeroTime || ts.Start.Before(grp.OldestStartTime) {
grp.OldestStartTime = ts.Start
}
if grp.Wchans == nil {
grp.Wchans = make(map[string]int)
}
for wchan, count := range ts.Wchans {
grp.Wchans[wchan] += count
}
return grp
}
// Update asks the tracker to report on each tracked process by name.
// These are aggregated by groupname, augmented by accumulated counts
// from the past, and returned. Note that while the Tracker reports
// only what counts have changed since last cycle, Grouper.Update
// returns counts that never decrease. Even once the last process
// with name X disappears, name X will still appear in the results
// with the same counts as before; of course, all non-count metrics
// will be zero.
func (g *Grouper) Update(iter Iter) (CollectErrors, GroupByName, error) {
cerrs, tracked, err := g.tracker.Update(iter)
if err != nil {
return cerrs, nil, err
}
return cerrs, g.groups(tracked), nil
}
// Translate the updates into a new GroupByName and update internal history.
func (g *Grouper) groups(tracked []Update) GroupByName {
groups := make(GroupByName)
threadsByGroup := make(map[string][]ThreadUpdate)
for _, update := range tracked {
groups[update.GroupName] = groupadd(groups[update.GroupName], update)
if update.Threads != nil {
threadsByGroup[update.GroupName] =
append(threadsByGroup[update.GroupName], update.Threads...)
}
}
// Add any accumulated counts to what was just observed,
// and update the accumulators.
for gname, group := range groups {
if oldcounts, ok := g.groupAccum[gname]; ok {
group.Counts.Add(Delta(oldcounts))
}
g.groupAccum[gname] = group.Counts
group.Threads = g.threads(gname, threadsByGroup[gname])
groups[gname] = group
}
// Now add any groups that were observed in the past but aren't running now.
for gname, gcounts := range g.groupAccum {
if _, ok := groups[gname]; !ok {
groups[gname] = Group{Counts: gcounts}
}
}
return groups
}
func (g *Grouper) threads(gname string, tracked []ThreadUpdate) []Threads {
if len(tracked) == 0 {
delete(g.threadAccum, gname)
return nil
}
ret := make([]Threads, 0, len(tracked))
threads := make(map[string]Threads)
// First aggregate the thread metrics by thread name.
for _, nc := range tracked {
curthr := threads[nc.ThreadName]
curthr.NumThreads++
curthr.Counts.Add(nc.Latest)
curthr.Name = nc.ThreadName
threads[nc.ThreadName] = curthr
}
// Add any accumulated counts to what was just observed,
// and update the accumulators.
if history := g.threadAccum[gname]; history != nil {
for tname := range threads {
if oldcounts, ok := history[tname]; ok {
counts := threads[tname]
counts.Add(Delta(oldcounts.Counts))
threads[tname] = counts
}
}
}
g.threadAccum[gname] = threads
for _, thr := range threads {
ret = append(ret, thr)
}
return ret
}