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
(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.

@ -22,7 +22,7 @@ psutil functions on some architectures.
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
@ -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
@ -194,13 +194,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

@ -5,6 +5,9 @@ import (
"runtime"
"strconv"
"strings"
"sync"
"github.com/shirou/gopsutil/internal/common"
)
type TimesStat struct {
@ -37,8 +40,22 @@ type InfoStat struct {
Flags []string `json:"flags"`
}
var lastCPUTimes []TimesStat
var lastPerCPUTimes []TimesStat
type lastPercent struct {
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) {
return runtime.NumCPU(), nil

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

@ -27,7 +27,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)
@ -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 = strings.Join(vals, " ")
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
}

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

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

@ -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 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 {
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 calculateAllBusy(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 calculateAllBusy(lastTimes, cpuTimes)
}

@ -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"`

@ -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 {
@ -332,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")
@ -339,7 +345,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 {

@ -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"`

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

@ -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"`

@ -138,12 +138,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)))
}

@ -143,12 +143,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)))
}

@ -16,6 +16,7 @@ import (
"time"
"github.com/shirou/gopsutil/internal/common"
common "github.com/shirou/gopsutil/internal/common"
"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) {
ret := &LSB{}
if common.PathExists(common.HostEtc("lsb-release")) {
@ -172,7 +193,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
}
@ -264,6 +285,18 @@ 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 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 +331,10 @@ func PlatformInformation() (platform string, family string, version string, err
family = "arch"
case "exherbo":
family = "exherbo"
case "alpine":
family = "alpine"
case "coreos":
family = "coreos"
}
return platform, family, version, nil
@ -359,7 +396,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 +424,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 +447,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,16 +458,28 @@ 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"
}
}
}
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
}

@ -1,3 +1,5 @@
// +build linux
// +build ppc64le
// Created by cgo -godefs - DO NOT EDIT
// 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) {
v, err := BootTime()
if err != nil {
@ -27,6 +37,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) {

@ -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()
@ -74,7 +74,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 {
@ -86,16 +86,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) {

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

@ -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.
//

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

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

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

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

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

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

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

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

@ -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"
"strings"
"syscall"
"github.com/shirou/gopsutil/internal/common"
)
// 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.Stderr = os.Stderr
err = cmd.Run()
if err := cmd.Start(); err != nil {
return err
}
err = common.WaitTimeout(cmd, common.Timeout)
if err != nil {
return err
}

@ -23,6 +23,11 @@ const (
MaxPathLength = 260
)
var (
modpsapi = syscall.NewLazyDLL("psapi.dll")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
)
type SystemProcessInformation struct {
NextEntryOffset uint64
NumberOfThreads uint64
@ -46,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
@ -76,14 +86,9 @@ type Win32_Process struct {
PeakVirtualSize uint64
PeakWorkingSetSize uint32
PrivatePageCount uint64
ReadOperationCount uint64
ReadTransferCount uint64
Status *string
TerminationDate *time.Time
UserModeTime uint64
WorkingSetSize uint64
WriteOperationCount uint64
WriteTransferCount uint64
*/
}
@ -158,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) {
@ -207,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
@ -234,7 +251,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: uint64(mem.WorkingSetSize),
VMS: uint64(mem.PagefileUsage),
}
return ret, nil
}
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return nil, common.ErrNotImplementedError
@ -355,3 +382,45 @@ 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))
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