From 903277ce2f1501d31ea8fb03731a30b382ddd1a5 Mon Sep 17 00:00:00 2001 From: WAKAYAMA Shirou Date: Wed, 4 May 2016 22:39:44 +0900 Subject: [PATCH 01/21] [process]windows: implement process.MemoryInfo (but no swap) --- process/process_windows.go | 45 ++++++++++++++++++++++++++++++++++++++-- process/process_windows_386.go | 16 ++++++++++++++ process/process_windows_amd64.go | 16 ++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 process/process_windows_386.go create mode 100644 process/process_windows_amd64.go diff --git a/process/process_windows.go b/process/process_windows.go index 3176cde..e5852ae 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -23,6 +23,11 @@ const ( MaxPathLength = 260 ) +var ( + modpsapi = syscall.NewLazyDLL("psapi.dll") + procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") +) + type SystemProcessInformation struct { NextEntryOffset uint64 NumberOfThreads uint64 @@ -75,7 +80,7 @@ type Win32_Process struct { PeakPageFileUsage uint32 PeakVirtualSize uint64 PeakWorkingSetSize uint32 - PrivatePageCount uint64 + PrivatePageCount uint64 ReadOperationCount uint64 ReadTransferCount uint64 Status *string @@ -234,7 +239,17 @@ func (p *Process) CPUAffinity() ([]int32, error) { return nil, common.ErrNotImplementedError } func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { - return nil, common.ErrNotImplementedError + mem, err := getMemoryInfo(p.Pid) + if err != nil { + return nil, err + } + + ret := &MemoryInfoStat{ + RSS: mem.WorkingSetSize, + VMS: mem.PagefileUsage, + } + + return ret, nil } func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { return nil, common.ErrNotImplementedError @@ -355,3 +370,29 @@ func getProcInfo(pid int32) (*SystemProcessInformation, error) { return &sysProcInfo, nil } + +func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) { + var mem PROCESS_MEMORY_COUNTERS + c, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid)) + if err != nil { + return mem, err + } + defer syscall.CloseHandle(c) + if err := getProcessMemoryInfo(c, &mem); err != nil { + return mem, err + } + + return mem, err +} + +func getProcessMemoryInfo(h syscall.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) { + r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem))) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} diff --git a/process/process_windows_386.go b/process/process_windows_386.go new file mode 100644 index 0000000..68f3153 --- /dev/null +++ b/process/process_windows_386.go @@ -0,0 +1,16 @@ +// +build windows + +package process + +type PROCESS_MEMORY_COUNTERS struct { + CB uint32 + PageFaultCount uint32 + PeakWorkingSetSize uint32 + WorkingSetSize uint32 + QuotaPeakPagedPoolUsage uint32 + QuotaPagedPoolUsage uint32 + QuotaPeakNonPagedPoolUsage uint32 + QuotaNonPagedPoolUsage uint32 + PagefileUsage uint32 + PeakPagefileUsage uint32 +} diff --git a/process/process_windows_amd64.go b/process/process_windows_amd64.go new file mode 100644 index 0000000..df286df --- /dev/null +++ b/process/process_windows_amd64.go @@ -0,0 +1,16 @@ +// +build windows + +package process + +type PROCESS_MEMORY_COUNTERS struct { + CB uint32 + PageFaultCount uint32 + PeakWorkingSetSize uint64 + WorkingSetSize uint64 + QuotaPeakPagedPoolUsage uint64 + QuotaPagedPoolUsage uint64 + QuotaPeakNonPagedPoolUsage uint64 + QuotaNonPagedPoolUsage uint64 + PagefileUsage uint64 + PeakPagefileUsage uint64 +} From ba34a3af8cc38f4c2bef584d184023121b29b23e Mon Sep 17 00:00:00 2001 From: WAKAYAMA Shirou Date: Wed, 4 May 2016 23:25:43 +0900 Subject: [PATCH 02/21] [process]windows: add IO counters and fix CreateTime --- README.rst | 4 +-- process/process_windows.go | 62 +++++++++++++++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/README.rst b/README.rst index b96fd5f..8a99795 100644 --- a/README.rst +++ b/README.rst @@ -187,13 +187,13 @@ exe x x x uids x x x gids x x x terminal x x x -io_counters x x +io_counters x x x nice x x x x num_fds x num_ctx_switches x num_threads x x x x cpu_times x -memory_info x x x +memory_info x x x x memory_info_ex x memory_maps x open_files x diff --git a/process/process_windows.go b/process/process_windows.go index e5852ae..c3d2837 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -51,13 +51,18 @@ type MemoryMapsStat struct { } type Win32_Process struct { - Name string - ExecutablePath *string - CommandLine *string - Priority uint32 - CreationDate *time.Time - ProcessID uint32 - ThreadCount uint32 + Name string + ExecutablePath *string + CommandLine *string + Priority uint32 + CreationDate *time.Time + ProcessID uint32 + ThreadCount uint32 + Status *string + ReadOperationCount uint64 + ReadTransferCount uint64 + WriteOperationCount uint64 + WriteTransferCount uint64 /* CSCreationClassName string @@ -80,15 +85,10 @@ type Win32_Process struct { PeakPageFileUsage uint32 PeakVirtualSize uint64 PeakWorkingSetSize uint32 - PrivatePageCount uint64 - ReadOperationCount uint64 - ReadTransferCount uint64 - Status *string + PrivatePageCount uint64 TerminationDate *time.Time UserModeTime uint64 WorkingSetSize uint64 - WriteOperationCount uint64 - WriteTransferCount uint64 */ } @@ -163,12 +163,12 @@ func (p *Process) CmdlineSlice() ([]string, error) { } func (p *Process) CreateTime() (int64, error) { - dst, err := GetWin32Proc(p.Pid) + ru, err := getRusage(p.Pid) if err != nil { return 0, fmt.Errorf("could not get CreationDate: %s", err) } - date := *dst[0].CreationDate - return date.Unix(), nil + + return ru.CreationTime.Nanoseconds() / 1000000, nil } func (p *Process) Cwd() (string, error) { @@ -212,8 +212,20 @@ func (p *Process) Rlimit() ([]RlimitStat, error) { return rlimit, common.ErrNotImplementedError } + func (p *Process) IOCounters() (*IOCountersStat, error) { - return nil, common.ErrNotImplementedError + dst, err := GetWin32Proc(p.Pid) + if err != nil || len(dst) == 0 { + return nil, fmt.Errorf("could not get Win32Proc: %s", err) + } + ret := &IOCountersStat{ + ReadCount: uint64(dst[0].ReadOperationCount), + ReadBytes: uint64(dst[0].ReadTransferCount), + WriteCount: uint64(dst[0].WriteOperationCount), + WriteBytes: uint64(dst[0].WriteTransferCount), + } + + return ret, nil } func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { return nil, common.ErrNotImplementedError @@ -371,6 +383,22 @@ func getProcInfo(pid int32) (*SystemProcessInformation, error) { return &sysProcInfo, nil } +func getRusage(pid int32) (*syscall.Rusage, error) { + var CPU syscall.Rusage + + c, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid)) + if err != nil { + return nil, err + } + defer syscall.CloseHandle(c) + + if err := syscall.GetProcessTimes(c, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil { + return nil, err + } + + return &CPU, nil +} + func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) { var mem PROCESS_MEMORY_COUNTERS c, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid)) From 57f6aebc7e130b7a934c1df72340cd6ab336e98b Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Fri, 20 May 2016 17:59:41 +0900 Subject: [PATCH 03/21] add Timeout to invoke command and use common.Invoke refs: #201 --- cpu/cpu.go | 8 +++++ cpu/cpu_darwin.go | 4 +-- cpu/cpu_freebsd.go | 2 +- cpu/cpu_linux.go | 2 +- disk/disk.go | 8 +++++ disk/disk_linux.go | 2 +- docker/docker.go | 12 +++++++- docker/docker_linux.go | 4 +-- host/host.go | 14 +++++++-- host/host_darwin.go | 4 +-- host/host_freebsd.go | 4 +-- host/host_linux.go | 2 +- internal/common/common.go | 77 ++++++++++++++++++++++++++++++++++++++--------- mem/mem.go | 8 +++++ mem/mem_darwin_nocgo.go | 2 +- mem/mem_darwin_test.go | 3 +- mem/mem_freebsd.go | 2 +- net/net_darwin.go | 2 +- net/net_freebsd.go | 2 +- process/process_posix.go | 4 ++- 20 files changed, 128 insertions(+), 38 deletions(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index 7153509..ae6ab04 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -5,6 +5,8 @@ import ( "runtime" "strconv" "strings" + + "github.com/shirou/gopsutil/internal/common" ) type TimesStat struct { @@ -37,6 +39,12 @@ type InfoStat struct { Flags []string `json:"flags"` } +var invoke common.Invoker + +func init() { + invoke = common.Invoke{} +} + var lastCPUTimes []TimesStat var lastPerCPUTimes []TimesStat diff --git a/cpu/cpu_darwin.go b/cpu/cpu_darwin.go index fbb74a8..4cb1d8c 100644 --- a/cpu/cpu_darwin.go +++ b/cpu/cpu_darwin.go @@ -36,7 +36,7 @@ func Info() ([]InfoStat, error) { if err != nil { return ret, err } - out, err := exec.Command(sysctl, "machdep.cpu").Output() + out, err := invoke.Command(sysctl, "machdep.cpu") if err != nil { return ret, err } @@ -90,7 +90,7 @@ func Info() ([]InfoStat, error) { // Use the rated frequency of the CPU. This is a static value and does not // account for low power or Turbo Boost modes. - out, err = exec.Command(sysctl, "hw.cpufrequency").Output() + out, err = invoke.Command(sysctl, "hw.cpufrequency") if err != nil { return ret, err } diff --git a/cpu/cpu_freebsd.go b/cpu/cpu_freebsd.go index ce1adf3..2f8d3ac 100644 --- a/cpu/cpu_freebsd.go +++ b/cpu/cpu_freebsd.go @@ -29,7 +29,7 @@ func init() { if err != nil { return } - out, err := exec.Command(getconf, "CLK_TCK").Output() + out, err := invoke.Command(getconf, "CLK_TCK") // ignore errors if err == nil { i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) diff --git a/cpu/cpu_linux.go b/cpu/cpu_linux.go index 975b75c..6df542b 100644 --- a/cpu/cpu_linux.go +++ b/cpu/cpu_linux.go @@ -19,7 +19,7 @@ func init() { if err != nil { return } - out, err := exec.Command(getconf, "CLK_TCK").Output() + out, err := invoke.Command(getconf, "CLK_TCK") // ignore errors if err == nil { i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) diff --git a/disk/disk.go b/disk/disk.go index b187a1d..3135c0c 100644 --- a/disk/disk.go +++ b/disk/disk.go @@ -2,8 +2,16 @@ package disk import ( "encoding/json" + + "github.com/shirou/gopsutil/internal/common" ) +var invoke common.Invoker + +func init() { + invoke = common.Invoke{} +} + type UsageStat struct { Path string `json:"path"` Fstype string `json:"fstype"` diff --git a/disk/disk_linux.go b/disk/disk_linux.go index c1c3345..f581ef7 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -339,7 +339,7 @@ func GetDiskSerialNumber(name string) string { return "" } - out, err := exec.Command(udevadm, "info", "--query=property", n).Output() + out, err := invoke.Command(udevadm, "info", "--query=property", n) // does not return error, just an empty string if err != nil { diff --git a/docker/docker.go b/docker/docker.go index 1bd3bdc..1d932cf 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -1,10 +1,20 @@ package docker -import "errors" +import ( + "errors" + + "github.com/shirou/gopsutil/internal/common" +) var ErrDockerNotAvailable = errors.New("docker not available") var ErrCgroupNotAvailable = errors.New("cgroup not available") +var invoke common.Invoker + +func init() { + invoke = common.Invoke{} +} + type CgroupMemStat struct { ContainerID string `json:"containerID"` Cache uint64 `json:"cache"` diff --git a/docker/docker_linux.go b/docker/docker_linux.go index 2501c16..000d2f2 100644 --- a/docker/docker_linux.go +++ b/docker/docker_linux.go @@ -23,7 +23,7 @@ func GetDockerStat() ([]CgroupDockerStat, error) { return nil, ErrDockerNotAvailable } - out, err := exec.Command(path, "ps", "-a", "--no-trunc", "--format", "{{.ID}}|{{.Image}}|{{.Names}}|{{.Status}}").Output() + out, err := invoke.Command(path, "ps", "-a", "--no-trunc", "--format", "{{.ID}}|{{.Image}}|{{.Names}}|{{.Status}}") if err != nil { return []CgroupDockerStat{}, err } @@ -65,7 +65,7 @@ func GetDockerIDList() ([]string, error) { return nil, ErrDockerNotAvailable } - out, err := exec.Command(path, "ps", "-q", "--no-trunc").Output() + out, err := invoke.Command(path, "ps", "-q", "--no-trunc") if err != nil { return []string{}, err } diff --git a/host/host.go b/host/host.go index 150eb60..1a6545b 100644 --- a/host/host.go +++ b/host/host.go @@ -2,17 +2,25 @@ package host import ( "encoding/json" + + "github.com/shirou/gopsutil/internal/common" ) +var invoke common.Invoker + +func init() { + invoke = common.Invoke{} +} + // A HostInfoStat describes the host status. // This is not in the psutil but it useful. type InfoStat struct { Hostname string `json:"hostname"` Uptime uint64 `json:"uptime"` BootTime uint64 `json:"bootTime"` - Procs uint64 `json:"procs"` // number of processes - OS string `json:"os"` // ex: freebsd, linux - Platform string `json:"platform"` // ex: ubuntu, linuxmint + Procs uint64 `json:"procs"` // number of processes + OS string `json:"os"` // ex: freebsd, linux + Platform string `json:"platform"` // ex: ubuntu, linuxmint PlatformFamily string `json:"platformFamily"` // ex: debian, rhel PlatformVersion string `json:"platformVersion"` VirtualizationSystem string `json:"virtualizationSystem"` diff --git a/host/host_darwin.go b/host/host_darwin.go index f4a8c36..ec2dc2c 100644 --- a/host/host_darwin.go +++ b/host/host_darwin.go @@ -131,12 +131,12 @@ func PlatformInformation() (string, string, string, error) { if err != nil { return "", "", "", err } - out, err := exec.Command(uname, "-s").Output() + out, err := invoke.Command(uname, "-s") if err == nil { platform = strings.ToLower(strings.TrimSpace(string(out))) } - out, err = exec.Command(uname, "-r").Output() + out, err = invoke.Command(uname, "-r") if err == nil { version = strings.ToLower(strings.TrimSpace(string(out))) } diff --git a/host/host_freebsd.go b/host/host_freebsd.go index aeb1b45..30206d2 100644 --- a/host/host_freebsd.go +++ b/host/host_freebsd.go @@ -136,12 +136,12 @@ func PlatformInformation() (string, string, string, error) { return "", "", "", err } - out, err := exec.Command(uname, "-s").Output() + out, err := invoke.Command(uname, "-s") if err == nil { platform = strings.ToLower(strings.TrimSpace(string(out))) } - out, err = exec.Command(uname, "-r").Output() + out, err = invoke.Command(uname, "-r") if err == nil { version = strings.ToLower(strings.TrimSpace(string(out))) } diff --git a/host/host_linux.go b/host/host_linux.go index 14d0935..97924bf 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -164,7 +164,7 @@ func getLSB() (*LSB, error) { if err != nil { return ret, err } - out, err := exec.Command(lsb_release).Output() + out, err := invoke.Command(lsb_release) if err != nil { return ret, err } diff --git a/internal/common/common.go b/internal/common/common.go index e190f4d..42cbaf9 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -8,8 +8,10 @@ package common // - windows (amd64) import ( "bufio" + "bytes" "errors" "io/ioutil" + "log" "net/url" "os" "os/exec" @@ -19,6 +21,12 @@ import ( "runtime" "strconv" "strings" + "time" +) + +var ( + Timeout = 3 * time.Second + TimeoutErr = errors.New("Command timed out.") ) type Invoker interface { @@ -28,7 +36,8 @@ type Invoker interface { type Invoke struct{} func (i Invoke) Command(name string, arg ...string) ([]byte, error) { - return exec.Command(name, arg...).Output() + cmd := exec.Command(name, arg...) + return CombinedOutputTimeout(cmd, Timeout) } type FakeInvoke struct { @@ -118,20 +127,20 @@ func IntToString(orig []int8) string { } func UintToString(orig []uint8) string { - ret := make([]byte, len(orig)) - size := -1 - for i, o := range orig { - if o == 0 { - size = i - break - } - ret[i] = byte(o) - } - if size == -1 { - size = len(orig) - } - - return string(ret[0:size]) + ret := make([]byte, len(orig)) + size := -1 + for i, o := range orig { + if o == 0 { + size = i + break + } + ret[i] = byte(o) + } + if size == -1 { + size = len(orig) + } + + return string(ret[0:size]) } func ByteToString(orig []byte) string { @@ -294,3 +303,41 @@ func HostSys(combineWith ...string) string { func HostEtc(combineWith ...string) string { return GetEnv("HOST_ETC", "/etc", combineWith...) } + +// CombinedOutputTimeout runs the given command with the given timeout and +// returns the combined output of stdout and stderr. +// If the command times out, it attempts to kill the process. +// copied from https://github.com/influxdata/telegraf +func CombinedOutputTimeout(c *exec.Cmd, timeout time.Duration) ([]byte, error) { + var b bytes.Buffer + c.Stdout = &b + c.Stderr = &b + if err := c.Start(); err != nil { + return nil, err + } + err := WaitTimeout(c, timeout) + return b.Bytes(), err +} + +// WaitTimeout waits for the given command to finish with a timeout. +// It assumes the command has already been started. +// If the command times out, it attempts to kill the process. +// copied from https://github.com/influxdata/telegraf +func WaitTimeout(c *exec.Cmd, timeout time.Duration) error { + timer := time.NewTimer(timeout) + done := make(chan error) + go func() { done <- c.Wait() }() + select { + case err := <-done: + timer.Stop() + return err + case <-timer.C: + if err := c.Process.Kill(); err != nil { + log.Printf("FATAL error killing process: %s", err) + return err + } + // wait for the command to return after killing it + <-done + return TimeoutErr + } +} diff --git a/mem/mem.go b/mem/mem.go index 5f122d1..3255890 100644 --- a/mem/mem.go +++ b/mem/mem.go @@ -2,8 +2,16 @@ package mem import ( "encoding/json" + + "github.com/shirou/gopsutil/internal/common" ) +var invoke common.Invoker + +func init() { + invoke = common.Invoke{} +} + // Memory usage statistics. Total, Available and Used contain numbers of bytes // for human consumption. // diff --git a/mem/mem_darwin_nocgo.go b/mem/mem_darwin_nocgo.go index 7094802..3eb33ab 100644 --- a/mem/mem_darwin_nocgo.go +++ b/mem/mem_darwin_nocgo.go @@ -16,7 +16,7 @@ func getVMStat(vms *VirtualMemoryStat) error { if err != nil { return err } - out, err := exec.Command(vm_stat).Output() + out, err := invoke.Command(vm_stat) if err != nil { return err } diff --git a/mem/mem_darwin_test.go b/mem/mem_darwin_test.go index 6d9e6b8..dba421f 100644 --- a/mem/mem_darwin_test.go +++ b/mem/mem_darwin_test.go @@ -3,7 +3,6 @@ package mem import ( - "os/exec" "strconv" "strings" "testing" @@ -15,7 +14,7 @@ func TestVirtualMemoryDarwin(t *testing.T) { v, err := VirtualMemory() assert.Nil(t, err) - outBytes, err := exec.Command("/usr/sbin/sysctl", "hw.memsize").Output() + outBytes, err := invoke.Command("/usr/sbin/sysctl", "hw.memsize") assert.Nil(t, err) outString := string(outBytes) outString = strings.TrimSpace(outString) diff --git a/mem/mem_freebsd.go b/mem/mem_freebsd.go index 7194057..8cca44b 100644 --- a/mem/mem_freebsd.go +++ b/mem/mem_freebsd.go @@ -93,7 +93,7 @@ func SwapMemory() (*SwapMemoryStat, error) { return nil, err } - out, err := exec.Command(swapinfo).Output() + out, err := invoke.Command(swapinfo) if err != nil { return nil, err } diff --git a/net/net_darwin.go b/net/net_darwin.go index 4fa358a..78ccb66 100644 --- a/net/net_darwin.go +++ b/net/net_darwin.go @@ -21,7 +21,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) { if err != nil { return nil, err } - out, err := exec.Command(netstat, "-ibdnW").Output() + out, err := invoke.Command(netstat, "-ibdnW") if err != nil { return nil, err } diff --git a/net/net_freebsd.go b/net/net_freebsd.go index 3a67b4a..2b54655 100644 --- a/net/net_freebsd.go +++ b/net/net_freebsd.go @@ -16,7 +16,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) { if err != nil { return nil, err } - out, err := exec.Command(netstat, "-ibdnW").Output() + out, err := invoke.Command(netstat, "-ibdnW") if err != nil { return nil, err } diff --git a/process/process_posix.go b/process/process_posix.go index 8853118..745ff6e 100644 --- a/process/process_posix.go +++ b/process/process_posix.go @@ -9,6 +9,8 @@ import ( "strconv" "strings" "syscall" + + "github.com/shirou/gopsutil/internal/common" ) // POSIX @@ -72,7 +74,7 @@ func (p *Process) SendSignal(sig syscall.Signal) error { } cmd := exec.Command(kill, "-s", sigAsStr, strconv.Itoa(int(p.Pid))) cmd.Stderr = os.Stderr - err = cmd.Run() + err = common.WaitTimeout(cmd, common.Timeout) if err != nil { return err } From 1d461a68b629ba70133c92f63e1405197c66a081 Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Fri, 20 May 2016 21:13:52 +0900 Subject: [PATCH 04/21] [process]linux: porcess was not started. --- process/process_posix.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/process/process_posix.go b/process/process_posix.go index 745ff6e..0751f68 100644 --- a/process/process_posix.go +++ b/process/process_posix.go @@ -74,6 +74,9 @@ func (p *Process) SendSignal(sig syscall.Signal) error { } cmd := exec.Command(kill, "-s", sigAsStr, strconv.Itoa(int(p.Pid))) cmd.Stderr = os.Stderr + if err := cmd.Start(); err != nil { + return err + } err = common.WaitTimeout(cmd, common.Timeout) if err != nil { return err From c3899894530a9b38dcad8ceca651ec44895f8441 Mon Sep 17 00:00:00 2001 From: Ben Aldrich Date: Wed, 1 Jun 2016 15:21:58 -0600 Subject: [PATCH 05/21] calculate cpu percentage compared to last call if 0 interval is given --- cpu/cpu.go | 18 ++++++++++-- cpu/cpu_test.go | 42 ++++++++++++++++++++++++++++ cpu/cpu_unix.go | 87 ++++++++++++++++++++++++++++++++++++++------------------- 3 files changed, 117 insertions(+), 30 deletions(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index 7153509..0081cb7 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -5,6 +5,7 @@ import ( "runtime" "strconv" "strings" + "sync" ) type TimesStat struct { @@ -37,8 +38,21 @@ type InfoStat struct { Flags []string `json:"flags"` } -var lastCPUTimes []TimesStat -var lastPerCPUTimes []TimesStat +//CPUPercent +type cpuPercent struct { + sync.Mutex + lastCPUTimes []TimesStat + lastPerCPUTimes []TimesStat +} + +var lastCPUPercent cpuPercent + +func init() { + lastCPUPercent.Lock() + lastCPUPercent.lastCPUTimes, _ = Times(false) + lastCPUPercent.lastPerCPUTimes, _ = Times(true) + lastCPUPercent.Unlock() +} func Counts(logical bool) (int, error) { return runtime.NumCPU(), nil diff --git a/cpu/cpu_test.go b/cpu/cpu_test.go index c3d56eb..6082ffc 100644 --- a/cpu/cpu_test.go +++ b/cpu/cpu_test.go @@ -94,6 +94,40 @@ func testCPUPercent(t *testing.T, percpu bool) { } } +func testCPUPercentLastUsed(t *testing.T, percpu bool) { + + numcpu := runtime.NumCPU() + testCount := 10 + + if runtime.GOOS != "windows" { + testCount = 2 + v, err := Percent(time.Millisecond, percpu) + if err != nil { + t.Errorf("error %v", err) + } + // Skip CircleCI which CPU num is different + if os.Getenv("CIRCLECI") != "true" { + if (percpu && len(v) != numcpu) || (!percpu && len(v) != 1) { + t.Fatalf("wrong number of entries from CPUPercent: %v", v) + } + } + } + for i := 0; i < testCount; i++ { + v, err := Percent(0, percpu) + if err != nil { + t.Errorf("error %v", err) + } + time.Sleep(1 * time.Millisecond) + for _, percent := range v { + // Check for slightly greater then 100% to account for any rounding issues. + if percent < 0.0 || percent > 100.0001*float64(numcpu) { + t.Fatalf("CPUPercent value is invalid: %f", percent) + } + } + } + +} + func TestCPUPercent(t *testing.T) { testCPUPercent(t, false) } @@ -101,3 +135,11 @@ func TestCPUPercent(t *testing.T) { func TestCPUPercentPerCpu(t *testing.T) { testCPUPercent(t, true) } + +func TestCPUPercentIntervalZero(t *testing.T) { + testCPUPercentLastUsed(t, false) +} + +func TestCPUPercentIntervalZeroPerCPU(t *testing.T) { + testCPUPercentLastUsed(t, true) +} diff --git a/cpu/cpu_unix.go b/cpu/cpu_unix.go index 9f1ea4d..598aac5 100644 --- a/cpu/cpu_unix.go +++ b/cpu/cpu_unix.go @@ -7,24 +7,46 @@ import ( "time" ) -func Percent(interval time.Duration, percpu bool) ([]float64, error) { - getAllBusy := func(t TimesStat) (float64, float64) { - busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + - t.Softirq + t.Steal + t.Guest + t.GuestNice + t.Stolen - return busy + t.Idle, busy +func getAllBusy(t TimesStat) (float64, float64) { + busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + + t.Softirq + t.Steal + t.Guest + t.GuestNice + t.Stolen + return busy + t.Idle, busy +} + +func calculateBusy(t1, t2 TimesStat) float64 { + t1All, t1Busy := getAllBusy(t1) + t2All, t2Busy := getAllBusy(t2) + + if t2Busy <= t1Busy { + return 0 + } + if t2All <= t1All { + return 1 + } + return (t2Busy - t1Busy) / (t2All - t1All) * 100 +} + +func calcualteALLBusy(t1, t2 []TimesStat) ([]float64, error) { + // Make sure the CPU measurements have the same length. + if len(t1) != len(t2) { + return nil, fmt.Errorf( + "received two CPU counts: %d != %d", + len(t1), len(t2), + ) } - calculate := func(t1, t2 TimesStat) float64 { - t1All, t1Busy := getAllBusy(t1) - t2All, t2Busy := getAllBusy(t2) + ret := make([]float64, len(t1)) + for i, t := range t2 { + ret[i] = calculateBusy(t1[i], t) + } + return ret, nil +} - if t2Busy <= t1Busy { - return 0 - } - if t2All <= t1All { - return 1 - } - return (t2Busy - t1Busy) / (t2All - t1All) * 100 +//Percent calculates the percentage of cpu used either per CPU or combined. +//If an interval of 0 is given it will compare the current cpu times against the last call. +func Percent(interval time.Duration, percpu bool) ([]float64, error) { + if interval <= 0 { + return percentUsedFromLastCall(percpu) } // Get CPU usage at the start of the interval. @@ -33,9 +55,7 @@ func Percent(interval time.Duration, percpu bool) ([]float64, error) { return nil, err } - if interval > 0 { - time.Sleep(interval) - } + time.Sleep(interval) // And at the end of the interval. cpuTimes2, err := Times(percpu) @@ -43,17 +63,28 @@ func Percent(interval time.Duration, percpu bool) ([]float64, error) { return nil, err } - // Make sure the CPU measurements have the same length. - if len(cpuTimes1) != len(cpuTimes2) { - return nil, fmt.Errorf( - "received two CPU counts: %d != %d", - len(cpuTimes1), len(cpuTimes2), - ) + return calcualteALLBusy(cpuTimes1, cpuTimes2) +} + +func percentUsedFromLastCall(percpu bool) ([]float64, error) { + cpuTimes, err := Times(percpu) + if err != nil { + return nil, err + } + lastCPUPercent.Lock() + defer lastCPUPercent.Unlock() + var lastTimes []TimesStat + if percpu { + lastTimes = lastCPUPercent.lastPerCPUTimes + lastCPUPercent.lastPerCPUTimes = cpuTimes + } else { + lastTimes = lastCPUPercent.lastCPUTimes + lastCPUPercent.lastCPUTimes = cpuTimes } - ret := make([]float64, len(cpuTimes1)) - for i, t := range cpuTimes2 { - ret[i] = calculate(cpuTimes1[i], t) + if lastTimes == nil { + return nil, fmt.Errorf("Error getting times for cpu percent. LastTimes was nil") } - return ret, nil + return calcualteALLBusy(lastTimes, cpuTimes) + } From c36f7ac8b0514e7b860570b1e52b2d0979860102 Mon Sep 17 00:00:00 2001 From: Ben Aldrich Date: Wed, 1 Jun 2016 15:45:29 -0600 Subject: [PATCH 06/21] rename var --- cpu/cpu.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index b5417ae..dbdef17 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -39,14 +39,13 @@ type InfoStat struct { Flags []string `json:"flags"` } -//CPUPercent -type cpuPercent struct { +type lastPercent struct { sync.Mutex lastCPUTimes []TimesStat lastPerCPUTimes []TimesStat } -var lastCPUPercent cpuPercent +var lastCPUPercent lastPercent var invoke common.Invoker func init() { From a76195e0883eb818d2445963e4168594a684be14 Mon Sep 17 00:00:00 2001 From: Ben Aldrich Date: Wed, 1 Jun 2016 15:53:54 -0600 Subject: [PATCH 07/21] fixed imports and spelling error --- cpu/cpu.go | 3 ++- cpu/cpu_unix.go | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index dbdef17..6f76b76 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -2,11 +2,12 @@ package cpu import ( "encoding/json" - "github.com/shirou/gopsutil/internal/common" "runtime" "strconv" "strings" "sync" + + "github.com/shirou/gopsutil/internal/common" ) type TimesStat struct { diff --git a/cpu/cpu_unix.go b/cpu/cpu_unix.go index 598aac5..4bdf3b7 100644 --- a/cpu/cpu_unix.go +++ b/cpu/cpu_unix.go @@ -26,7 +26,7 @@ func calculateBusy(t1, t2 TimesStat) float64 { return (t2Busy - t1Busy) / (t2All - t1All) * 100 } -func calcualteALLBusy(t1, t2 []TimesStat) ([]float64, error) { +func calculateALLBusy(t1, t2 []TimesStat) ([]float64, error) { // Make sure the CPU measurements have the same length. if len(t1) != len(t2) { return nil, fmt.Errorf( @@ -63,7 +63,7 @@ func Percent(interval time.Duration, percpu bool) ([]float64, error) { return nil, err } - return calcualteALLBusy(cpuTimes1, cpuTimes2) + return calculateALLBusy(cpuTimes1, cpuTimes2) } func percentUsedFromLastCall(percpu bool) ([]float64, error) { @@ -85,6 +85,6 @@ func percentUsedFromLastCall(percpu bool) ([]float64, error) { if lastTimes == nil { return nil, fmt.Errorf("Error getting times for cpu percent. LastTimes was nil") } - return calcualteALLBusy(lastTimes, cpuTimes) + return calculateALLBusy(lastTimes, cpuTimes) } From 9dbebcf46eb3874e728ff2e3b7380fb41e577f42 Mon Sep 17 00:00:00 2001 From: Ben Aldrich Date: Wed, 1 Jun 2016 16:03:11 -0600 Subject: [PATCH 08/21] rename ALL to all --- cpu/cpu_unix.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpu/cpu_unix.go b/cpu/cpu_unix.go index 4bdf3b7..0ed7d62 100644 --- a/cpu/cpu_unix.go +++ b/cpu/cpu_unix.go @@ -26,7 +26,7 @@ func calculateBusy(t1, t2 TimesStat) float64 { return (t2Busy - t1Busy) / (t2All - t1All) * 100 } -func calculateALLBusy(t1, t2 []TimesStat) ([]float64, error) { +func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) { // Make sure the CPU measurements have the same length. if len(t1) != len(t2) { return nil, fmt.Errorf( @@ -63,7 +63,7 @@ func Percent(interval time.Duration, percpu bool) ([]float64, error) { return nil, err } - return calculateALLBusy(cpuTimes1, cpuTimes2) + return calculateAllBusy(cpuTimes1, cpuTimes2) } func percentUsedFromLastCall(percpu bool) ([]float64, error) { @@ -85,6 +85,6 @@ func percentUsedFromLastCall(percpu bool) ([]float64, error) { if lastTimes == nil { return nil, fmt.Errorf("Error getting times for cpu percent. LastTimes was nil") } - return calculateALLBusy(lastTimes, cpuTimes) + return calculateAllBusy(lastTimes, cpuTimes) } From da43049324a8b14812c2691617978d7b3b7bc1e2 Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Fri, 3 Jun 2016 11:12:08 +0100 Subject: [PATCH 09/21] Fix potential panic in linux disk IO counters Old kernels have a bug in diskstats where lines can have less than 14 fields. This applies to the kernel present in RHEL 5.2 and earlier. It's a bit of a niche but probably best to patch to be safe from future bugs too. RHEL bug case: https://bugzilla.redhat.com/show_bug.cgi?id=583285 Encountered in Telegraf: https://github.com/influxdata/telegraf/issues/1322 --- disk/disk_linux.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index f581ef7..7bdf8c9 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -283,6 +283,10 @@ func IOCounters() (map[string]IOCountersStat, error) { for _, line := range lines { fields := strings.Fields(line) + if len(fields) < 14 { + // malformed line in /proc/diskstats, avoid panic by ignoring. + continue + } name := fields[2] reads, err := strconv.ParseUint((fields[3]), 10, 64) if err != nil { From c0d118580967206d87b2d3e0adf9d36b08c6de38 Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Mon, 6 Jun 2016 22:57:17 +0900 Subject: [PATCH 10/21] [host]linux: add alpine linux to detect host.Info(). --- host/host_linux.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/host/host_linux.go b/host/host_linux.go index 97924bf..5c33cf4 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -15,7 +15,7 @@ import ( "strings" "time" - "github.com/shirou/gopsutil/internal/common" + common "github.com/shirou/gopsutil/internal/common" ) type LSB struct { @@ -256,6 +256,12 @@ func PlatformInformation() (platform string, family string, version string, err } else if common.PathExists(common.HostEtc("arch-release")) { platform = "arch" version = lsb.Release + } else if common.PathExists(common.HostEtc("alpine-release")) { + platform = "alpine" + contents, err := common.ReadLines(common.HostEtc("alpine-release")) + if err == nil && len(contents) > 0 { + version = contents[0] + } } else if lsb.ID == "RedHat" { platform = "redhat" version = lsb.Release @@ -290,6 +296,8 @@ func PlatformInformation() (platform string, family string, version string, err family = "arch" case "exherbo": family = "exherbo" + case "alpine": + family = "alpine" } return platform, family, version, nil From e9afb36ccf661e1a596d7f852bd02a4d9b71c7ae Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Mon, 6 Jun 2016 23:50:19 +0900 Subject: [PATCH 11/21] [host] linux: detect docker and rkt correctly --- host/host_linux.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/host/host_linux.go b/host/host_linux.go index 5c33cf4..ded1be3 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -359,7 +359,7 @@ func Virtualization() (string, string, error) { if common.PathExists(filename + "/capabilities") { contents, err := common.ReadLines(filename + "/capabilities") if err == nil { - if common.StringsHas(contents, "control_d") { + if common.StringsContains(contents, "control_d") { role = "host" } } @@ -387,9 +387,9 @@ func Virtualization() (string, string, error) { if common.PathExists(filename) { contents, err := common.ReadLines(filename) if err == nil { - if common.StringsHas(contents, "QEMU Virtual CPU") || - common.StringsHas(contents, "Common KVM processor") || - common.StringsHas(contents, "Common 32-bit KVM processor") { + if common.StringsContains(contents, "QEMU Virtual CPU") || + common.StringsContains(contents, "Common KVM processor") || + common.StringsContains(contents, "Common 32-bit KVM processor") { system = "kvm" role = "guest" } @@ -410,8 +410,8 @@ func Virtualization() (string, string, error) { contents, err := common.ReadLines(filename + "/self/status") if err == nil { - if common.StringsHas(contents, "s_context:") || - common.StringsHas(contents, "VxID:") { + if common.StringsContains(contents, "s_context:") || + common.StringsContains(contents, "VxID:") { system = "linux-vserver" } // TODO: guest or host @@ -421,11 +421,16 @@ func Virtualization() (string, string, error) { if common.PathExists(filename + "/self/cgroup") { contents, err := common.ReadLines(filename + "/self/cgroup") if err == nil { - if common.StringsHas(contents, "lxc") || - common.StringsHas(contents, "docker") { + if common.StringsContains(contents, "lxc") { system = "lxc" role = "guest" - } else if common.PathExists("/usr/bin/lxc-version") { // TODO: which + } else if common.StringsContains(contents, "docker") { + system = "docker" + role = "guest" + } else if common.StringsContains(contents, "machine-rkt") { + system = "rkt" + role = "guest" + } else if common.PathExists("/usr/bin/lxc-version") { system = "lxc" role = "host" } From 53406b283244bfe6ebf41a1c4b38b4fac57e6201 Mon Sep 17 00:00:00 2001 From: WAKAYAMA Shirou Date: Sun, 12 Jun 2016 23:20:51 +0900 Subject: [PATCH 12/21] [host]Windows: BootTime and Uptime are reversed on Windows. --- host/host_linux_ppc64le.go | 2 ++ host/host_test.go | 13 +++++++++++++ host/host_windows.go | 14 +++++++------- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/host/host_linux_ppc64le.go b/host/host_linux_ppc64le.go index 37dbe5c..d081a08 100644 --- a/host/host_linux_ppc64le.go +++ b/host/host_linux_ppc64le.go @@ -1,3 +1,5 @@ +// +build linux +// +build ppc64le // Created by cgo -godefs - DO NOT EDIT // cgo -godefs types_linux.go diff --git a/host/host_test.go b/host/host_test.go index 2bf395c..526f0ec 100644 --- a/host/host_test.go +++ b/host/host_test.go @@ -16,6 +16,16 @@ func TestHostInfo(t *testing.T) { } } +func TestUptime(t *testing.T) { + v, err := Uptime() + if err != nil { + t.Errorf("error %v", err) + } + if v == 0 { + t.Errorf("Could not get up time %v", v) + } +} + func TestBoot_time(t *testing.T) { v, err := BootTime() if err != nil { @@ -24,6 +34,9 @@ func TestBoot_time(t *testing.T) { if v == 0 { t.Errorf("Could not get boot time %v", v) } + if v < 946652400 { + t.Errorf("Invalid Boottime, older than 2000-01-01") + } } func TestUsers(t *testing.T) { diff --git a/host/host_windows.go b/host/host_windows.go index 29f900c..18062df 100644 --- a/host/host_windows.go +++ b/host/host_windows.go @@ -50,7 +50,7 @@ func Info() (*InfoStat, error) { boot, err := BootTime() if err == nil { ret.BootTime = boot - ret.Uptime = uptime(boot) + ret.Uptime, _ = Uptime() } procs, err := process.Pids() @@ -76,7 +76,7 @@ func GetOSInfo() (Win32_OperatingSystem, error) { return dst[0], nil } -func BootTime() (uint64, error) { +func Uptime() (uint64, error) { if osInfo == nil { _, err := GetOSInfo() if err != nil { @@ -88,16 +88,16 @@ func BootTime() (uint64, error) { return uint64(now.Sub(t).Seconds()), nil } -func uptime(boot uint64) uint64 { - return uint64(time.Now().Unix()) - boot +func bootTime(up uint64) uint64 { + return uint64(time.Now().Unix()) - up } -func Uptime() (uint64, error) { - boot, err := BootTime() +func BootTime() (uint64, error) { + up, err := Uptime() if err != nil { return 0, err } - return uptime(boot), nil + return bootTime(up), nil } func PlatformInformation() (platform string, family string, version string, err error) { From c916da25023b8832ff9590ceb7eeefdb26e1656c Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Mon, 13 Jun 2016 16:17:40 +0900 Subject: [PATCH 13/21] [process]linux: add linux_arm64.go explicitly --- process/process_linux_arm64.go | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 process/process_linux_arm64.go diff --git a/process/process_linux_arm64.go b/process/process_linux_arm64.go new file mode 100644 index 0000000..529aeaa --- /dev/null +++ b/process/process_linux_arm64.go @@ -0,0 +1,9 @@ +// +build linux +// +build arm64 + +package process + +const ( + ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK) + PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE) +) From dd9b50926cadc8ce84e8436d628018c7978b076c Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Mon, 13 Jun 2016 17:22:02 +0900 Subject: [PATCH 14/21] [host]linux: add coreos to Info() and Virtualization(). --- host/host_linux.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/host/host_linux.go b/host/host_linux.go index ded1be3..b56cc98 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -136,6 +136,26 @@ func Users() ([]UserStat, error) { } +func getOSRelease() (platform string, version string, err error) { + contents, err := common.ReadLines(common.HostEtc("os-release")) + if err != nil { + return "", "", nil // return empty + } + for _, line := range contents { + field := strings.Split(line, "=") + if len(field) < 2 { + continue + } + switch field[0] { + case "ID": // use ID for lowercase + platform = field[1] + case "VERSION": + version = field[1] + } + } + return platform, version, nil +} + func getLSB() (*LSB, error) { ret := &LSB{} if common.PathExists(common.HostEtc("lsb-release")) { @@ -262,6 +282,12 @@ func PlatformInformation() (platform string, family string, version string, err if err == nil && len(contents) > 0 { version = contents[0] } + } else if common.PathExists(common.HostEtc("os-release")) { + p, v, err := getOSRelease() + if err == nil { + platform = p + version = v + } } else if lsb.ID == "RedHat" { platform = "redhat" version = lsb.Release @@ -298,6 +324,8 @@ func PlatformInformation() (platform string, family string, version string, err family = "exherbo" case "alpine": family = "alpine" + case "coreos": + family = "coreos" } return platform, family, version, nil @@ -437,5 +465,12 @@ func Virtualization() (string, string, error) { } } + if common.PathExists(common.HostEtc("os-release")) { + p, _, err := getOSRelease() + if err == nil && p == "coreos" { + system = "rkt" // Is it true? + role = "host" + } + } return system, role, nil } From 70cbab704544bfd8710c8c6ccee680a159e233d9 Mon Sep 17 00:00:00 2001 From: Zdenek Styblik Date: Mon, 13 Jun 2016 11:11:12 +0200 Subject: [PATCH 15/21] [disk]linux: add documentation for GetDiskSerialNumber() Commit adds documentation for GetDiskSerialNumber(), because it wasn't clear what was expected as a parameter. Fixes #212 --- disk/disk_linux.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index 7bdf8c9..757becb 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -336,6 +336,8 @@ func IOCounters() (map[string]IOCountersStat, error) { return ret, nil } +// GetDiskSerialNumber returns Serial Number of given device or empty string +// on error. Name of device is expected, eg. /dev/sda func GetDiskSerialNumber(name string) string { n := fmt.Sprintf("--name=%s", name) udevadm, err := exec.LookPath("/sbin/udevadm") From 480ccb89e1ffa7bcaa10d3f8a62b08b72b77495b Mon Sep 17 00:00:00 2001 From: sandlbn Date: Mon, 13 Jun 2016 13:27:25 +0100 Subject: [PATCH 16/21] Added support for linux net interface fifo errors --- net/net.go | 13 ++++++++----- net/net_linux.go | 10 ++++++++++ net/net_test.go | 2 +- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/net/net.go b/net/net.go index 60f1069..4d120dc 100644 --- a/net/net.go +++ b/net/net.go @@ -18,15 +18,18 @@ func init() { } type IOCountersStat struct { - Name string `json:"name"` // interface name + Name string `json:"name"` // interface name BytesSent uint64 `json:"bytesSent"` // number of bytes sent BytesRecv uint64 `json:"bytesRecv"` // number of bytes received PacketsSent uint64 `json:"packetsSent"` // number of packets sent PacketsRecv uint64 `json:"packetsRecv"` // number of packets received - Errin uint64 `json:"errin"` // total number of errors while receiving - Errout uint64 `json:"errout"` // total number of errors while sending - Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped - Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD) + Errin uint64 `json:"errin"` // total number of errors while receiving + Errout uint64 `json:"errout"` // total number of errors while sending + Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped + Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD) + Fifoin uint64 `json:"fifoin"` // total number of FIFO buffers errors while receiving + Fifoout uint64 `json:"fifoout"` // total number of FIFO buffers errors while sending + } // Addr is implemented compatibility to psutil diff --git a/net/net_linux.go b/net/net_linux.go index e024771..0803d8e 100644 --- a/net/net_linux.go +++ b/net/net_linux.go @@ -63,6 +63,10 @@ func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { if err != nil { return ret, err } + fifoIn, err := strconv.ParseUint(fields[4], 10, 64) + if err != nil { + return ret, err + } bytesSent, err := strconv.ParseUint(fields[8], 10, 64) if err != nil { return ret, err @@ -79,6 +83,10 @@ func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { if err != nil { return ret, err } + fifoOut, err := strconv.ParseUint(fields[14], 10, 64) + if err != nil { + return ret, err + } nic := IOCountersStat{ Name: interfaceName, @@ -86,10 +94,12 @@ func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { PacketsRecv: packetsRecv, Errin: errIn, Dropin: dropIn, + Fifoin: fifoIn, BytesSent: bytesSent, PacketsSent: packetsSent, Errout: errOut, Dropout: dropOut, + Fifoout: fifoOut, } ret = append(ret, nic) } diff --git a/net/net_test.go b/net/net_test.go index 88748be..9ec87ad 100644 --- a/net/net_test.go +++ b/net/net_test.go @@ -23,7 +23,7 @@ func TestNetIOCountersStatString(t *testing.T) { Name: "test", BytesSent: 100, } - e := `{"name":"test","bytesSent":100,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0}` + e := `{"name":"test","bytesSent":100,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0}` if e != fmt.Sprintf("%v", v) { t.Errorf("NetIOCountersStat string is invalid: %v", v) } From c184d5359e78490bf6539767b618fc1abfa9734c Mon Sep 17 00:00:00 2001 From: Zdenek Styblik Date: Tue, 14 Jun 2016 11:06:54 +0200 Subject: [PATCH 17/21] Fix typos in README.rst Commit fixes some typos in README.rst --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 9ee9453..9766956 100644 --- a/README.rst +++ b/README.rst @@ -22,7 +22,7 @@ psutil functions on some architectures. Migrating to v2 ------------------------- -On gopsutil itself, `v2migration.sh `_ is used for migration. It can not be commly used, but it may help to your migration. +On gopsutil itself, `v2migration.sh `_ is used for migration. It can not be commonly used, but it may help you with migration. Available Architectures @@ -79,7 +79,7 @@ Documentation see http://godoc.org/github.com/shirou/gopsutil -Requrement +Requirements ----------------- - go1.5 or above is required. @@ -153,7 +153,7 @@ Current Status ------------------ - x: work -- b: almost work but something broken +- b: almost works, but something is broken ================= ====== ======= ====== ======= name Linux FreeBSD MacOSX Windows From 606680edc5486977c85fa90eca9e9d6becf74fa6 Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Thu, 16 Jun 2016 19:25:27 -0700 Subject: [PATCH 18/21] Improve the accuracy of CPU detection on FreeBSD. --- cpu/cpu_freebsd.go | 55 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/cpu/cpu_freebsd.go b/cpu/cpu_freebsd.go index bb949a4..9976024 100644 --- a/cpu/cpu_freebsd.go +++ b/cpu/cpu_freebsd.go @@ -97,29 +97,46 @@ func Times(percpu bool) ([]TimesStat, error) { return ret, nil } -// Returns only one CPUInfoStat on FreeBSD +// Returns only one InfoStat on FreeBSD. The information regarding core +// count, however is accurate and it is assumed that all InfoStat attributes +// are the same across CPUs. func Info() ([]InfoStat, error) { - filename := "/var/run/dmesg.boot" - lines, _ := common.ReadLines(filename) - - var ret []InfoStat + const dmesgBoot = "/var/run/dmesg.boot" + lines, _ := common.ReadLines(dmesgBoot) c := InfoStat{} + var vals []string + var err error + if vals, err = common.DoSysctrl("hw.clockrate"); err != nil { + return nil, err + } + if c.Mhz, err = strconv.ParseFloat(vals[0], 64); err != nil { + return nil, fmt.Errorf("Unable to parse FreeBSD CPU clock rate: %v", err) + } + c.CPU = int32(c.Mhz) + + if vals, err = common.DoSysctrl("hw.ncpu"); err != nil { + return nil, err + } + var i64 int64 + if i64, err = strconv.ParseInt(vals[0], 10, 32); err != nil { + return nil, fmt.Errorf("Unable to parse FreeBSD cores: %v", err) + } + c.Cores = int32(i64) + + if vals, err = common.DoSysctrl("hw.model"); err != nil { + return nil, err + } + c.ModelName = vals[0] + for _, line := range lines { - if matches := regexp.MustCompile(`CPU:\s+(.+) \(([\d.]+).+\)`).FindStringSubmatch(line); matches != nil { - c.ModelName = matches[1] - t, err := strconv.ParseFloat(matches[2], 64) - if err != nil { - return ret, nil - } - c.Mhz = t - } else if matches := regexp.MustCompile(`Origin = "(.+)" Id = (.+) Family = (.+) Model = (.+) Stepping = (.+)`).FindStringSubmatch(line); matches != nil { + if matches := regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Family\s*=\s*(.+)\s+Model\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`).FindStringSubmatch(line); matches != nil { c.VendorID = matches[1] c.Family = matches[3] c.Model = matches[4] t, err := strconv.ParseInt(matches[5], 10, 32) if err != nil { - return ret, nil + return nil, fmt.Errorf("Unable to parse FreeBSD CPU stepping information from %q: %v", line, err) } c.Stepping = int32(t) } else if matches := regexp.MustCompile(`Features=.+<(.+)>`).FindStringSubmatch(line); matches != nil { @@ -130,16 +147,8 @@ func Info() ([]InfoStat, error) { for _, v := range strings.Split(matches[1], ",") { c.Flags = append(c.Flags, strings.ToLower(v)) } - } else if matches := regexp.MustCompile(`Logical CPUs per core: (\d+)`).FindStringSubmatch(line); matches != nil { - // FIXME: no this line? - t, err := strconv.ParseInt(matches[1], 10, 32) - if err != nil { - return ret, nil - } - c.Cores = int32(t) } - } - return append(ret, c), nil + return []InfoStat{c}, nil } From 14b2ce8b817e459a4e74b0591ff375650a0f9f44 Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Thu, 16 Jun 2016 19:32:49 -0700 Subject: [PATCH 19/21] Don't be lossy re: CPU model number. --- cpu/cpu_freebsd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpu/cpu_freebsd.go b/cpu/cpu_freebsd.go index 9976024..a536e50 100644 --- a/cpu/cpu_freebsd.go +++ b/cpu/cpu_freebsd.go @@ -127,7 +127,7 @@ func Info() ([]InfoStat, error) { if vals, err = common.DoSysctrl("hw.model"); err != nil { return nil, err } - c.ModelName = vals[0] + c.ModelName = strings.Join(vals, " ") for _, line := range lines { if matches := regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Family\s*=\s*(.+)\s+Model\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`).FindStringSubmatch(line); matches != nil { From af4c8608c91805b2fd91a50068c3739067ee9eb2 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Wed, 22 Jun 2016 16:22:37 -0700 Subject: [PATCH 20/21] Casting the values of working set and page file to uint64 --- process/process_windows.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/process/process_windows.go b/process/process_windows.go index c3d2837..8982a95 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -257,8 +257,8 @@ func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { } ret := &MemoryInfoStat{ - RSS: mem.WorkingSetSize, - VMS: mem.PagefileUsage, + RSS: uint64(mem.WorkingSetSize), + VMS: uint64(mem.PagefileUsage), } return ret, nil From 2728d81cdef579d6ed88d2fd5c71e85aa83eaba8 Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Sat, 25 Jun 2016 18:45:55 +0900 Subject: [PATCH 21/21] copied from golang LICENSE. --- LICENSE | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/LICENSE b/LICENSE index 602b2c0..da71a5e 100644 --- a/LICENSE +++ b/LICENSE @@ -25,3 +25,37 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +------- +internal/common/binary.go in the gopsutil is copied and modifid from golang/encoding/binary.go. + + + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file