Merge branch 'master' of github.com:shirou/gopsutil

pull/228/head
Sean Chittenden 9 years ago
commit fcd296ea11
No known key found for this signature in database
GPG Key ID: 4EBC9DC16C2E5E16

@ -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 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 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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.

@ -22,7 +22,7 @@ psutil functions on some architectures.
Migrating to v2 Migrating to v2
------------------------- -------------------------
On gopsutil itself, `v2migration.sh <https://github.com/shirou/gopsutil/blob/v2/v2migration.sh>`_ is used for migration. It can not be commly used, but it may help to your migration. On gopsutil itself, `v2migration.sh <https://github.com/shirou/gopsutil/blob/v2/v2migration.sh>`_ is used for migration. It can not be commonly used, but it may help you with migration.
Available Architectures Available Architectures
@ -79,7 +79,7 @@ Documentation
see http://godoc.org/github.com/shirou/gopsutil see http://godoc.org/github.com/shirou/gopsutil
Requrement Requirements
----------------- -----------------
- go1.5 or above is required. - go1.5 or above is required.
@ -153,7 +153,7 @@ Current Status
------------------ ------------------
- x: work - x: work
- b: almost work but something broken - b: almost works, but something is broken
================= ====== ======= ====== ======= ================= ====== ======= ====== =======
name Linux FreeBSD MacOSX Windows name Linux FreeBSD MacOSX Windows
@ -194,13 +194,13 @@ exe x x x
uids x x x uids x x x
gids x x x gids x x x
terminal x x x terminal x x x
io_counters x x io_counters x x x
nice x x x x nice x x x x
num_fds x num_fds x
num_ctx_switches x num_ctx_switches x
num_threads x x x x num_threads x x x x
cpu_times x cpu_times x
memory_info x x x memory_info x x x x
memory_info_ex x memory_info_ex x
memory_maps x memory_maps x
open_files x open_files x

@ -5,6 +5,9 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/shirou/gopsutil/internal/common"
) )
type TimesStat struct { type TimesStat struct {
@ -37,8 +40,22 @@ type InfoStat struct {
Flags []string `json:"flags"` Flags []string `json:"flags"`
} }
var lastCPUTimes []TimesStat type lastPercent struct {
var lastPerCPUTimes []TimesStat sync.Mutex
lastCPUTimes []TimesStat
lastPerCPUTimes []TimesStat
}
var lastCPUPercent lastPercent
var invoke common.Invoker
func init() {
invoke = common.Invoke{}
lastCPUPercent.Lock()
lastCPUPercent.lastCPUTimes, _ = Times(false)
lastCPUPercent.lastPerCPUTimes, _ = Times(true)
lastCPUPercent.Unlock()
}
func Counts(logical bool) (int, error) { func Counts(logical bool) (int, error) {
return runtime.NumCPU(), nil return runtime.NumCPU(), nil

@ -36,7 +36,7 @@ func Info() ([]InfoStat, error) {
if err != nil { if err != nil {
return ret, err return ret, err
} }
out, err := exec.Command(sysctl, "machdep.cpu").Output() out, err := invoke.Command(sysctl, "machdep.cpu")
if err != nil { if err != nil {
return ret, err 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 // Use the rated frequency of the CPU. This is a static value and does not
// account for low power or Turbo Boost modes. // 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 { if err != nil {
return ret, err return ret, err
} }

@ -27,7 +27,7 @@ func init() {
if err != nil { if err != nil {
return return
} }
out, err := exec.Command(getconf, "CLK_TCK").Output() out, err := invoke.Command(getconf, "CLK_TCK")
// ignore errors // ignore errors
if err == nil { if err == nil {
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
@ -97,29 +97,46 @@ func Times(percpu bool) ([]TimesStat, error) {
return ret, nil 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) { func Info() ([]InfoStat, error) {
filename := "/var/run/dmesg.boot" const dmesgBoot = "/var/run/dmesg.boot"
lines, _ := common.ReadLines(filename) lines, _ := common.ReadLines(dmesgBoot)
var ret []InfoStat
c := InfoStat{} 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 = strings.Join(vals, " ")
for _, line := range lines { for _, line := range lines {
if matches := regexp.MustCompile(`CPU:\s+(.+) \(([\d.]+).+\)`).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.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 {
c.VendorID = matches[1] c.VendorID = matches[1]
c.Family = matches[3] c.Family = matches[3]
c.Model = matches[4] c.Model = matches[4]
t, err := strconv.ParseInt(matches[5], 10, 32) t, err := strconv.ParseInt(matches[5], 10, 32)
if err != nil { 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) c.Stepping = int32(t)
} else if matches := regexp.MustCompile(`Features=.+<(.+)>`).FindStringSubmatch(line); matches != nil { } 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], ",") { for _, v := range strings.Split(matches[1], ",") {
c.Flags = append(c.Flags, strings.ToLower(v)) 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
} }

@ -19,7 +19,7 @@ func init() {
if err != nil { if err != nil {
return return
} }
out, err := exec.Command(getconf, "CLK_TCK").Output() out, err := invoke.Command(getconf, "CLK_TCK")
// ignore errors // ignore errors
if err == nil { if err == nil {
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)

@ -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) { func TestCPUPercent(t *testing.T) {
testCPUPercent(t, false) testCPUPercent(t, false)
} }
@ -101,3 +135,11 @@ func TestCPUPercent(t *testing.T) {
func TestCPUPercentPerCpu(t *testing.T) { func TestCPUPercentPerCpu(t *testing.T) {
testCPUPercent(t, true) testCPUPercent(t, true)
} }
func TestCPUPercentIntervalZero(t *testing.T) {
testCPUPercentLastUsed(t, false)
}
func TestCPUPercentIntervalZeroPerCPU(t *testing.T) {
testCPUPercentLastUsed(t, true)
}

@ -7,24 +7,46 @@ import (
"time" "time"
) )
func Percent(interval time.Duration, percpu bool) ([]float64, error) { func getAllBusy(t TimesStat) (float64, float64) {
getAllBusy := func(t TimesStat) (float64, float64) { busy := t.User + t.System + t.Nice + t.Iowait + t.Irq +
busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + t.Softirq + t.Steal + t.Guest + t.GuestNice + t.Stolen
t.Softirq + t.Steal + t.Guest + t.GuestNice + t.Stolen return busy + t.Idle, busy
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 calculateAllBusy(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 { ret := make([]float64, len(t1))
t1All, t1Busy := getAllBusy(t1) for i, t := range t2 {
t2All, t2Busy := getAllBusy(t2) ret[i] = calculateBusy(t1[i], t)
}
return ret, nil
}
if t2Busy <= t1Busy { //Percent calculates the percentage of cpu used either per CPU or combined.
return 0 //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 t2All <= t1All { if interval <= 0 {
return 1 return percentUsedFromLastCall(percpu)
}
return (t2Busy - t1Busy) / (t2All - t1All) * 100
} }
// Get CPU usage at the start of the interval. // 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 return nil, err
} }
if interval > 0 { time.Sleep(interval)
time.Sleep(interval)
}
// And at the end of the interval. // And at the end of the interval.
cpuTimes2, err := Times(percpu) cpuTimes2, err := Times(percpu)
@ -43,17 +63,28 @@ func Percent(interval time.Duration, percpu bool) ([]float64, error) {
return nil, err return nil, err
} }
// Make sure the CPU measurements have the same length. return calculateAllBusy(cpuTimes1, cpuTimes2)
if len(cpuTimes1) != len(cpuTimes2) { }
return nil, fmt.Errorf(
"received two CPU counts: %d != %d", func percentUsedFromLastCall(percpu bool) ([]float64, error) {
len(cpuTimes1), len(cpuTimes2), 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)) if lastTimes == nil {
for i, t := range cpuTimes2 { return nil, fmt.Errorf("Error getting times for cpu percent. LastTimes was nil")
ret[i] = calculate(cpuTimes1[i], t)
} }
return ret, nil return calculateAllBusy(lastTimes, cpuTimes)
} }

@ -2,8 +2,16 @@ package disk
import ( import (
"encoding/json" "encoding/json"
"github.com/shirou/gopsutil/internal/common"
) )
var invoke common.Invoker
func init() {
invoke = common.Invoke{}
}
type UsageStat struct { type UsageStat struct {
Path string `json:"path"` Path string `json:"path"`
Fstype string `json:"fstype"` Fstype string `json:"fstype"`

@ -283,6 +283,10 @@ func IOCounters() (map[string]IOCountersStat, error) {
for _, line := range lines { for _, line := range lines {
fields := strings.Fields(line) fields := strings.Fields(line)
if len(fields) < 14 {
// malformed line in /proc/diskstats, avoid panic by ignoring.
continue
}
name := fields[2] name := fields[2]
reads, err := strconv.ParseUint((fields[3]), 10, 64) reads, err := strconv.ParseUint((fields[3]), 10, 64)
if err != nil { if err != nil {
@ -332,6 +336,8 @@ func IOCounters() (map[string]IOCountersStat, error) {
return ret, nil 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 { func GetDiskSerialNumber(name string) string {
n := fmt.Sprintf("--name=%s", name) n := fmt.Sprintf("--name=%s", name)
udevadm, err := exec.LookPath("/sbin/udevadm") udevadm, err := exec.LookPath("/sbin/udevadm")
@ -339,7 +345,7 @@ func GetDiskSerialNumber(name string) string {
return "" 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 // does not return error, just an empty string
if err != nil { if err != nil {

@ -1,10 +1,20 @@
package docker package docker
import "errors" import (
"errors"
"github.com/shirou/gopsutil/internal/common"
)
var ErrDockerNotAvailable = errors.New("docker not available") var ErrDockerNotAvailable = errors.New("docker not available")
var ErrCgroupNotAvailable = errors.New("cgroup not available") var ErrCgroupNotAvailable = errors.New("cgroup not available")
var invoke common.Invoker
func init() {
invoke = common.Invoke{}
}
type CgroupMemStat struct { type CgroupMemStat struct {
ContainerID string `json:"containerID"` ContainerID string `json:"containerID"`
Cache uint64 `json:"cache"` Cache uint64 `json:"cache"`

@ -23,7 +23,7 @@ func GetDockerStat() ([]CgroupDockerStat, error) {
return nil, ErrDockerNotAvailable 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 { if err != nil {
return []CgroupDockerStat{}, err return []CgroupDockerStat{}, err
} }
@ -65,7 +65,7 @@ func GetDockerIDList() ([]string, error) {
return nil, ErrDockerNotAvailable 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 { if err != nil {
return []string{}, err return []string{}, err
} }

@ -2,17 +2,25 @@ package host
import ( import (
"encoding/json" "encoding/json"
"github.com/shirou/gopsutil/internal/common"
) )
var invoke common.Invoker
func init() {
invoke = common.Invoke{}
}
// A HostInfoStat describes the host status. // A HostInfoStat describes the host status.
// This is not in the psutil but it useful. // This is not in the psutil but it useful.
type InfoStat struct { type InfoStat struct {
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
Uptime uint64 `json:"uptime"` Uptime uint64 `json:"uptime"`
BootTime uint64 `json:"bootTime"` BootTime uint64 `json:"bootTime"`
Procs uint64 `json:"procs"` // number of processes Procs uint64 `json:"procs"` // number of processes
OS string `json:"os"` // ex: freebsd, linux OS string `json:"os"` // ex: freebsd, linux
Platform string `json:"platform"` // ex: ubuntu, linuxmint Platform string `json:"platform"` // ex: ubuntu, linuxmint
PlatformFamily string `json:"platformFamily"` // ex: debian, rhel PlatformFamily string `json:"platformFamily"` // ex: debian, rhel
PlatformVersion string `json:"platformVersion"` PlatformVersion string `json:"platformVersion"`
VirtualizationSystem string `json:"virtualizationSystem"` VirtualizationSystem string `json:"virtualizationSystem"`

@ -138,12 +138,12 @@ func PlatformInformation() (string, string, string, error) {
if err != nil { if err != nil {
return "", "", "", err return "", "", "", err
} }
out, err := exec.Command(uname, "-s").Output() out, err := invoke.Command(uname, "-s")
if err == nil { if err == nil {
platform = strings.ToLower(strings.TrimSpace(string(out))) platform = strings.ToLower(strings.TrimSpace(string(out)))
} }
out, err = exec.Command(uname, "-r").Output() out, err = invoke.Command(uname, "-r")
if err == nil { if err == nil {
version = strings.ToLower(strings.TrimSpace(string(out))) version = strings.ToLower(strings.TrimSpace(string(out)))
} }

@ -143,12 +143,12 @@ func PlatformInformation() (string, string, string, error) {
return "", "", "", err return "", "", "", err
} }
out, err := exec.Command(uname, "-s").Output() out, err := invoke.Command(uname, "-s")
if err == nil { if err == nil {
platform = strings.ToLower(strings.TrimSpace(string(out))) platform = strings.ToLower(strings.TrimSpace(string(out)))
} }
out, err = exec.Command(uname, "-r").Output() out, err = invoke.Command(uname, "-r")
if err == nil { if err == nil {
version = strings.ToLower(strings.TrimSpace(string(out))) version = strings.ToLower(strings.TrimSpace(string(out)))
} }

@ -16,6 +16,7 @@ import (
"time" "time"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
common "github.com/shirou/gopsutil/internal/common"
"github.com/shirou/gopsutil/process" "github.com/shirou/gopsutil/process"
) )
@ -144,6 +145,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) { func getLSB() (*LSB, error) {
ret := &LSB{} ret := &LSB{}
if common.PathExists(common.HostEtc("lsb-release")) { if common.PathExists(common.HostEtc("lsb-release")) {
@ -172,7 +193,7 @@ func getLSB() (*LSB, error) {
if err != nil { if err != nil {
return ret, err return ret, err
} }
out, err := exec.Command(lsb_release).Output() out, err := invoke.Command(lsb_release)
if err != nil { if err != nil {
return ret, err return ret, err
} }
@ -264,6 +285,18 @@ func PlatformInformation() (platform string, family string, version string, err
} else if common.PathExists(common.HostEtc("arch-release")) { } else if common.PathExists(common.HostEtc("arch-release")) {
platform = "arch" platform = "arch"
version = lsb.Release 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 common.PathExists(common.HostEtc("os-release")) {
p, v, err := getOSRelease()
if err == nil {
platform = p
version = v
}
} else if lsb.ID == "RedHat" { } else if lsb.ID == "RedHat" {
platform = "redhat" platform = "redhat"
version = lsb.Release version = lsb.Release
@ -298,6 +331,10 @@ func PlatformInformation() (platform string, family string, version string, err
family = "arch" family = "arch"
case "exherbo": case "exherbo":
family = "exherbo" family = "exherbo"
case "alpine":
family = "alpine"
case "coreos":
family = "coreos"
} }
return platform, family, version, nil return platform, family, version, nil
@ -359,7 +396,7 @@ func Virtualization() (string, string, error) {
if common.PathExists(filename + "/capabilities") { if common.PathExists(filename + "/capabilities") {
contents, err := common.ReadLines(filename + "/capabilities") contents, err := common.ReadLines(filename + "/capabilities")
if err == nil { if err == nil {
if common.StringsHas(contents, "control_d") { if common.StringsContains(contents, "control_d") {
role = "host" role = "host"
} }
} }
@ -387,9 +424,9 @@ func Virtualization() (string, string, error) {
if common.PathExists(filename) { if common.PathExists(filename) {
contents, err := common.ReadLines(filename) contents, err := common.ReadLines(filename)
if err == nil { if err == nil {
if common.StringsHas(contents, "QEMU Virtual CPU") || if common.StringsContains(contents, "QEMU Virtual CPU") ||
common.StringsHas(contents, "Common KVM processor") || common.StringsContains(contents, "Common KVM processor") ||
common.StringsHas(contents, "Common 32-bit KVM processor") { common.StringsContains(contents, "Common 32-bit KVM processor") {
system = "kvm" system = "kvm"
role = "guest" role = "guest"
} }
@ -410,8 +447,8 @@ func Virtualization() (string, string, error) {
contents, err := common.ReadLines(filename + "/self/status") contents, err := common.ReadLines(filename + "/self/status")
if err == nil { if err == nil {
if common.StringsHas(contents, "s_context:") || if common.StringsContains(contents, "s_context:") ||
common.StringsHas(contents, "VxID:") { common.StringsContains(contents, "VxID:") {
system = "linux-vserver" system = "linux-vserver"
} }
// TODO: guest or host // TODO: guest or host
@ -421,16 +458,28 @@ func Virtualization() (string, string, error) {
if common.PathExists(filename + "/self/cgroup") { if common.PathExists(filename + "/self/cgroup") {
contents, err := common.ReadLines(filename + "/self/cgroup") contents, err := common.ReadLines(filename + "/self/cgroup")
if err == nil { if err == nil {
if common.StringsHas(contents, "lxc") || if common.StringsContains(contents, "lxc") {
common.StringsHas(contents, "docker") {
system = "lxc" system = "lxc"
role = "guest" 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" system = "lxc"
role = "host" role = "host"
} }
} }
} }
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 return system, role, nil
} }

@ -1,3 +1,5 @@
// +build linux
// +build ppc64le
// Created by cgo -godefs - DO NOT EDIT // Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go // cgo -godefs types_linux.go

@ -19,6 +19,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) { func TestBoot_time(t *testing.T) {
v, err := BootTime() v, err := BootTime()
if err != nil { if err != nil {
@ -27,6 +37,9 @@ func TestBoot_time(t *testing.T) {
if v == 0 { if v == 0 {
t.Errorf("Could not get boot time %v", v) 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) { func TestUsers(t *testing.T) {

@ -50,7 +50,7 @@ func Info() (*InfoStat, error) {
boot, err := BootTime() boot, err := BootTime()
if err == nil { if err == nil {
ret.BootTime = boot ret.BootTime = boot
ret.Uptime = uptime(boot) ret.Uptime, _ = Uptime()
} }
procs, err := process.Pids() procs, err := process.Pids()
@ -74,7 +74,7 @@ func GetOSInfo() (Win32_OperatingSystem, error) {
return dst[0], nil return dst[0], nil
} }
func BootTime() (uint64, error) { func Uptime() (uint64, error) {
if osInfo == nil { if osInfo == nil {
_, err := GetOSInfo() _, err := GetOSInfo()
if err != nil { if err != nil {
@ -86,16 +86,16 @@ func BootTime() (uint64, error) {
return uint64(now.Sub(t).Seconds()), nil return uint64(now.Sub(t).Seconds()), nil
} }
func uptime(boot uint64) uint64 { func bootTime(up uint64) uint64 {
return uint64(time.Now().Unix()) - boot return uint64(time.Now().Unix()) - up
} }
func Uptime() (uint64, error) { func BootTime() (uint64, error) {
boot, err := BootTime() up, err := Uptime()
if err != nil { if err != nil {
return 0, err return 0, err
} }
return uptime(boot), nil return bootTime(up), nil
} }
func PlatformInformation() (platform string, family string, version string, err error) { func PlatformInformation() (platform string, family string, version string, err error) {

@ -8,8 +8,10 @@ package common
// - windows (amd64) // - windows (amd64)
import ( import (
"bufio" "bufio"
"bytes"
"errors" "errors"
"io/ioutil" "io/ioutil"
"log"
"net/url" "net/url"
"os" "os"
"os/exec" "os/exec"
@ -19,6 +21,12 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"time"
)
var (
Timeout = 3 * time.Second
TimeoutErr = errors.New("Command timed out.")
) )
type Invoker interface { type Invoker interface {
@ -28,7 +36,8 @@ type Invoker interface {
type Invoke struct{} type Invoke struct{}
func (i Invoke) Command(name string, arg ...string) ([]byte, error) { 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 { type FakeInvoke struct {
@ -118,20 +127,20 @@ func IntToString(orig []int8) string {
} }
func UintToString(orig []uint8) string { func UintToString(orig []uint8) string {
ret := make([]byte, len(orig)) ret := make([]byte, len(orig))
size := -1 size := -1
for i, o := range orig { for i, o := range orig {
if o == 0 { if o == 0 {
size = i size = i
break break
} }
ret[i] = byte(o) ret[i] = byte(o)
} }
if size == -1 { if size == -1 {
size = len(orig) size = len(orig)
} }
return string(ret[0:size]) return string(ret[0:size])
} }
func ByteToString(orig []byte) string { func ByteToString(orig []byte) string {
@ -294,3 +303,41 @@ func HostSys(combineWith ...string) string {
func HostEtc(combineWith ...string) string { func HostEtc(combineWith ...string) string {
return GetEnv("HOST_ETC", "/etc", combineWith...) 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
}
}

@ -2,8 +2,16 @@ package mem
import ( import (
"encoding/json" "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 // Memory usage statistics. Total, Available and Used contain numbers of bytes
// for human consumption. // for human consumption.
// //

@ -16,7 +16,7 @@ func getVMStat(vms *VirtualMemoryStat) error {
if err != nil { if err != nil {
return err return err
} }
out, err := exec.Command(vm_stat).Output() out, err := invoke.Command(vm_stat)
if err != nil { if err != nil {
return err return err
} }

@ -3,7 +3,6 @@
package mem package mem
import ( import (
"os/exec"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
@ -15,7 +14,7 @@ func TestVirtualMemoryDarwin(t *testing.T) {
v, err := VirtualMemory() v, err := VirtualMemory()
assert.Nil(t, err) 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) assert.Nil(t, err)
outString := string(outBytes) outString := string(outBytes)
outString = strings.TrimSpace(outString) outString = strings.TrimSpace(outString)

@ -93,7 +93,7 @@ func SwapMemory() (*SwapMemoryStat, error) {
return nil, err return nil, err
} }
out, err := exec.Command(swapinfo).Output() out, err := invoke.Command(swapinfo)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -18,15 +18,18 @@ func init() {
} }
type IOCountersStat struct { type IOCountersStat struct {
Name string `json:"name"` // interface name Name string `json:"name"` // interface name
BytesSent uint64 `json:"bytesSent"` // number of bytes sent BytesSent uint64 `json:"bytesSent"` // number of bytes sent
BytesRecv uint64 `json:"bytesRecv"` // number of bytes received BytesRecv uint64 `json:"bytesRecv"` // number of bytes received
PacketsSent uint64 `json:"packetsSent"` // number of packets sent PacketsSent uint64 `json:"packetsSent"` // number of packets sent
PacketsRecv uint64 `json:"packetsRecv"` // number of packets received PacketsRecv uint64 `json:"packetsRecv"` // number of packets received
Errin uint64 `json:"errin"` // total number of errors while receiving Errin uint64 `json:"errin"` // total number of errors while receiving
Errout uint64 `json:"errout"` // total number of errors while sending Errout uint64 `json:"errout"` // total number of errors while sending
Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped 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) 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 // Addr is implemented compatibility to psutil

@ -21,7 +21,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
out, err := exec.Command(netstat, "-ibdnW").Output() out, err := invoke.Command(netstat, "-ibdnW")
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -16,7 +16,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
out, err := exec.Command(netstat, "-ibdnW").Output() out, err := invoke.Command(netstat, "-ibdnW")
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -63,6 +63,10 @@ func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
if err != nil { if err != nil {
return ret, err 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) bytesSent, err := strconv.ParseUint(fields[8], 10, 64)
if err != nil { if err != nil {
return ret, err return ret, err
@ -79,6 +83,10 @@ func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
if err != nil { if err != nil {
return ret, err return ret, err
} }
fifoOut, err := strconv.ParseUint(fields[14], 10, 64)
if err != nil {
return ret, err
}
nic := IOCountersStat{ nic := IOCountersStat{
Name: interfaceName, Name: interfaceName,
@ -86,10 +94,12 @@ func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
PacketsRecv: packetsRecv, PacketsRecv: packetsRecv,
Errin: errIn, Errin: errIn,
Dropin: dropIn, Dropin: dropIn,
Fifoin: fifoIn,
BytesSent: bytesSent, BytesSent: bytesSent,
PacketsSent: packetsSent, PacketsSent: packetsSent,
Errout: errOut, Errout: errOut,
Dropout: dropOut, Dropout: dropOut,
Fifoout: fifoOut,
} }
ret = append(ret, nic) ret = append(ret, nic)
} }

@ -23,7 +23,7 @@ func TestNetIOCountersStatString(t *testing.T) {
Name: "test", Name: "test",
BytesSent: 100, 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) { if e != fmt.Sprintf("%v", v) {
t.Errorf("NetIOCountersStat string is invalid: %v", v) t.Errorf("NetIOCountersStat string is invalid: %v", v)
} }

@ -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)
)

@ -9,6 +9,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
"github.com/shirou/gopsutil/internal/common"
) )
// POSIX // POSIX
@ -72,7 +74,10 @@ func (p *Process) SendSignal(sig syscall.Signal) error {
} }
cmd := exec.Command(kill, "-s", sigAsStr, strconv.Itoa(int(p.Pid))) cmd := exec.Command(kill, "-s", sigAsStr, strconv.Itoa(int(p.Pid)))
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
err = cmd.Run() if err := cmd.Start(); err != nil {
return err
}
err = common.WaitTimeout(cmd, common.Timeout)
if err != nil { if err != nil {
return err return err
} }

@ -23,6 +23,11 @@ const (
MaxPathLength = 260 MaxPathLength = 260
) )
var (
modpsapi = syscall.NewLazyDLL("psapi.dll")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
)
type SystemProcessInformation struct { type SystemProcessInformation struct {
NextEntryOffset uint64 NextEntryOffset uint64
NumberOfThreads uint64 NumberOfThreads uint64
@ -46,13 +51,18 @@ type MemoryMapsStat struct {
} }
type Win32_Process struct { type Win32_Process struct {
Name string Name string
ExecutablePath *string ExecutablePath *string
CommandLine *string CommandLine *string
Priority uint32 Priority uint32
CreationDate *time.Time CreationDate *time.Time
ProcessID uint32 ProcessID uint32
ThreadCount uint32 ThreadCount uint32
Status *string
ReadOperationCount uint64
ReadTransferCount uint64
WriteOperationCount uint64
WriteTransferCount uint64
/* /*
CSCreationClassName string CSCreationClassName string
@ -76,14 +86,9 @@ type Win32_Process struct {
PeakVirtualSize uint64 PeakVirtualSize uint64
PeakWorkingSetSize uint32 PeakWorkingSetSize uint32
PrivatePageCount uint64 PrivatePageCount uint64
ReadOperationCount uint64
ReadTransferCount uint64
Status *string
TerminationDate *time.Time TerminationDate *time.Time
UserModeTime uint64 UserModeTime uint64
WorkingSetSize uint64 WorkingSetSize uint64
WriteOperationCount uint64
WriteTransferCount uint64
*/ */
} }
@ -158,12 +163,12 @@ func (p *Process) CmdlineSlice() ([]string, error) {
} }
func (p *Process) CreateTime() (int64, error) { func (p *Process) CreateTime() (int64, error) {
dst, err := GetWin32Proc(p.Pid) ru, err := getRusage(p.Pid)
if err != nil { if err != nil {
return 0, fmt.Errorf("could not get CreationDate: %s", err) 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) { func (p *Process) Cwd() (string, error) {
@ -207,8 +212,20 @@ func (p *Process) Rlimit() ([]RlimitStat, error) {
return rlimit, common.ErrNotImplementedError return rlimit, common.ErrNotImplementedError
} }
func (p *Process) IOCounters() (*IOCountersStat, error) { 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) { func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
@ -234,7 +251,17 @@ func (p *Process) CPUAffinity() ([]int32, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return nil, common.ErrNotImplementedError mem, err := getMemoryInfo(p.Pid)
if err != nil {
return nil, err
}
ret := &MemoryInfoStat{
RSS: uint64(mem.WorkingSetSize),
VMS: uint64(mem.PagefileUsage),
}
return ret, nil
} }
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
@ -355,3 +382,45 @@ func getProcInfo(pid int32) (*SystemProcessInformation, error) {
return &sysProcInfo, nil 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))
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
}

@ -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
}

@ -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
}
Loading…
Cancel
Save