diff --git a/process/process.go b/process/process.go index 14d7a83..e6abe99 100644 --- a/process/process.go +++ b/process/process.go @@ -2,6 +2,10 @@ package process import ( "encoding/json" + "runtime" + "time" + + cpu "github.com/shirou/gopsutil/cpu" ) type Process struct { @@ -13,6 +17,9 @@ type Process struct { gids []int32 numThreads int32 memInfo *MemoryInfoStat + + lastCPUTimes *cpu.CPUTimesStat + lastCPUTime time.Time } type OpenFilesStat struct { @@ -88,3 +95,48 @@ func PidExists(pid int32) (bool, error) { return false, err } + +// If interval is 0, return difference from last call(non-blocking). +// If interval > 0, wait interval sec and return diffrence between start and end. +func (p *Process) CPUPercent(interval time.Duration) (float64, error) { + calculate := func(t1, t2 *cpu.CPUTimesStat, delta float64) float64 { + if delta == 0 { + return 0 + } + numcpu := runtime.NumCPU() + delta_proc := (t2.User - t1.User) + (t2.System - t1.System) + overall_percent := ((delta_proc / delta) * 100) * float64(numcpu) + return overall_percent + } + + cpuTimes, err := p.CPUTimes() + if err != nil { + return 0, err + } + + if interval > 0 { + p.lastCPUTimes = cpuTimes + p.lastCPUTime = time.Now() + time.Sleep(interval) + cpuTimes, err = p.CPUTimes() + if err != nil { + return 0, err + } + } else { + if p.lastCPUTimes == nil { + // invoked first time + p.lastCPUTimes, err = p.CPUTimes() + if err != nil { + return 0, err + } + p.lastCPUTime = time.Now() + return 0, nil + } + } + + delta := (time.Now().Sub(p.lastCPUTime).Seconds()) * 1000 + ret := calculate(p.lastCPUTimes, cpuTimes, float64(delta)) + p.lastCPUTimes = cpuTimes + p.lastCPUTime = time.Now() + return ret, nil +} diff --git a/process/process_linux.go b/process/process_linux.go index 855761d..abe1645 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -148,9 +148,6 @@ func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) { } return cpuTimes, nil } -func (p *Process) CPUPercent() (int32, error) { - return 0, common.NotImplementedError -} func (p *Process) CPUAffinity() ([]int32, error) { return nil, common.NotImplementedError } @@ -545,6 +542,7 @@ func (p *Process) fillFromStat() (string, int32, *cpu.CPUTimesStat, int64, int32 if err != nil { return "", 0, nil, 0, 0, err } + stime, err := strconv.ParseFloat(fields[14], 64) if err != nil { return "", 0, nil, 0, 0, err diff --git a/process/process_test.go b/process/process_test.go index ac7bf50..2d4e9e6 100644 --- a/process/process_test.go +++ b/process/process_test.go @@ -4,6 +4,7 @@ import ( "os" "runtime" "testing" + "time" ) func testGetProcess() Process { @@ -126,3 +127,38 @@ func Test_Process_Nice(t *testing.T) { return } } + +func Test_Process_CpuPercent(t *testing.T) { + p := testGetProcess() + percent, err := p.CPUPercent(0) + if err != nil { + t.Errorf("error %v", err) + } + duration := time.Duration(1000) * time.Microsecond + time.Sleep(duration) + percent, err = p.CPUPercent(0) + if err != nil { + t.Errorf("error %v", err) + } + + numcpu := runtime.NumCPU() + if percent < 0.0 || percent > 100.0*float64(numcpu) { + t.Fatalf("CPUPercent value is invalid: %f", percent) + } +} + +func Test_Process_CpuPercentLoop(t *testing.T) { + p := testGetProcess() + numcpu := runtime.NumCPU() + + for i := 0; i < 2; i++ { + duration := time.Duration(100) * time.Microsecond + percent, err := p.CPUPercent(duration) + if err != nil { + t.Errorf("error %v", err) + } + if percent < 0.0 || percent > 100.0*float64(numcpu) { + t.Fatalf("CPUPercent value is invalid: %f", percent) + } + } +}