203 lines
6.5 KiB
Go
203 lines
6.5 KiB
Go
// Copyright 2018 The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package procfs
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// ProcStatus provides status information about the process,
|
|
// read from /proc/[pid]/status.
|
|
type (
|
|
ProcStatus struct {
|
|
TID int
|
|
TracerPid int
|
|
UIDReal int
|
|
UIDEffective int
|
|
UIDSavedSet int
|
|
UIDFileSystem int
|
|
GIDReal int
|
|
GIDEffective int
|
|
GIDSavedSet int
|
|
GIDFileSystem int
|
|
FDSize int
|
|
VmPeakKB int
|
|
VmSizeKB int
|
|
VmLckKB int
|
|
VmHWMKB int
|
|
VmRSSKB int
|
|
VmDataKB int
|
|
VmStkKB int
|
|
VmExeKB int
|
|
VmLibKB int
|
|
VmPTEKB int
|
|
VmSwapKB int
|
|
VoluntaryCtxtSwitches int
|
|
NonvoluntaryCtxtSwitches int
|
|
}
|
|
|
|
procStatusFiller func(*ProcStatus, string) error
|
|
procStatusBuilder struct {
|
|
scanners map[string]procStatusFiller
|
|
}
|
|
)
|
|
|
|
func (ps *ProcStatus) refTID() []interface{} {
|
|
return []interface{}{&ps.TID}
|
|
}
|
|
func (ps *ProcStatus) refTracerPid() []interface{} {
|
|
return []interface{}{&ps.TracerPid}
|
|
}
|
|
func (ps *ProcStatus) refUID() []interface{} {
|
|
return []interface{}{&ps.UIDReal, &ps.UIDEffective, &ps.UIDSavedSet, &ps.UIDFileSystem}
|
|
}
|
|
func (ps *ProcStatus) refGID() []interface{} {
|
|
return []interface{}{&ps.GIDReal, &ps.GIDEffective, &ps.GIDSavedSet, &ps.GIDFileSystem}
|
|
}
|
|
func (ps *ProcStatus) refFDSize() []interface{} {
|
|
return []interface{}{&ps.FDSize}
|
|
}
|
|
func (ps *ProcStatus) refVmPeakKB() []interface{} {
|
|
return []interface{}{&ps.VmPeakKB}
|
|
}
|
|
func (ps *ProcStatus) refVmSizeKB() []interface{} {
|
|
return []interface{}{&ps.VmSizeKB}
|
|
}
|
|
func (ps *ProcStatus) refVmLckKB() []interface{} {
|
|
return []interface{}{&ps.VmLckKB}
|
|
}
|
|
func (ps *ProcStatus) refVmHWMKB() []interface{} {
|
|
return []interface{}{&ps.VmHWMKB}
|
|
}
|
|
func (ps *ProcStatus) refVmRSSKB() []interface{} {
|
|
return []interface{}{&ps.VmRSSKB}
|
|
}
|
|
func (ps *ProcStatus) refVmDataKB() []interface{} {
|
|
return []interface{}{&ps.VmDataKB}
|
|
}
|
|
func (ps *ProcStatus) refVmStkKB() []interface{} {
|
|
return []interface{}{&ps.VmStkKB}
|
|
}
|
|
func (ps *ProcStatus) refVmExeKB() []interface{} {
|
|
return []interface{}{&ps.VmExeKB}
|
|
}
|
|
func (ps *ProcStatus) refVmLibKB() []interface{} {
|
|
return []interface{}{&ps.VmLibKB}
|
|
}
|
|
func (ps *ProcStatus) refVmPTEKB() []interface{} {
|
|
return []interface{}{&ps.VmPTEKB}
|
|
}
|
|
func (ps *ProcStatus) refVmSwapKB() []interface{} {
|
|
return []interface{}{&ps.VmSwapKB}
|
|
}
|
|
func (ps *ProcStatus) refVoluntaryCtxtSwitches() []interface{} {
|
|
return []interface{}{&ps.VoluntaryCtxtSwitches}
|
|
}
|
|
func (ps *ProcStatus) refNonvoluntaryCtxtSwitches() []interface{} {
|
|
return []interface{}{&ps.NonvoluntaryCtxtSwitches}
|
|
}
|
|
|
|
func newFiller(format string, ref func(ps *ProcStatus) []interface{}) procStatusFiller {
|
|
return procStatusFiller(func(ps *ProcStatus, s string) error {
|
|
_, err := fmt.Sscanf(s, format, ref(ps)...)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func newProcStatusBuilder() *procStatusBuilder {
|
|
return &procStatusBuilder{
|
|
scanners: map[string]procStatusFiller{
|
|
"Pid": newFiller("%d", (*ProcStatus).refTID),
|
|
"TracerPid": newFiller("%d", (*ProcStatus).refTracerPid),
|
|
"Uid": newFiller("%d %d %d %d", (*ProcStatus).refUID),
|
|
"Gid": newFiller("%d %d %d %d", (*ProcStatus).refGID),
|
|
"FDSize": newFiller("%d", (*ProcStatus).refFDSize),
|
|
"VmPeak": newFiller("%d kB", (*ProcStatus).refVmPeakKB),
|
|
"VmSize": newFiller("%d kB", (*ProcStatus).refVmSizeKB),
|
|
"VmLck": newFiller("%d kB", (*ProcStatus).refVmLckKB),
|
|
"VmHWM": newFiller("%d kB", (*ProcStatus).refVmHWMKB),
|
|
"VmRSS": newFiller("%d kB", (*ProcStatus).refVmRSSKB),
|
|
"VmData": newFiller("%d kB", (*ProcStatus).refVmDataKB),
|
|
"VmStk": newFiller("%d kB", (*ProcStatus).refVmStkKB),
|
|
"VmExe": newFiller("%d kB", (*ProcStatus).refVmExeKB),
|
|
"VmLib": newFiller("%d kB", (*ProcStatus).refVmLibKB),
|
|
"VmPTE": newFiller("%d kB", (*ProcStatus).refVmPTEKB),
|
|
"VmSwap": newFiller("%d kB", (*ProcStatus).refVmSwapKB),
|
|
"voluntary_ctxt_switches": newFiller("%d", (*ProcStatus).refVoluntaryCtxtSwitches),
|
|
"nonvoluntary_ctxt_switches": newFiller("%d", (*ProcStatus).refNonvoluntaryCtxtSwitches),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (b *procStatusBuilder) readStatus(r io.Reader) (ProcStatus, error) {
|
|
contents, err := ioutil.ReadAll(r)
|
|
if err != nil {
|
|
return ProcStatus{}, err
|
|
}
|
|
s := string(contents)
|
|
|
|
var ps ProcStatus
|
|
for lineno := 0; s != ""; lineno++ {
|
|
crpos := strings.IndexByte(s, '\n')
|
|
if crpos == -1 {
|
|
return ProcStatus{}, fmt.Errorf("line %d from status file without newline: %s", lineno, s)
|
|
}
|
|
line := strings.TrimSpace(s[:crpos])
|
|
s = s[crpos+1:]
|
|
if line == "" {
|
|
if s == "" {
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
|
|
pos := strings.IndexByte(line, ':')
|
|
if pos == -1 {
|
|
return ProcStatus{}, fmt.Errorf("line %d from status file without ':': %s", lineno, line)
|
|
}
|
|
|
|
field := line[:pos]
|
|
scanner, ok := b.scanners[field]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
err = scanner(&ps, line[pos+1:])
|
|
// TODO: flag parse errors with some kind of "warning" error.
|
|
if err != nil {
|
|
// Be lenient about parse errors, because otherwise we miss out on some interesting
|
|
// procs. For example, my Ubuntu kernel (4.4.0-130-generic #156-Ubuntu) is showing
|
|
// a Chromium status file with "VmLib: 18446744073709442944 kB".
|
|
continue
|
|
}
|
|
}
|
|
return ps, nil
|
|
}
|
|
|
|
var psb = newProcStatusBuilder()
|
|
|
|
// NewStatus returns the current status information of the process.
|
|
func (p Proc) NewStatus() (ProcStatus, error) {
|
|
f, err := os.Open(p.path("status"))
|
|
if err != nil {
|
|
return ProcStatus{}, err
|
|
}
|
|
defer f.Close()
|
|
|
|
return psb.readStatus(f)
|
|
}
|