Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add current values to rlimit retrieval #346

Merged
merged 1 commit into from
Aug 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Process struct {
gids []int32
numThreads int32
memInfo *MemoryInfoStat
sigInfo *SignalInfoStat

lastCPUTimes *cpu.TimesStat
lastCPUTime time.Time
Expand All @@ -37,15 +38,27 @@ type OpenFilesStat struct {
}

type MemoryInfoStat struct {
RSS uint64 `json:"rss"` // bytes
VMS uint64 `json:"vms"` // bytes
Swap uint64 `json:"swap"` // bytes
RSS uint64 `json:"rss"` // bytes
VMS uint64 `json:"vms"` // bytes
Data uint64 `json:"data"` // bytes
Stack uint64 `json:"stack"` // bytes
Locked uint64 `json:"locked"` // bytes
Swap uint64 `json:"swap"` // bytes
}

type SignalInfoStat struct {
PendingProcess uint64 `json:"pending_process"`
PendingThread uint64 `json:"pending_thread"`
Blocked uint64 `json:"blocked"`
Ignored uint64 `json:"ignored"`
Caught uint64 `json:"caught"`
}

type RlimitStat struct {
Resource int32 `json:"resource"`
Soft int32 `json:"soft"`
Hard int32 `json:"hard"`
Resource int32 `json:"resource"`
Soft int32 `json:"soft"` //TODO too small. needs to be uint64
Hard int32 `json:"hard"` //TODO too small. needs to be uint64
Used uint64 `json:"used"`
}

type IOCountersStat struct {
Expand Down
4 changes: 4 additions & 0 deletions process/process_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ func (p *Process) Rlimit() ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(_ bool) ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
return nil, common.ErrNotImplementedError
}
Expand Down
3 changes: 3 additions & 0 deletions process/process_fallback.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ func (p *Process) IOnice() (int32, error) {
func (p *Process) Rlimit() ([]RlimitStat, error) {
return nil, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(_ bool) ([]RlimitStat, error) {
return nil, common.ErrNotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
return nil, common.ErrNotImplementedError
}
Expand Down
4 changes: 4 additions & 0 deletions process/process_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ func (p *Process) Rlimit() ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(_ bool) ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
k, err := p.getKProc()
if err != nil {
Expand Down
145 changes: 131 additions & 14 deletions process/process_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func NewProcess(pid int32) (*Process, error) {

// Ppid returns Parent Process ID of the process.
func (p *Process) Ppid() (int32, error) {
_, ppid, _, _, _, err := p.fillFromStat()
_, ppid, _, _, _, _, err := p.fillFromStat()
if err != nil {
return -1, err
}
Expand Down Expand Up @@ -119,7 +119,7 @@ func (p *Process) CmdlineSlice() ([]string, error) {

// CreateTime returns created time of the process in seconds since the epoch, in UTC.
func (p *Process) CreateTime() (int64, error) {
_, _, _, createTime, _, err := p.fillFromStat()
_, _, _, createTime, _, _, err := p.fillFromStat()
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -176,7 +176,7 @@ func (p *Process) Gids() ([]int32, error) {

// Terminal returns a terminal which is associated with the process.
func (p *Process) Terminal() (string, error) {
terminal, _, _, _, _, err := p.fillFromStat()
terminal, _, _, _, _, _, err := p.fillFromStat()
if err != nil {
return "", err
}
Expand All @@ -186,7 +186,7 @@ func (p *Process) Terminal() (string, error) {
// Nice returns a nice value (priority).
// Notice: gopsutil can not set nice value.
func (p *Process) Nice() (int32, error) {
_, _, _, _, nice, err := p.fillFromStat()
_, _, _, _, _, nice, err := p.fillFromStat()
if err != nil {
return 0, err
}
Expand All @@ -200,7 +200,65 @@ func (p *Process) IOnice() (int32, error) {

// Rlimit returns Resource Limits.
func (p *Process) Rlimit() ([]RlimitStat, error) {
return p.fillFromLimits()
return p.RlimitUsage(false)
}

// RlimitUsage returns Resource Limits.
// If gatherUsed is true, the currently used value will be gathered and added
// to the resulting RlimitStat.
func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) {
rlimits, err := p.fillFromLimits()
if !gatherUsed || err != nil {
return rlimits, err
}

_, _, _, _, rtprio, nice, err := p.fillFromStat()
if err != nil {
return nil, err
}
if err := p.fillFromStatus(); err != nil {
return nil, err
}

for i := range rlimits {
rs := &rlimits[i]
switch rs.Resource {
case RLIMIT_CPU:
times, err := p.Times()
if err != nil {
return nil, err
}
rs.Used = uint64(times.User + times.System)
case RLIMIT_DATA:
rs.Used = uint64(p.memInfo.Data)
case RLIMIT_STACK:
rs.Used = uint64(p.memInfo.Stack)
case RLIMIT_RSS:
rs.Used = uint64(p.memInfo.RSS)
case RLIMIT_NOFILE:
n, err := p.NumFDs()
if err != nil {
return nil, err
}
rs.Used = uint64(n)
case RLIMIT_MEMLOCK:
rs.Used = uint64(p.memInfo.Locked)
case RLIMIT_AS:
rs.Used = uint64(p.memInfo.VMS)
case RLIMIT_LOCKS:
//TODO we can get the used value from /proc/$pid/locks. But linux doesn't enforce it, so not a high priority.
case RLIMIT_SIGPENDING:
rs.Used = p.sigInfo.PendingProcess
case RLIMIT_NICE:
// The rlimit for nice is a little unusual, in that 0 means the niceness cannot be decreased beyond the current value, but it can be increased.
// So effectively: if rs.Soft == 0 { rs.Soft = rs.Used }
rs.Used = uint64(nice)
case RLIMIT_RTPRIO:
rs.Used = uint64(rtprio)
}
}

return rlimits, err
}

// IOCounters returns IO Counters.
Expand Down Expand Up @@ -242,7 +300,7 @@ func (p *Process) Threads() (map[string]string, error) {

// Times returns CPU times of the process.
func (p *Process) Times() (*cpu.TimesStat, error) {
_, _, cpuTimes, _, _, err := p.fillFromStat()
_, _, cpuTimes, _, _, _, err := p.fillFromStat()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -711,6 +769,7 @@ func (p *Process) fillFromStatus() error {
lines := strings.Split(string(contents), "\n")
p.numCtxSwitches = &NumCtxSwitchesStat{}
p.memInfo = &MemoryInfoStat{}
p.sigInfo = &SignalInfoStat{}
for _, line := range lines {
tabParts := strings.SplitN(line, "\t", 2)
if len(tabParts) < 2 {
Expand Down Expand Up @@ -797,18 +856,69 @@ func (p *Process) fillFromStatus() error {
return err
}
p.memInfo.Swap = v * 1024
case "VmData":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Data = v * 1024
case "VmStk":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Stack = v * 1024
case "VmLck":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Locked = v * 1024
case "SigPnd":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.PendingThread = v
case "ShdPnd":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.PendingProcess = v
case "SigBlk":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.Blocked = v
case "SigIgn":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.Ignored = v
case "SigCgt":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.Caught = v
}

}
return nil
}

func (p *Process) fillFromStat() (string, int32, *cpu.TimesStat, int64, int32, error) {
func (p *Process) fillFromStat() (string, int32, *cpu.TimesStat, int64, uint32, int32, error) {
pid := p.Pid
statPath := common.HostProc(strconv.Itoa(int(pid)), "stat")
contents, err := ioutil.ReadFile(statPath)
if err != nil {
return "", 0, nil, 0, 0, err
return "", 0, nil, 0, 0, 0, err
}
fields := strings.Fields(string(contents))

Expand All @@ -822,23 +932,23 @@ func (p *Process) fillFromStat() (string, int32, *cpu.TimesStat, int64, int32, e
if err == nil {
t, err := strconv.ParseUint(fields[i+5], 10, 64)
if err != nil {
return "", 0, nil, 0, 0, err
return "", 0, nil, 0, 0, 0, err
}
terminal = termmap[t]
}

ppid, err := strconv.ParseInt(fields[i+2], 10, 32)
if err != nil {
return "", 0, nil, 0, 0, err
return "", 0, nil, 0, 0, 0, err
}
utime, err := strconv.ParseFloat(fields[i+12], 64)
if err != nil {
return "", 0, nil, 0, 0, err
return "", 0, nil, 0, 0, 0, err
}

stime, err := strconv.ParseFloat(fields[i+13], 64)
if err != nil {
return "", 0, nil, 0, 0, err
return "", 0, nil, 0, 0, 0, err
}

cpuTimes := &cpu.TimesStat{
Expand All @@ -850,17 +960,24 @@ func (p *Process) fillFromStat() (string, int32, *cpu.TimesStat, int64, int32, e
bootTime, _ := host.BootTime()
t, err := strconv.ParseUint(fields[i+20], 10, 64)
if err != nil {
return "", 0, nil, 0, 0, err
return "", 0, nil, 0, 0, 0, err
}
ctime := (t / uint64(ClockTicks)) + uint64(bootTime)
createTime := int64(ctime * 1000)

rtpriority, err := strconv.ParseInt(fields[i+16], 10, 32)
if rtpriority < 0 {
rtpriority = rtpriority*-1 - 1
} else {
rtpriority = 0
}

// p.Nice = mustParseInt32(fields[18])
// use syscall instead of parse Stat file
snice, _ := syscall.Getpriority(PrioProcess, int(pid))
nice := int32(snice) // FIXME: is this true?

return terminal, int32(ppid), cpuTimes, createTime, nice, nil
return terminal, int32(ppid), cpuTimes, createTime, uint32(rtpriority), nice, nil
}

// Pids returns a slice of process ID list which are running now.
Expand Down
4 changes: 4 additions & 0 deletions process/process_openbsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ func (p *Process) Rlimit() ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(_ bool) ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
k, err := p.getKProc()
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions process/process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ func (p *Process) Rlimit() ([]RlimitStat, error) {

return rlimit, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(_ bool) ([]RlimitStat, error) {
var rlimit []RlimitStat

return rlimit, common.ErrNotImplementedError
}

func (p *Process) IOCounters() (*IOCountersStat, error) {
dst, err := GetWin32Proc(p.Pid)
Expand Down