Skip to content

Commit

Permalink
Add getrusage for unix and partially for Windows (#95)
Browse files Browse the repository at this point in the history
  • Loading branch information
kvch authored and andrewkroh committed Jan 22, 2018
1 parent daa20fc commit 450aa2b
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]

### Added
- Added partial `getrusage` support for Windows to retrieve system CPU time and user CPU time. #95
- Added full `getrusage` support for Unix. #95

### Fixed

Expand Down
8 changes: 8 additions & 0 deletions concrete_sigar.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,11 @@ func (c *ConcreteSigar) GetFDUsage() (FDUsage, error) {
err := fd.Get()
return fd, err
}

// GetRusage return the resource usage of the process
// Possible params: 0 = RUSAGE_SELF, 1 = RUSAGE_CHILDREN, 2 = RUSAGE_THREAD
func (c *ConcreteSigar) GetRusage(who int) (Rusage, error) {
r := Rusage{}
err := r.Get(who)
return r, err
}
10 changes: 10 additions & 0 deletions concrete_sigar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,13 @@ func TestConcreteGetFDUsage(t *testing.T) {
assert.True(t, fdUsage.Open <= fdUsage.Max)
}
}

func TestConcreteGetRusage(t *testing.T) {
concreteSigar := &sigar.ConcreteSigar{}
resourceUsage, err := concreteSigar.GetRusage(0)
skipNotImplemented(t, err, "netbsd", "solaris")
if assert.NoError(t, err) {
assert.True(t, resourceUsage.Utime >= 0)
assert.True(t, resourceUsage.Stime >= 0)
}
}
20 changes: 20 additions & 0 deletions sigar_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Sigar interface {
GetSwap() (Swap, error)
GetFileSystemUsage(string) (FileSystemUsage, error)
GetFDUsage() (FDUsage, error)
GetRusage(who int) (Rusage, error)
}

type Cpu struct {
Expand Down Expand Up @@ -175,3 +176,22 @@ type ProcFDUsage struct {
SoftLimit uint64
HardLimit uint64
}

type Rusage struct {
Utime time.Duration
Stime time.Duration
Maxrss int64
Ixrss int64
Idrss int64
Isrss int64
Minflt int64
Majflt int64
Nswap int64
Inblock int64
Oublock int64
Msgsnd int64
Msgrcv int64
Nsignals int64
Nvcsw int64
Nivcsw int64
}
4 changes: 4 additions & 0 deletions sigar_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,7 @@ func (p *ProcList) Get() error {
func (p *ProcArgs) Get(int) error {
return ErrNotImplemented{runtime.GOOS}
}

func (self *Rusage) Get(int) error {
return ErrNotImplemented{runtime.GOOS}
}
47 changes: 46 additions & 1 deletion sigar_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@

package gosigar

import "syscall"
import (
"syscall"
"time"

"golang.org/x/sys/unix"
)

func (self *FileSystemUsage) Get(path string) error {
stat := syscall.Statfs_t{}
Expand All @@ -22,3 +27,43 @@ func (self *FileSystemUsage) Get(path string) error {

return nil
}

func (r *Rusage) Get(who int) error {
ru, err := getResourceUsage(who)
if err != nil {
return err
}

uTime := convertRtimeToDur(ru.Utime)
sTime := convertRtimeToDur(ru.Stime)

r.Utime = uTime
r.Stime = sTime
r.Maxrss = int64(ru.Maxrss)
r.Ixrss = int64(ru.Ixrss)
r.Idrss = int64(ru.Idrss)
r.Isrss = int64(ru.Isrss)
r.Minflt = int64(ru.Minflt)
r.Majflt = int64(ru.Majflt)
r.Nswap = int64(ru.Nswap)
r.Inblock = int64(ru.Inblock)
r.Oublock = int64(ru.Oublock)
r.Msgsnd = int64(ru.Msgsnd)
r.Msgrcv = int64(ru.Msgrcv)
r.Nsignals = int64(ru.Nsignals)
r.Nvcsw = int64(ru.Nvcsw)
r.Nivcsw = int64(ru.Nivcsw)

return nil
}

func getResourceUsage(who int) (unix.Rusage, error) {
r := unix.Rusage{}
err := unix.Getrusage(who, &r)

return r, err
}

func convertRtimeToDur(t unix.Timeval) time.Duration {
return time.Duration(t.Nano())
}
49 changes: 38 additions & 11 deletions sigar_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package gosigar

import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
Expand Down Expand Up @@ -328,30 +329,39 @@ func (self *ProcMem) Get(pid int) error {
}

func (self *ProcTime) Get(pid int) error {
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
cpu, err := getProcTimes(pid)
if err != nil {
return errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
}
defer syscall.CloseHandle(handle)

var CPU syscall.Rusage
if err := syscall.GetProcessTimes(handle, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil {
return errors.Wrapf(err, "GetProcessTimes failed for pid=%v", pid)
return err
}

// Windows epoch times are expressed as time elapsed since midnight on
// January 1, 1601 at Greenwich, England. This converts the Filetime to
// unix epoch in milliseconds.
self.StartTime = uint64(CPU.CreationTime.Nanoseconds() / 1e6)
self.StartTime = uint64(cpu.CreationTime.Nanoseconds() / 1e6)

// Convert to millis.
self.User = uint64(windows.FiletimeToDuration(&CPU.UserTime).Nanoseconds() / 1e6)
self.Sys = uint64(windows.FiletimeToDuration(&CPU.KernelTime).Nanoseconds() / 1e6)
self.User = uint64(windows.FiletimeToDuration(&cpu.UserTime).Nanoseconds() / 1e6)
self.Sys = uint64(windows.FiletimeToDuration(&cpu.KernelTime).Nanoseconds() / 1e6)
self.Total = self.User + self.Sys

return nil
}

func getProcTimes(pid int) (*syscall.Rusage, error) {
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
if err != nil {
return nil, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
}
defer syscall.CloseHandle(handle)

var cpu syscall.Rusage
if err := syscall.GetProcessTimes(handle, &cpu.CreationTime, &cpu.ExitTime, &cpu.KernelTime, &cpu.UserTime); err != nil {
return nil, errors.Wrapf(err, "GetProcessTimes failed for pid=%v", pid)
}

return &cpu, nil
}

func (self *ProcArgs) Get(pid int) error {
// The minimum supported client for Win32_Process is Windows Vista.
if !version.IsWindowsVistaOrGreater() {
Expand Down Expand Up @@ -408,3 +418,20 @@ func getWin32OperatingSystem() (Win32_OperatingSystem, error) {
}
return dst[0], nil
}

func (self *Rusage) Get(who int) error {
if who != 0 {
return ErrNotImplemented{runtime.GOOS}
}

pid := os.Getpid()
cpu, err := getProcTimes(pid)
if err != nil {
return err
}

self.Utime = windows.FiletimeToDuration(&cpu.UserTime)
self.Stime = windows.FiletimeToDuration(&cpu.KernelTime)

return nil
}

0 comments on commit 450aa2b

Please sign in to comment.