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

306 lines
6.7 KiB
Go

package proc
import (
"fmt"
"github.com/prometheus/procfs"
"time"
)
func newProcIdStatic(pid, ppid int, startTime uint64, name string, cmdline []string) ProcIdStatic {
return ProcIdStatic{ProcId{pid, startTime}, ProcStatic{name, cmdline, ppid, time.Time{}}}
}
type (
// ProcId uniquely identifies a process.
ProcId struct {
// UNIX process id
Pid int
// The time the process started after system boot, the value is expressed
// in clock ticks.
StartTimeRel uint64
}
// ProcStatic contains data read from /proc/pid/*
ProcStatic struct {
Name string
Cmdline []string
ParentPid int
StartTime time.Time
}
// ProcMetrics contains data read from /proc/pid/*
ProcMetrics struct {
CpuTime float64
ReadBytes uint64
WriteBytes uint64
ResidentBytes uint64
VirtualBytes uint64
}
ProcIdStatic struct {
ProcId
ProcStatic
}
ProcInfo struct {
ProcStatic
ProcMetrics
}
ProcIdInfo struct {
ProcId
ProcStatic
ProcMetrics
}
// Proc wraps the details of the underlying procfs-reading library.
Proc interface {
// GetPid() returns the POSIX PID (process id). They may be reused over time.
GetPid() int
// GetProcId() returns (pid,starttime), which can be considered a unique process id.
// It may fail if the caller doesn't have permission to read /proc/<pid>/stat, or if
// the process has disapeared.
GetProcId() (ProcId, error)
// GetStatic() returns various details read from files under /proc/<pid>/. Technically
// name may not be static, but we'll pretend it is.
// It may fail if the caller doesn't have permission to read those files, or if
// the process has disapeared.
GetStatic() (ProcStatic, error)
// GetMetrics() returns various metrics read from files under /proc/<pid>/.
// It may fail if the caller doesn't have permission to read those files, or if
// the process has disapeared.
GetMetrics() (ProcMetrics, error)
}
// proc is a wrapper for procfs.Proc that caches results of some reads and implements Proc.
proc struct {
procfs.Proc
procid *ProcId
stat *procfs.ProcStat
cmdline []string
io *procfs.ProcIO
bootTime int64
}
procs interface {
get(int) Proc
length() int
}
procfsprocs struct {
Procs []procfs.Proc
bootTime int64
}
// ProcIter is an iterator over a sequence of procs.
ProcIter interface {
// Next returns true if the iterator is not exhausted.
Next() bool
// Close releases any resources the iterator uses.
Close() error
// The iterator satisfies the Proc interface.
Proc
}
// procIterator implements the ProcIter interface using procfs.
procIterator struct {
// procs is the list of Proc we're iterating over.
procs
// idx is the current iteration, i.e. it's an index into procs.
idx int
// err is set with an error when Next() fails. It is not affected by failures accessing
// the current iteration variable, e.g. with GetProcId.
err error
// Proc is the current iteration variable, or nil if Next() has never been called or the
// iterator is exhausted.
Proc
}
procIdInfos []ProcIdInfo
)
func procInfoIter(ps ...ProcIdInfo) ProcIter {
return &procIterator{procs: procIdInfos(ps), idx: -1}
}
func Info(p Proc) (ProcIdInfo, error) {
id, err := p.GetProcId()
if err != nil {
return ProcIdInfo{}, err
}
static, err := p.GetStatic()
if err != nil {
return ProcIdInfo{}, err
}
metrics, err := p.GetMetrics()
if err != nil {
return ProcIdInfo{}, err
}
return ProcIdInfo{id, static, metrics}, nil
}
func (p procIdInfos) get(i int) Proc {
return &p[i]
}
func (p procIdInfos) length() int {
return len(p)
}
func (p ProcIdInfo) GetPid() int {
return p.ProcId.Pid
}
func (p ProcIdInfo) GetProcId() (ProcId, error) {
return p.ProcId, nil
}
func (p ProcIdInfo) GetStatic() (ProcStatic, error) {
return p.ProcStatic, nil
}
func (p ProcIdInfo) GetMetrics() (ProcMetrics, error) {
return p.ProcMetrics, nil
}
func (p procfsprocs) get(i int) Proc {
return &proc{Proc: p.Procs[i], bootTime: p.bootTime}
}
func (p procfsprocs) length() int {
return len(p.Procs)
}
func (p *proc) GetPid() int {
return p.Proc.PID
}
func (p *proc) GetStat() (procfs.ProcStat, error) {
if p.stat == nil {
stat, err := p.Proc.NewStat()
if err != nil {
return procfs.ProcStat{}, err
}
p.stat = &stat
}
return *p.stat, nil
}
func (p *proc) GetProcId() (ProcId, error) {
if p.procid == nil {
stat, err := p.GetStat()
if err != nil {
return ProcId{}, err
}
p.procid = &ProcId{Pid: p.GetPid(), StartTimeRel: stat.Starttime}
}
return *p.procid, nil
}
func (p *proc) GetCmdLine() ([]string, error) {
if p.cmdline == nil {
cmdline, err := p.Proc.CmdLine()
if err != nil {
return nil, err
}
p.cmdline = cmdline
}
return p.cmdline, nil
}
func (p *proc) GetIo() (procfs.ProcIO, error) {
if p.io == nil {
io, err := p.Proc.NewIO()
if err != nil {
return procfs.ProcIO{}, err
}
p.io = &io
}
return *p.io, nil
}
func (p proc) GetStatic() (ProcStatic, error) {
cmdline, err := p.GetCmdLine()
if err != nil {
return ProcStatic{}, err
}
stat, err := p.GetStat()
if err != nil {
return ProcStatic{}, err
}
startTime := time.Unix(p.bootTime, 0)
startTime = startTime.Add(time.Second / userHZ * time.Duration(stat.Starttime))
return ProcStatic{
Name: stat.Comm,
Cmdline: cmdline,
ParentPid: stat.PPID,
StartTime: startTime,
}, nil
}
func (p proc) GetMetrics() (ProcMetrics, error) {
io, err := p.GetIo()
if err != nil {
return ProcMetrics{}, err
}
stat, err := p.GetStat()
if err != nil {
return ProcMetrics{}, err
}
return ProcMetrics{
CpuTime: stat.CPUTime(),
ReadBytes: io.ReadBytes,
WriteBytes: io.WriteBytes,
ResidentBytes: uint64(stat.ResidentMemory()),
VirtualBytes: uint64(stat.VirtualMemory()),
}, nil
}
type FS struct {
procfs.FS
BootTime int64
}
// See https://github.com/prometheus/procfs/blob/master/proc_stat.go for details on userHZ.
const userHZ = 100
// NewFS returns a new FS mounted under the given mountPoint. It will error
// if the mount point can't be read.
func NewFS(mountPoint string) (*FS, error) {
fs, err := procfs.NewFS(mountPoint)
if err != nil {
return nil, err
}
stat, err := fs.NewStat()
if err != nil {
return nil, err
}
return &FS{fs, stat.BootTime}, nil
}
func (fs *FS) AllProcs() ProcIter {
procs, err := fs.FS.AllProcs()
if err != nil {
err = fmt.Errorf("Error reading procs: %v", err)
}
return &procIterator{procs: procfsprocs{procs, fs.BootTime}, err: err, idx: -1}
}
func (pi *procIterator) Next() bool {
pi.idx++
if pi.idx < pi.procs.length() {
pi.Proc = pi.procs.get(pi.idx)
} else {
pi.Proc = nil
}
return pi.idx < pi.procs.length()
}
func (pi *procIterator) Close() error {
pi.Next()
pi.procs = nil
pi.Proc = nil
return pi.err
}