ingress-nginx-helm/vendor/github.com/ncabatoff/process-exporter/proc/grouper.go
Manuel de Brito Fontes f7011d22f8 Update godeps
2016-11-29 18:10:06 -03:00

166 lines
4.4 KiB
Go

package proc
import (
common "github.com/ncabatoff/process-exporter"
"time"
)
type (
Grouper struct {
namer common.MatchNamer
trackChildren bool
// track how much was seen last time so we can report the delta
GroupStats map[string]Counts
tracker *Tracker
}
GroupCountMap map[string]GroupCounts
GroupCounts struct {
Counts
Procs int
Memresident uint64
Memvirtual uint64
OldestStartTime time.Time
}
)
func NewGrouper(trackChildren bool, namer common.MatchNamer) *Grouper {
g := Grouper{
trackChildren: trackChildren,
namer: namer,
GroupStats: make(map[string]Counts),
tracker: NewTracker(),
}
return &g
}
func (g *Grouper) checkAncestry(idinfo ProcIdInfo, newprocs map[ProcId]ProcIdInfo) string {
ppid := idinfo.ParentPid
pProcId := g.tracker.ProcIds[ppid]
if pProcId.Pid < 1 {
// Reached root of process tree without finding a tracked parent.
g.tracker.Ignore(idinfo.ProcId)
return ""
}
// Is the parent already known to the tracker?
if ptproc, ok := g.tracker.Tracked[pProcId]; ok {
if ptproc != nil {
// We've found a tracked parent.
g.tracker.Track(ptproc.GroupName, idinfo)
return ptproc.GroupName
} else {
// We've found an untracked parent.
g.tracker.Ignore(idinfo.ProcId)
return ""
}
}
// Is the parent another new process?
if pinfoid, ok := newprocs[pProcId]; ok {
if name := g.checkAncestry(pinfoid, newprocs); name != "" {
// We've found a tracked parent, which implies this entire lineage should be tracked.
g.tracker.Track(name, idinfo)
return name
}
}
// Parent is dead, i.e. we never saw it, or there's no tracked proc in our ancestry.
g.tracker.Ignore(idinfo.ProcId)
return ""
}
// Update tracks any new procs that should be according to policy, and updates
// the metrics for already tracked procs. Permission errors are returned as a
// count, and will not affect the error return value.
func (g *Grouper) Update(iter ProcIter) (int, error) {
newProcs, permErrs, err := g.tracker.Update(iter)
if err != nil {
return permErrs, err
}
// Step 1: track any new proc that should be tracked based on its name and cmdline.
untracked := make(map[ProcId]ProcIdInfo)
for _, idinfo := range newProcs {
wanted, gname := g.namer.MatchAndName(common.NameAndCmdline{idinfo.Name, idinfo.Cmdline})
if !wanted {
untracked[idinfo.ProcId] = idinfo
continue
}
g.tracker.Track(gname, idinfo)
}
// Step 2: track any untracked new proc that should be tracked because its parent is tracked.
if !g.trackChildren {
return permErrs, nil
}
for _, idinfo := range untracked {
if _, ok := g.tracker.Tracked[idinfo.ProcId]; ok {
// Already tracked or ignored
continue
}
g.checkAncestry(idinfo, untracked)
}
return permErrs, nil
}
// groups returns the aggregate metrics for all groups tracked. This reflects
// solely what's currently running.
func (g *Grouper) groups() GroupCountMap {
gcounts := make(GroupCountMap)
var zeroTime time.Time
for _, tinfo := range g.tracker.Tracked {
if tinfo == nil {
continue
}
cur := gcounts[tinfo.GroupName]
cur.Procs++
_, counts, mem, start := tinfo.GetStats()
cur.Memresident += mem.Resident
cur.Memvirtual += mem.Virtual
cur.Counts.Cpu += counts.Cpu
cur.Counts.ReadBytes += counts.ReadBytes
cur.Counts.WriteBytes += counts.WriteBytes
if cur.OldestStartTime == zeroTime || start.Before(cur.OldestStartTime) {
cur.OldestStartTime = start
}
gcounts[tinfo.GroupName] = cur
}
return gcounts
}
// Groups returns GroupCounts with Counts that never decrease in value from one
// call to the next. Even if processes exit, their CPU and IO contributions up
// to that point are included in the results. Even if no processes remain
// in a group it will still be included in the results.
func (g *Grouper) Groups() GroupCountMap {
groups := g.groups()
// First add any accumulated counts to what was just observed,
// and update the accumulators.
for gname, group := range groups {
if oldcounts, ok := g.GroupStats[gname]; ok {
group.Counts.Cpu += oldcounts.Cpu
group.Counts.ReadBytes += oldcounts.ReadBytes
group.Counts.WriteBytes += oldcounts.WriteBytes
}
g.GroupStats[gname] = group.Counts
groups[gname] = group
}
// Now add any groups that were observed in the past but aren't running now.
for gname, gcounts := range g.GroupStats {
if _, ok := groups[gname]; !ok {
groups[gname] = GroupCounts{Counts: gcounts}
}
}
return groups
}