From df651ab98eb16b36711037b010c72d2895f6db07 Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Thu, 17 Sep 2015 12:08:10 -0700 Subject: [PATCH] Properly vendor the gopsutil dependency --- Godeps/Godeps.json | 35 + .../shirou/gopsutil/common/common.go | 209 ++++++ .../shirou/gopsutil/common/common_darwin.go | 89 +++ .../shirou/gopsutil/common/common_freebsd.go | 61 ++ .../shirou/gopsutil/common/common_linux.go | 37 + .../shirou/gopsutil/common/common_test.go | 90 +++ .../shirou/gopsutil/common/common_windows.go | 110 +++ .../src/github.com/shirou/gopsutil/cpu/cpu.go | 74 ++ .../shirou/gopsutil/cpu/cpu_darwin.go | 203 ++++++ .../shirou/gopsutil/cpu/cpu_freebsd.go | 143 ++++ .../shirou/gopsutil/cpu/cpu_linux.go | 201 ++++++ .../shirou/gopsutil/cpu/cpu_test.go | 98 +++ .../shirou/gopsutil/cpu/cpu_unix.go | 61 ++ .../shirou/gopsutil/cpu/cpu_windows.go | 105 +++ .../github.com/shirou/gopsutil/disk/binary.go | 634 ++++++++++++++++++ .../github.com/shirou/gopsutil/disk/disk.go | 52 ++ .../shirou/gopsutil/disk/disk_darwin.go | 104 +++ .../shirou/gopsutil/disk/disk_darwin_amd64.go | 58 ++ .../shirou/gopsutil/disk/disk_freebsd.go | 179 +++++ .../gopsutil/disk/disk_freebsd_amd64.go | 111 +++ .../shirou/gopsutil/disk/disk_linux.go | 327 +++++++++ .../shirou/gopsutil/disk/disk_test.go | 97 +++ .../shirou/gopsutil/disk/disk_unix.go | 30 + .../shirou/gopsutil/disk/disk_windows.go | 155 +++++ .../shirou/gopsutil/disk/types_freebsd.go | 85 +++ .../shirou/gopsutil/docker/docker.go | 37 + .../shirou/gopsutil/docker/docker_linux.go | 170 +++++ .../gopsutil/docker/docker_linux_test.go | 60 ++ .../shirou/gopsutil/docker/docker_notlinux.go | 40 ++ .../github.com/shirou/gopsutil/load/load.go | 16 + .../shirou/gopsutil/load/load_darwin.go | 37 + .../shirou/gopsutil/load/load_freebsd.go | 37 + .../shirou/gopsutil/load/load_linux.go | 40 ++ .../shirou/gopsutil/load/load_test.go | 30 + .../shirou/gopsutil/load/load_windows.go | 13 + .../src/github.com/shirou/gopsutil/mem/mem.go | 38 ++ .../shirou/gopsutil/mem/mem_darwin.go | 109 +++ .../shirou/gopsutil/mem/mem_freebsd.go | 131 ++++ .../shirou/gopsutil/mem/mem_linux.go | 92 +++ .../shirou/gopsutil/mem/mem_test.go | 55 ++ .../shirou/gopsutil/mem/mem_windows.go | 50 ++ .../src/github.com/shirou/gopsutil/net/net.go | 227 +++++++ .../shirou/gopsutil/net/net_darwin.go | 147 ++++ .../shirou/gopsutil/net/net_freebsd.go | 89 +++ .../shirou/gopsutil/net/net_linux.go | 145 ++++ .../shirou/gopsutil/net/net_test.go | 143 ++++ .../shirou/gopsutil/net/net_windows.go | 98 +++ 47 files changed, 5152 insertions(+) create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/common/common.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_darwin.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_windows.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_darwin.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_unix.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_windows.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/binary.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_darwin.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_darwin_amd64.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_freebsd_amd64.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_unix.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_windows.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/disk/types_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_linux_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_notlinux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/load/load.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_darwin.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_windows.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_darwin.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_windows.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/net/net.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_darwin.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_linux.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_test.go create mode 100644 Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_windows.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 9edccea83..fdbafa30a 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -141,6 +141,41 @@ "ImportPath": "github.com/samuel/go-zookeeper/zk", "Rev": "5bb5cfc093ad18a28148c578f8632cfdb4d802e4" }, + { + "ImportPath": "github.com/shirou/gopsutil/common", + "Comment": "1.0.0-146-g9c0474c", + "Rev": "9c0474c2a7cbea73ea2d93fc5e1ef1c024caf761" + }, + { + "ImportPath": "github.com/shirou/gopsutil/cpu", + "Comment": "1.0.0-146-g9c0474c", + "Rev": "9c0474c2a7cbea73ea2d93fc5e1ef1c024caf761" + }, + { + "ImportPath": "github.com/shirou/gopsutil/disk", + "Comment": "1.0.0-146-g9c0474c", + "Rev": "9c0474c2a7cbea73ea2d93fc5e1ef1c024caf761" + }, + { + "ImportPath": "github.com/shirou/gopsutil/docker", + "Comment": "1.0.0-146-g9c0474c", + "Rev": "9c0474c2a7cbea73ea2d93fc5e1ef1c024caf761" + }, + { + "ImportPath": "github.com/shirou/gopsutil/load", + "Comment": "1.0.0-146-g9c0474c", + "Rev": "9c0474c2a7cbea73ea2d93fc5e1ef1c024caf761" + }, + { + "ImportPath": "github.com/shirou/gopsutil/mem", + "Comment": "1.0.0-146-g9c0474c", + "Rev": "9c0474c2a7cbea73ea2d93fc5e1ef1c024caf761" + }, + { + "ImportPath": "github.com/shirou/gopsutil/net", + "Comment": "1.0.0-146-g9c0474c", + "Rev": "9c0474c2a7cbea73ea2d93fc5e1ef1c024caf761" + }, { "ImportPath": "github.com/streadway/amqp", "Rev": "f4879ba28fffbb576743b03622a9ff20461826b2" diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common.go new file mode 100644 index 000000000..a7dbc28eb --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common.go @@ -0,0 +1,209 @@ +// +// gopsutil is a port of psutil(http://pythonhosted.org/psutil/). +// This covers these architectures. +// - linux (amd64, arm) +// - freebsd (amd64) +// - windows (amd64) +package common + +import ( + "bufio" + "errors" + "io/ioutil" + "net/url" + "os" + "os/exec" + "path" + "reflect" + "runtime" + "strconv" + "strings" +) + +type Invoker interface { + Command(string, ...string) ([]byte, error) +} + +type Invoke struct{} + +func (i Invoke) Command(name string, arg ...string) ([]byte, error) { + return exec.Command(name, arg...).Output() +} + +type FakeInvoke struct { + CommandExpectedDir string // CommandExpectedDir specifies dir which includes expected outputs. + Suffix string // Suffix species expected file name suffix such as "fail" + Error error // If Error specfied, return the error. +} + +// Command in FakeInvoke returns from expected file if exists. +func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) { + if i.Error != nil { + return []byte{}, i.Error + } + + arch := runtime.GOOS + + fname := strings.Join(append([]string{name}, arg...), "") + fname = url.QueryEscape(fname) + var dir string + if i.CommandExpectedDir == "" { + dir = "expected" + } else { + dir = i.CommandExpectedDir + } + fpath := path.Join(dir, arch, fname) + if i.Suffix != "" { + fpath += "_" + i.Suffix + } + if PathExists(fpath) { + return ioutil.ReadFile(fpath) + } else { + return exec.Command(name, arg...).Output() + } +} + +var NotImplementedError = errors.New("not implemented yet") + +// ReadLines reads contents from a file and splits them by new lines. +// A convenience wrapper to ReadLinesOffsetN(filename, 0, -1). +func ReadLines(filename string) ([]string, error) { + return ReadLinesOffsetN(filename, 0, -1) +} + +// ReadLines reads contents from file and splits them by new line. +// The offset tells at which line number to start. +// The count determines the number of lines to read (starting from offset): +// n >= 0: at most n lines +// n < 0: whole file +func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) { + f, err := os.Open(filename) + if err != nil { + return []string{""}, err + } + defer f.Close() + + var ret []string + + r := bufio.NewReader(f) + for i := 0; i < n+int(offset) || n < 0; i++ { + line, err := r.ReadString('\n') + if err != nil { + break + } + if i < int(offset) { + continue + } + ret = append(ret, strings.Trim(line, "\n")) + } + + return ret, nil +} + +func IntToString(orig []int8) 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]) +} + +func ByteToString(orig []byte) string { + n := -1 + l := -1 + for i, b := range orig { + // skip left side null + if l == -1 && b == 0 { + continue + } + if l == -1 { + l = i + } + + if b == 0 { + break + } + n = i + 1 + } + if n == -1 { + return string(orig) + } + return string(orig[l:n]) +} + +// Parse to int32 without error +func mustParseInt32(val string) int32 { + vv, _ := strconv.ParseInt(val, 10, 32) + return int32(vv) +} + +// Parse to uint64 without error +func mustParseUint64(val string) uint64 { + vv, _ := strconv.ParseInt(val, 10, 64) + return uint64(vv) +} + +// Parse to Float64 without error +func mustParseFloat64(val string) float64 { + vv, _ := strconv.ParseFloat(val, 64) + return vv +} + +// StringsHas checks the target string slice containes src or not +func StringsHas(target []string, src string) bool { + for _, t := range target { + if strings.TrimSpace(t) == src { + return true + } + } + return false +} + +// StringsContains checks the src in any string of the target string slice +func StringsContains(target []string, src string) bool { + for _, t := range target { + if strings.Contains(t, src) { + return true + } + } + return false +} + +// get struct attributes. +// This method is used only for debugging platform dependent code. +func attributes(m interface{}) map[string]reflect.Type { + typ := reflect.TypeOf(m) + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + + attrs := make(map[string]reflect.Type) + if typ.Kind() != reflect.Struct { + return nil + } + + for i := 0; i < typ.NumField(); i++ { + p := typ.Field(i) + if !p.Anonymous { + attrs[p.Name] = p.Type + } + } + + return attrs +} + +func PathExists(filename string) bool { + if _, err := os.Stat(filename); err == nil { + return true + } + return false +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_darwin.go new file mode 100644 index 000000000..cc746018c --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_darwin.go @@ -0,0 +1,89 @@ +// +build darwin + +package common + +import ( + "os/exec" + "strconv" + "strings" + "syscall" + "unsafe" +) + +func DoSysctrl(mib string) ([]string, error) { + out, err := exec.Command("/usr/sbin/sysctl", "-n", mib).Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} + +func CallLsof(invoke Invoker, pid int32, args ...string) ([]string, error) { + var cmd []string + if pid == 0 { // will get from all processes. + cmd = []string{"-a", "-n", "-P"} + } else { + cmd = []string{"-a", "-n", "-P", "-p", strconv.Itoa(int(pid))} + } + cmd = append(cmd, args...) + lsof, err := exec.LookPath("lsof") + if err != nil { + return []string{}, err + } + out, err := invoke.Command(lsof, cmd...) + if err != nil { + return []string{}, err + } + lines := strings.Split(string(out), "\n") + + var ret []string + for _, l := range lines[1:] { + if len(l) == 0 { + continue + } + ret = append(ret, l) + } + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_freebsd.go new file mode 100644 index 000000000..3c1124655 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_freebsd.go @@ -0,0 +1,61 @@ +// +build freebsd + +package common + +import ( + "syscall" + "os/exec" + "strings" + "unsafe" +) + +func DoSysctrl(mib string) ([]string, error) { + out, err := exec.Command("/sbin/sysctl", "-n", mib).Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} + diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_linux.go new file mode 100644 index 000000000..3c760e5c8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_linux.go @@ -0,0 +1,37 @@ +// +build linux + +package common + +import ( + "os/exec" + "strconv" + "strings" +) + +func CallLsof(invoke Invoker, pid int32, args ...string) ([]string, error) { + var cmd []string + if pid == 0 { // will get from all processes. + cmd = []string{"-a", "-n", "-P"} + } else { + cmd = []string{"-a", "-n", "-P", "-p", strconv.Itoa(int(pid))} + } + cmd = append(cmd, args...) + lsof, err := exec.LookPath("lsof") + if err != nil { + return []string{}, err + } + out, err := invoke.Command(lsof, cmd...) + if err != nil { + return []string{}, err + } + lines := strings.Split(string(out), "\n") + + var ret []string + for _, l := range lines[1:] { + if len(l) == 0 { + continue + } + ret = append(ret, l) + } + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_test.go new file mode 100644 index 000000000..b2660b2af --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_test.go @@ -0,0 +1,90 @@ +package common + +import ( + "fmt" + "strings" + "testing" +) + +func TestReadlines(t *testing.T) { + ret, err := ReadLines("common_test.go") + if err != nil { + t.Error(err) + } + if !strings.Contains(ret[0], "package common") { + t.Error("could not read correctly") + } +} + +func TestReadLinesOffsetN(t *testing.T) { + ret, err := ReadLinesOffsetN("common_test.go", 2, 1) + if err != nil { + t.Error(err) + } + fmt.Println(ret[0]) + if !strings.Contains(ret[0], `import (`) { + t.Error("could not read correctly") + } +} + +func TestIntToString(t *testing.T) { + src := []int8{65, 66, 67} + dst := IntToString(src) + if dst != "ABC" { + t.Error("could not convert") + } +} +func TestByteToString(t *testing.T) { + src := []byte{65, 66, 67} + dst := ByteToString(src) + if dst != "ABC" { + t.Error("could not convert") + } + + src = []byte{0, 65, 66, 67} + dst = ByteToString(src) + if dst != "ABC" { + t.Error("could not convert") + } +} + +func TestmustParseInt32(t *testing.T) { + ret := mustParseInt32("11111") + if ret != int32(11111) { + t.Error("could not parse") + } +} +func TestmustParseUint64(t *testing.T) { + ret := mustParseUint64("11111") + if ret != uint64(11111) { + t.Error("could not parse") + } +} +func TestmustParseFloat64(t *testing.T) { + ret := mustParseFloat64("11111.11") + if ret != float64(11111.11) { + t.Error("could not parse") + } + ret = mustParseFloat64("11111") + if ret != float64(11111) { + t.Error("could not parse") + } +} +func TestStringsContains(t *testing.T) { + target, err := ReadLines("common_test.go") + if err != nil { + t.Error(err) + } + if !StringsContains(target, "func TestStringsContains(t *testing.T) {") { + t.Error("cloud not test correctly") + } +} + +func TestPathExists(t *testing.T) { + if !PathExists("common_test.go") { + t.Error("exists but return not exists") + } + if PathExists("should_not_exists.go") { + t.Error("not exists but return exists") + } +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_windows.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_windows.go new file mode 100644 index 000000000..d727378cb --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/common/common_windows.go @@ -0,0 +1,110 @@ +// +build windows + +package common + +import ( + "syscall" + "unsafe" +) + +// for double values +type PDH_FMT_COUNTERVALUE_DOUBLE struct { + CStatus uint32 + DoubleValue float64 +} + +// for 64 bit integer values +type PDH_FMT_COUNTERVALUE_LARGE struct { + CStatus uint32 + LargeValue int64 +} + +// for long values +type PDH_FMT_COUNTERVALUE_LONG struct { + CStatus uint32 + LongValue int32 + padding [4]byte +} + +// windows system const +const ( + ERROR_SUCCESS = 0 + ERROR_FILE_NOT_FOUND = 2 + DRIVE_REMOVABLE = 2 + DRIVE_FIXED = 3 + HKEY_LOCAL_MACHINE = 0x80000002 + RRF_RT_REG_SZ = 0x00000002 + RRF_RT_REG_DWORD = 0x00000010 + PDH_FMT_LONG = 0x00000100 + PDH_FMT_DOUBLE = 0x00000200 + PDH_FMT_LARGE = 0x00000400 + PDH_INVALID_DATA = 0xc0000bc6 + PDH_INVALID_HANDLE = 0xC0000bbc + PDH_NO_DATA = 0x800007d5 +) + +var ( + Modkernel32 = syscall.NewLazyDLL("kernel32.dll") + ModNt = syscall.NewLazyDLL("ntdll.dll") + ModPdh = syscall.NewLazyDLL("pdh.dll") + + ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes") + ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation") + PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery") + PdhAddCounter = ModPdh.NewProc("PdhAddCounterW") + PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData") + PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue") + PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery") +) + +type FILETIME struct { + DwLowDateTime uint32 + DwHighDateTime uint32 +} + +// borrowed from net/interface_windows.go +func BytePtrToString(p *uint8) string { + a := (*[10000]uint8)(unsafe.Pointer(p)) + i := 0 + for a[i] != 0 { + i++ + } + return string(a[:i]) +} + +// CounterInfo +// copied from https://github.com/mackerelio/mackerel-agent/ +type CounterInfo struct { + PostName string + CounterName string + Counter syscall.Handle +} + +// CreateQuery XXX +// copied from https://github.com/mackerelio/mackerel-agent/ +func CreateQuery() (syscall.Handle, error) { + var query syscall.Handle + r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query))) + if r != 0 { + return 0, err + } + return query, nil +} + +// CreateCounter XXX +func CreateCounter(query syscall.Handle, pname, cname string) (*CounterInfo, error) { + var counter syscall.Handle + r, _, err := PdhAddCounter.Call( + uintptr(query), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(cname))), + 0, + uintptr(unsafe.Pointer(&counter))) + if r != 0 { + return nil, err + } + return &CounterInfo{ + PostName: pname, + CounterName: cname, + Counter: counter, + }, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu.go new file mode 100644 index 000000000..b8e8edb44 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu.go @@ -0,0 +1,74 @@ +package cpu + +import ( + "encoding/json" + "runtime" + "strconv" + "strings" +) + +type CPUTimesStat struct { + CPU string `json:"cpu"` + User float64 `json:"user"` + System float64 `json:"system"` + Idle float64 `json:"idle"` + Nice float64 `json:"nice"` + Iowait float64 `json:"iowait"` + Irq float64 `json:"irq"` + Softirq float64 `json:"softirq"` + Steal float64 `json:"steal"` + Guest float64 `json:"guest"` + GuestNice float64 `json:"guest_nice"` + Stolen float64 `json:"stolen"` +} + +type CPUInfoStat struct { + CPU int32 `json:"cpu"` + VendorID string `json:"vendor_id"` + Family string `json:"family"` + Model string `json:"model"` + Stepping int32 `json:"stepping"` + PhysicalID string `json:"physical_id"` + CoreID string `json:"core_id"` + Cores int32 `json:"cores"` + ModelName string `json:"model_name"` + Mhz float64 `json:"mhz"` + CacheSize int32 `json:"cache_size"` + Flags []string `json:"flags"` +} + +var lastCPUTimes []CPUTimesStat +var lastPerCPUTimes []CPUTimesStat + +func CPUCounts(logical bool) (int, error) { + return runtime.NumCPU(), nil +} + +func (c CPUTimesStat) String() string { + v := []string{ + `"cpu":"` + c.CPU + `"`, + `"user":` + strconv.FormatFloat(c.User, 'f', 1, 64), + `"system":` + strconv.FormatFloat(c.System, 'f', 1, 64), + `"idle":` + strconv.FormatFloat(c.Idle, 'f', 1, 64), + `"nice":` + strconv.FormatFloat(c.Nice, 'f', 1, 64), + `"iowait":` + strconv.FormatFloat(c.Iowait, 'f', 1, 64), + `"irq":` + strconv.FormatFloat(c.Irq, 'f', 1, 64), + `"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64), + `"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64), + `"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64), + `"guest_nice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64), + `"stolen":` + strconv.FormatFloat(c.Stolen, 'f', 1, 64), + } + + return `{` + strings.Join(v, ",") + `}` +} + +func (c CPUInfoStat) String() string { + s, _ := json.Marshal(c) + return string(s) +} + +func init() { + lastCPUTimes, _ = CPUTimes(false) + lastPerCPUTimes, _ = CPUTimes(true) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_darwin.go new file mode 100644 index 000000000..455294717 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_darwin.go @@ -0,0 +1,203 @@ +// +build darwin + +package cpu + +/* +#include +#include +#include +#include +#include +#include +#include +#include +#include +*/ +import "C" + +import ( + "bytes" + "encoding/binary" + "fmt" + "os/exec" + "strconv" + "strings" + "unsafe" +) + +// sys/resource.h +const ( + CPUser = 0 + CPNice = 1 + CPSys = 2 + CPIntr = 3 + CPIdle = 4 + CPUStates = 5 +) + +// default value. from time.h +var ClocksPerSec = float64(128) + +// these CPU times for darwin is borrowed from influxdb/telegraf. + +func perCPUTimes() ([]CPUTimesStat, error) { + var ( + count C.mach_msg_type_number_t + cpuload *C.processor_cpu_load_info_data_t + ncpu C.natural_t + ) + + status := C.host_processor_info(C.host_t(C.mach_host_self()), + C.PROCESSOR_CPU_LOAD_INFO, + &ncpu, + (*C.processor_info_array_t)(unsafe.Pointer(&cpuload)), + &count) + + if status != C.KERN_SUCCESS { + return nil, fmt.Errorf("host_processor_info error=%d", status) + } + + // jump through some cgo casting hoops and ensure we properly free + // the memory that cpuload points to + target := C.vm_map_t(C.mach_task_self_) + address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload))) + defer C.vm_deallocate(target, address, C.vm_size_t(ncpu)) + + // the body of struct processor_cpu_load_info + // aka processor_cpu_load_info_data_t + var cpu_ticks [C.CPU_STATE_MAX]uint32 + + // copy the cpuload array to a []byte buffer + // where we can binary.Read the data + size := int(ncpu) * binary.Size(cpu_ticks) + buf := C.GoBytes(unsafe.Pointer(cpuload), C.int(size)) + + bbuf := bytes.NewBuffer(buf) + + var ret []CPUTimesStat + + for i := 0; i < int(ncpu); i++ { + err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks) + if err != nil { + return nil, err + } + + c := CPUTimesStat{ + CPU: fmt.Sprintf("cpu%d", i), + User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, + System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, + Nice: float64(cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, + Idle: float64(cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, + } + + ret = append(ret, c) + } + + return ret, nil +} + +func allCPUTimes() ([]CPUTimesStat, error) { + var count C.mach_msg_type_number_t = C.HOST_CPU_LOAD_INFO_COUNT + var cpuload C.host_cpu_load_info_data_t + + status := C.host_statistics(C.host_t(C.mach_host_self()), + C.HOST_CPU_LOAD_INFO, + C.host_info_t(unsafe.Pointer(&cpuload)), + &count) + + if status != C.KERN_SUCCESS { + return nil, fmt.Errorf("host_statistics error=%d", status) + } + + c := CPUTimesStat{ + CPU: "cpu-total", + User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, + System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, + Nice: float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, + Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, + } + + return []CPUTimesStat{c}, nil + +} + +func CPUTimes(percpu bool) ([]CPUTimesStat, error) { + if percpu { + return perCPUTimes() + } + + return allCPUTimes() +} + +// Returns only one CPUInfoStat on FreeBSD +func CPUInfo() ([]CPUInfoStat, error) { + var ret []CPUInfoStat + + out, err := exec.Command("/usr/sbin/sysctl", "machdep.cpu").Output() + if err != nil { + return ret, err + } + + c := CPUInfoStat{} + for _, line := range strings.Split(string(out), "\n") { + values := strings.Fields(line) + if len(values) < 1 { + continue + } + + t, err := strconv.ParseInt(values[1], 10, 64) + // err is not checked here because some value is string. + if strings.HasPrefix(line, "machdep.cpu.brand_string") { + c.ModelName = strings.Join(values[1:], " ") + } else if strings.HasPrefix(line, "machdep.cpu.family") { + c.Family = values[1] + } else if strings.HasPrefix(line, "machdep.cpu.model") { + c.Model = values[1] + } else if strings.HasPrefix(line, "machdep.cpu.stepping") { + if err != nil { + return ret, err + } + c.Stepping = int32(t) + } else if strings.HasPrefix(line, "machdep.cpu.features") { + for _, v := range values[1:] { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if strings.HasPrefix(line, "machdep.cpu.leaf7_features") { + for _, v := range values[1:] { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if strings.HasPrefix(line, "machdep.cpu.extfeatures") { + for _, v := range values[1:] { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if strings.HasPrefix(line, "machdep.cpu.core_count") { + if err != nil { + return ret, err + } + c.Cores = int32(t) + } else if strings.HasPrefix(line, "machdep.cpu.cache.size") { + if err != nil { + return ret, err + } + c.CacheSize = int32(t) + } else if strings.HasPrefix(line, "machdep.cpu.vendor") { + c.VendorID = values[1] + } + } + + // 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("/usr/sbin/sysctl", "hw.cpufrequency").Output() + if err != nil { + return ret, err + } + + values := strings.Fields(string(out)) + mhz, err := strconv.ParseFloat(values[1], 64) + if err != nil { + return ret, err + } + c.Mhz = mhz / 1000000.0 + + return append(ret, c), nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_freebsd.go new file mode 100644 index 000000000..2c22a3953 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_freebsd.go @@ -0,0 +1,143 @@ +// +build freebsd + +package cpu + +import ( + "fmt" + "os/exec" + "regexp" + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" +) + +// sys/resource.h +const ( + CPUser = 0 + CPNice = 1 + CPSys = 2 + CPIntr = 3 + CPIdle = 4 + CPUStates = 5 +) + +var ClocksPerSec = float64(128) + +func init() { + out, err := exec.Command("/usr/bin/getconf", "CLK_TCK").Output() + // ignore errors + if err == nil { + i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) + if err == nil { + ClocksPerSec = float64(i) + } + } +} + +func CPUTimes(percpu bool) ([]CPUTimesStat, error) { + var ret []CPUTimesStat + + var sysctlCall string + var ncpu int + if percpu { + sysctlCall = "kern.cp_times" + ncpu, _ = CPUCounts(true) + } else { + sysctlCall = "kern.cp_time" + ncpu = 1 + } + + cpuTimes, err := common.DoSysctrl(sysctlCall) + if err != nil { + return ret, err + } + + for i := 0; i < ncpu; i++ { + offset := CPUStates * i + user, err := strconv.ParseFloat(cpuTimes[CPUser+offset], 64) + if err != nil { + return ret, err + } + nice, err := strconv.ParseFloat(cpuTimes[CPNice+offset], 64) + if err != nil { + return ret, err + } + sys, err := strconv.ParseFloat(cpuTimes[CPSys+offset], 64) + if err != nil { + return ret, err + } + idle, err := strconv.ParseFloat(cpuTimes[CPIdle+offset], 64) + if err != nil { + return ret, err + } + intr, err := strconv.ParseFloat(cpuTimes[CPIntr+offset], 64) + if err != nil { + return ret, err + } + + c := CPUTimesStat{ + User: float64(user / ClocksPerSec), + Nice: float64(nice / ClocksPerSec), + System: float64(sys / ClocksPerSec), + Idle: float64(idle / ClocksPerSec), + Irq: float64(intr / ClocksPerSec), + } + if !percpu { + c.CPU = "cpu-total" + } else { + c.CPU = fmt.Sprintf("cpu%d", i) + } + + ret = append(ret, c) + } + + return ret, nil +} + +// Returns only one CPUInfoStat on FreeBSD +func CPUInfo() ([]CPUInfoStat, error) { + filename := "/var/run/dmesg.boot" + lines, _ := common.ReadLines(filename) + + var ret []CPUInfoStat + + c := CPUInfoStat{} + 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 { + 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 + } + c.Stepping = int32(t) + } else if matches := regexp.MustCompile(`Features=.+<(.+)>`).FindStringSubmatch(line); matches != nil { + for _, v := range strings.Split(matches[1], ",") { + c.Flags = append(c.Flags, strings.ToLower(v)) + } + } else if matches := regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`).FindStringSubmatch(line); matches != nil { + 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 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_linux.go new file mode 100644 index 000000000..942ef0c2a --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_linux.go @@ -0,0 +1,201 @@ +// +build linux + +package cpu + +import ( + "errors" + "os/exec" + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" +) + +var cpu_tick = float64(100) + +func init() { + out, err := exec.Command("/usr/bin/getconf", "CLK_TCK").Output() + // ignore errors + if err == nil { + i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) + if err == nil { + cpu_tick = float64(i) + } + } +} + +func CPUTimes(percpu bool) ([]CPUTimesStat, error) { + filename := "/proc/stat" + var lines = []string{} + if percpu { + var startIdx uint = 1 + for { + linen, _ := common.ReadLinesOffsetN(filename, startIdx, 1) + line := linen[0] + if !strings.HasPrefix(line, "cpu") { + break + } + lines = append(lines, line) + startIdx += 1 + } + } else { + lines, _ = common.ReadLinesOffsetN(filename, 0, 1) + } + + ret := make([]CPUTimesStat, 0, len(lines)) + + for _, line := range lines { + ct, err := parseStatLine(line) + if err != nil { + continue + } + ret = append(ret, *ct) + + } + return ret, nil +} + +func CPUInfo() ([]CPUInfoStat, error) { + filename := "/proc/cpuinfo" + lines, _ := common.ReadLines(filename) + + var ret []CPUInfoStat + + var c CPUInfoStat + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) < 2 { + if c.VendorID != "" { + ret = append(ret, c) + } + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + + switch key { + case "processor": + c = CPUInfoStat{} + t, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return ret, err + } + c.CPU = int32(t) + case "vendor_id": + c.VendorID = value + case "cpu family": + c.Family = value + case "model": + c.Model = value + case "model name": + c.ModelName = value + case "stepping": + t, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return ret, err + } + c.Stepping = int32(t) + case "cpu MHz": + t, err := strconv.ParseFloat(value, 64) + if err != nil { + return ret, err + } + c.Mhz = t + case "cache size": + t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 64) + if err != nil { + return ret, err + } + c.CacheSize = int32(t) + case "physical id": + c.PhysicalID = value + case "core id": + c.CoreID = value + case "cpu cores": + t, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return ret, err + } + c.Cores = int32(t) + case "flags": + c.Flags = strings.Split(value, ",") + } + } + return ret, nil +} + +func parseStatLine(line string) (*CPUTimesStat, error) { + fields := strings.Fields(line) + + if strings.HasPrefix(fields[0], "cpu") == false { + // return CPUTimesStat{}, e + return nil, errors.New("not contain cpu") + } + + cpu := fields[0] + if cpu == "cpu" { + cpu = "cpu-total" + } + user, err := strconv.ParseFloat(fields[1], 64) + if err != nil { + return nil, err + } + nice, err := strconv.ParseFloat(fields[2], 64) + if err != nil { + return nil, err + } + system, err := strconv.ParseFloat(fields[3], 64) + if err != nil { + return nil, err + } + idle, err := strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, err + } + iowait, err := strconv.ParseFloat(fields[5], 64) + if err != nil { + return nil, err + } + irq, err := strconv.ParseFloat(fields[6], 64) + if err != nil { + return nil, err + } + softirq, err := strconv.ParseFloat(fields[7], 64) + if err != nil { + return nil, err + } + + ct := &CPUTimesStat{ + CPU: cpu, + User: float64(user) / cpu_tick, + Nice: float64(nice) / cpu_tick, + System: float64(system) / cpu_tick, + Idle: float64(idle) / cpu_tick, + Iowait: float64(iowait) / cpu_tick, + Irq: float64(irq) / cpu_tick, + Softirq: float64(softirq) / cpu_tick, + } + if len(fields) > 8 { // Linux >= 2.6.11 + steal, err := strconv.ParseFloat(fields[8], 64) + if err != nil { + return nil, err + } + ct.Steal = float64(steal) / cpu_tick + } + if len(fields) > 9 { // Linux >= 2.6.24 + guest, err := strconv.ParseFloat(fields[9], 64) + if err != nil { + return nil, err + } + ct.Guest = float64(guest) / cpu_tick + } + if len(fields) > 10 { // Linux >= 3.2.0 + guestNice, err := strconv.ParseFloat(fields[10], 64) + if err != nil { + return nil, err + } + ct.GuestNice = float64(guestNice) / cpu_tick + } + + return ct, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_test.go new file mode 100644 index 000000000..7de17a767 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_test.go @@ -0,0 +1,98 @@ +package cpu + +import ( + "fmt" + "runtime" + "testing" + "time" +) + +func TestCpu_times(t *testing.T) { + v, err := CPUTimes(false) + if err != nil { + t.Errorf("error %v", err) + } + if len(v) == 0 { + t.Error("could not get CPUs ", err) + } + empty := CPUTimesStat{} + for _, vv := range v { + if vv == empty { + t.Errorf("could not get CPU User: %v", vv) + } + } +} + +func TestCpu_counts(t *testing.T) { + v, err := CPUCounts(true) + if err != nil { + t.Errorf("error %v", err) + } + if v == 0 { + t.Errorf("could not get CPU counts: %v", v) + } +} + +func TestCPUTimeStat_String(t *testing.T) { + v := CPUTimesStat{ + CPU: "cpu0", + User: 100.1, + System: 200.1, + Idle: 300.1, + } + e := `{"cpu":"cpu0","user":100.1,"system":200.1,"idle":300.1,"nice":0.0,"iowait":0.0,"irq":0.0,"softirq":0.0,"steal":0.0,"guest":0.0,"guest_nice":0.0,"stolen":0.0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("CPUTimesStat string is invalid: %v", v) + } +} + +func TestCpuInfo(t *testing.T) { + v, err := CPUInfo() + if err != nil { + t.Errorf("error %v", err) + } + if len(v) == 0 { + t.Errorf("could not get CPU Info") + } + for _, vv := range v { + if vv.ModelName == "" { + t.Errorf("could not get CPU Info: %v", vv) + } + } +} + +func testCPUPercent(t *testing.T, percpu bool) { + numcpu := runtime.NumCPU() + testCount := 3 + + if runtime.GOOS != "windows" { + testCount = 100 + v, err := CPUPercent(time.Millisecond, percpu) + if err != nil { + t.Errorf("error %v", err) + } + 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++ { + duration := time.Duration(10) * time.Microsecond + v, err := CPUPercent(duration, percpu) + if err != nil { + t.Errorf("error %v", err) + } + for _, percent := range v { + if percent < 0.0 || percent > 100.0*float64(numcpu) { + t.Fatalf("CPUPercent value is invalid: %f", percent) + } + } + } +} + +func TestCPUPercent(t *testing.T) { + testCPUPercent(t, false) +} + +func TestCPUPercentPerCpu(t *testing.T) { + testCPUPercent(t, true) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_unix.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_unix.go new file mode 100644 index 000000000..fd488a284 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_unix.go @@ -0,0 +1,61 @@ +// +build linux freebsd darwin + +package cpu + +import "time" + +func init() { + lastCPUTimes, _ = CPUTimes(false) + lastPerCPUTimes, _ = CPUTimes(true) +} + +func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) { + getAllBusy := func(t CPUTimesStat) (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 + } + + calculate := func(t1, t2 CPUTimesStat) 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 + } + + cpuTimes, err := CPUTimes(percpu) + if err != nil { + return nil, err + } + + if interval > 0 { + if !percpu { + lastCPUTimes = cpuTimes + } else { + lastPerCPUTimes = cpuTimes + } + time.Sleep(interval) + cpuTimes, err = CPUTimes(percpu) + if err != nil { + return nil, err + } + } + + ret := make([]float64, len(cpuTimes)) + if !percpu { + ret[0] = calculate(lastCPUTimes[0], cpuTimes[0]) + lastCPUTimes = cpuTimes + } else { + for i, t := range cpuTimes { + ret[i] = calculate(lastPerCPUTimes[i], t) + } + lastPerCPUTimes = cpuTimes + } + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_windows.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_windows.go new file mode 100644 index 000000000..96f4c70f6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/cpu/cpu_windows.go @@ -0,0 +1,105 @@ +// +build windows + +package cpu + +import ( + "fmt" + "syscall" + "time" + "unsafe" + + "github.com/StackExchange/wmi" + + common "github.com/shirou/gopsutil/common" +) + +type Win32_Processor struct { + LoadPercentage *uint16 + Family uint16 + Manufacturer string + Name string + NumberOfLogicalProcessors uint32 + ProcessorId *string + Stepping *string + MaxClockSpeed uint32 +} + +// TODO: Get percpu +func CPUTimes(percpu bool) ([]CPUTimesStat, error) { + var ret []CPUTimesStat + + var lpIdleTime common.FILETIME + var lpKernelTime common.FILETIME + var lpUserTime common.FILETIME + r, _, _ := common.ProcGetSystemTimes.Call( + uintptr(unsafe.Pointer(&lpIdleTime)), + uintptr(unsafe.Pointer(&lpKernelTime)), + uintptr(unsafe.Pointer(&lpUserTime))) + if r == 0 { + return ret, syscall.GetLastError() + } + + LOT := float64(0.0000001) + HIT := (LOT * 4294967296.0) + idle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime))) + user := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime))) + kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) + system := (kernel - idle) + + ret = append(ret, CPUTimesStat{ + Idle: float64(idle), + User: float64(user), + System: float64(system), + }) + return ret, nil +} + +func CPUInfo() ([]CPUInfoStat, error) { + var ret []CPUInfoStat + var dst []Win32_Processor + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) + if err != nil { + return ret, err + } + + var procID string + for i, l := range dst { + procID = "" + if l.ProcessorId != nil { + procID = *l.ProcessorId + } + + cpu := CPUInfoStat{ + CPU: int32(i), + Family: fmt.Sprintf("%d", l.Family), + VendorID: l.Manufacturer, + ModelName: l.Name, + Cores: int32(l.NumberOfLogicalProcessors), + PhysicalID: procID, + Mhz: float64(l.MaxClockSpeed), + Flags: []string{}, + } + ret = append(ret, cpu) + } + + return ret, nil +} + +func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) { + var ret []float64 + var dst []Win32_Processor + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) + if err != nil { + return ret, err + } + for _, l := range dst { + // use range but windows can only get one percent. + if l.LoadPercentage == nil { + continue + } + ret = append(ret, float64(*l.LoadPercentage)) + } + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/binary.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/binary.go new file mode 100644 index 000000000..418e591f4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/binary.go @@ -0,0 +1,634 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package binary implements simple translation between numbers and byte +// sequences and encoding and decoding of varints. +// +// Numbers are translated by reading and writing fixed-size values. +// A fixed-size value is either a fixed-size arithmetic +// type (int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// +// The varint functions encode and decode single integer values using +// a variable-length encoding; smaller values require fewer bytes. +// For a specification, see +// http://code.google.com/apis/protocolbuffers/docs/encoding.html. +// +// This package favors simplicity over efficiency. Clients that require +// high-performance serialization, especially for large data structures, +// should look at more advanced solutions such as the encoding/gob +// package or protocol buffers. +package disk + +import ( + "errors" + "io" + "math" + "reflect" +) + +// A ByteOrder specifies how to convert byte sequences into +// 16-, 32-, or 64-bit unsigned integers. +type ByteOrder interface { + Uint16([]byte) uint16 + Uint32([]byte) uint32 + Uint64([]byte) uint64 + PutUint16([]byte, uint16) + PutUint32([]byte, uint32) + PutUint64([]byte, uint64) + String() string +} + +// LittleEndian is the little-endian implementation of ByteOrder. +var LittleEndian littleEndian + +// BigEndian is the big-endian implementation of ByteOrder. +var BigEndian bigEndian + +type littleEndian struct{} + +func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } + +func (littleEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v) + b[1] = byte(v >> 8) +} + +func (littleEndian) Uint32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (littleEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) +} + +func (littleEndian) Uint64(b []byte) uint64 { + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +func (littleEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} + +func (littleEndian) String() string { return "LittleEndian" } + +func (littleEndian) GoString() string { return "binary.LittleEndian" } + +type bigEndian struct{} + +func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 } + +func (bigEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v >> 8) + b[1] = byte(v) +} + +func (bigEndian) Uint32(b []byte) uint32 { + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +func (bigEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) +} + +func (bigEndian) Uint64(b []byte) uint64 { + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +func (bigEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +func (bigEndian) String() string { return "BigEndian" } + +func (bigEndian) GoString() string { return "binary.BigEndian" } + +// Read reads structured binary data from r into data. +// Data must be a pointer to a fixed-size value or a slice +// of fixed-size values. +// Bytes read from r are decoded using the specified byte order +// and written to successive fields of the data. +// When reading into structs, the field data for fields with +// blank (_) field names is skipped; i.e., blank field names +// may be used for padding. +// When reading into a struct, all non-blank fields must be exported. +func Read(r io.Reader, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + if _, err := io.ReadFull(r, bs); err != nil { + return err + } + switch data := data.(type) { + case *int8: + *data = int8(b[0]) + case *uint8: + *data = b[0] + case *int16: + *data = int16(order.Uint16(bs)) + case *uint16: + *data = order.Uint16(bs) + case *int32: + *data = int32(order.Uint32(bs)) + case *uint32: + *data = order.Uint32(bs) + case *int64: + *data = int64(order.Uint64(bs)) + case *uint64: + *data = order.Uint64(bs) + case []int8: + for i, x := range bs { // Easier to loop over the input for 8-bit values. + data[i] = int8(x) + } + case []uint8: + copy(data, bs) + case []int16: + for i := range data { + data[i] = int16(order.Uint16(bs[2*i:])) + } + case []uint16: + for i := range data { + data[i] = order.Uint16(bs[2*i:]) + } + case []int32: + for i := range data { + data[i] = int32(order.Uint32(bs[4*i:])) + } + case []uint32: + for i := range data { + data[i] = order.Uint32(bs[4*i:]) + } + case []int64: + for i := range data { + data[i] = int64(order.Uint64(bs[8*i:])) + } + case []uint64: + for i := range data { + data[i] = order.Uint64(bs[8*i:]) + } + } + return nil + } + + // Fallback to reflect-based decoding. + v := reflect.ValueOf(data) + size := -1 + switch v.Kind() { + case reflect.Ptr: + v = v.Elem() + size = dataSize(v) + case reflect.Slice: + size = dataSize(v) + } + if size < 0 { + return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String()) + } + d := &decoder{order: order, buf: make([]byte, size)} + if _, err := io.ReadFull(r, d.buf); err != nil { + return err + } + d.value(v) + return nil +} + +// Write writes the binary representation of data into w. +// Data must be a fixed-size value or a slice of fixed-size +// values, or a pointer to such data. +// Bytes written to w are encoded using the specified byte order +// and read from successive fields of the data. +// When writing structs, zero values are written for fields +// with blank (_) field names. +func Write(w io.Writer, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + switch v := data.(type) { + case *int8: + bs = b[:1] + b[0] = byte(*v) + case int8: + bs = b[:1] + b[0] = byte(v) + case []int8: + for i, x := range v { + bs[i] = byte(x) + } + case *uint8: + bs = b[:1] + b[0] = *v + case uint8: + bs = b[:1] + b[0] = byte(v) + case []uint8: + bs = v + case *int16: + bs = b[:2] + order.PutUint16(bs, uint16(*v)) + case int16: + bs = b[:2] + order.PutUint16(bs, uint16(v)) + case []int16: + for i, x := range v { + order.PutUint16(bs[2*i:], uint16(x)) + } + case *uint16: + bs = b[:2] + order.PutUint16(bs, *v) + case uint16: + bs = b[:2] + order.PutUint16(bs, v) + case []uint16: + for i, x := range v { + order.PutUint16(bs[2*i:], x) + } + case *int32: + bs = b[:4] + order.PutUint32(bs, uint32(*v)) + case int32: + bs = b[:4] + order.PutUint32(bs, uint32(v)) + case []int32: + for i, x := range v { + order.PutUint32(bs[4*i:], uint32(x)) + } + case *uint32: + bs = b[:4] + order.PutUint32(bs, *v) + case uint32: + bs = b[:4] + order.PutUint32(bs, v) + case []uint32: + for i, x := range v { + order.PutUint32(bs[4*i:], x) + } + case *int64: + bs = b[:8] + order.PutUint64(bs, uint64(*v)) + case int64: + bs = b[:8] + order.PutUint64(bs, uint64(v)) + case []int64: + for i, x := range v { + order.PutUint64(bs[8*i:], uint64(x)) + } + case *uint64: + bs = b[:8] + order.PutUint64(bs, *v) + case uint64: + bs = b[:8] + order.PutUint64(bs, v) + case []uint64: + for i, x := range v { + order.PutUint64(bs[8*i:], x) + } + } + _, err := w.Write(bs) + return err + } + + // Fallback to reflect-based encoding. + v := reflect.Indirect(reflect.ValueOf(data)) + size := dataSize(v) + if size < 0 { + return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String()) + } + buf := make([]byte, size) + e := &encoder{order: order, buf: buf} + e.value(v) + _, err := w.Write(buf) + return err +} + +// Size returns how many bytes Write would generate to encode the value v, which +// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data. +// If v is neither of these, Size returns -1. +func Size(v interface{}) int { + return dataSize(reflect.Indirect(reflect.ValueOf(v))) +} + +// dataSize returns the number of bytes the actual data represented by v occupies in memory. +// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice +// it returns the length of the slice times the element size and does not count the memory +// occupied by the header. If the type of v is not acceptable, dataSize returns -1. +func dataSize(v reflect.Value) int { + if v.Kind() == reflect.Slice { + if s := sizeof(v.Type().Elem()); s >= 0 { + return s * v.Len() + } + return -1 + } + return sizeof(v.Type()) +} + +// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable. +func sizeof(t reflect.Type) int { + switch t.Kind() { + case reflect.Array: + if s := sizeof(t.Elem()); s >= 0 { + return s * t.Len() + } + + case reflect.Struct: + sum := 0 + for i, n := 0, t.NumField(); i < n; i++ { + s := sizeof(t.Field(i).Type) + if s < 0 { + return -1 + } + sum += s + } + return sum + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.Ptr: + return int(t.Size()) + } + + return -1 +} + +type coder struct { + order ByteOrder + buf []byte +} + +type decoder coder +type encoder coder + +func (d *decoder) uint8() uint8 { + x := d.buf[0] + d.buf = d.buf[1:] + return x +} + +func (e *encoder) uint8(x uint8) { + e.buf[0] = x + e.buf = e.buf[1:] +} + +func (d *decoder) uint16() uint16 { + x := d.order.Uint16(d.buf[0:2]) + d.buf = d.buf[2:] + return x +} + +func (e *encoder) uint16(x uint16) { + e.order.PutUint16(e.buf[0:2], x) + e.buf = e.buf[2:] +} + +func (d *decoder) uint32() uint32 { + x := d.order.Uint32(d.buf[0:4]) + d.buf = d.buf[4:] + return x +} + +func (e *encoder) uint32(x uint32) { + e.order.PutUint32(e.buf[0:4], x) + e.buf = e.buf[4:] +} + +func (d *decoder) uint64() uint64 { + x := d.order.Uint64(d.buf[0:8]) + d.buf = d.buf[8:] + return x +} + +func (e *encoder) uint64(x uint64) { + e.order.PutUint64(e.buf[0:8], x) + e.buf = e.buf[8:] +} + +func (d *decoder) int8() int8 { return int8(d.uint8()) } + +func (e *encoder) int8(x int8) { e.uint8(uint8(x)) } + +func (d *decoder) int16() int16 { return int16(d.uint16()) } + +func (e *encoder) int16(x int16) { e.uint16(uint16(x)) } + +func (d *decoder) int32() int32 { return int32(d.uint32()) } + +func (e *encoder) int32(x int32) { e.uint32(uint32(x)) } + +func (d *decoder) int64() int64 { return int64(d.uint64()) } + +func (e *encoder) int64(x int64) { e.uint64(uint64(x)) } + +func (d *decoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // Note: Calling v.CanSet() below is an optimization. + // It would be sufficient to check the field name, + // but creating the StructField info for each field is + // costly (run "go test -bench=ReadStruct" and compare + // results when making changes to this code). + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + d.value(v) + } else { + d.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Int8: + v.SetInt(int64(d.int8())) + case reflect.Int16: + v.SetInt(int64(d.int16())) + case reflect.Int32: + v.SetInt(int64(d.int32())) + case reflect.Int64: + v.SetInt(d.int64()) + + case reflect.Uint8: + v.SetUint(uint64(d.uint8())) + case reflect.Uint16: + v.SetUint(uint64(d.uint16())) + case reflect.Uint32: + v.SetUint(uint64(d.uint32())) + case reflect.Uint64: + v.SetUint(d.uint64()) + + case reflect.Float32: + v.SetFloat(float64(math.Float32frombits(d.uint32()))) + case reflect.Float64: + v.SetFloat(math.Float64frombits(d.uint64())) + + case reflect.Complex64: + v.SetComplex(complex( + float64(math.Float32frombits(d.uint32())), + float64(math.Float32frombits(d.uint32())), + )) + case reflect.Complex128: + v.SetComplex(complex( + math.Float64frombits(d.uint64()), + math.Float64frombits(d.uint64()), + )) + } +} + +func (e *encoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // see comment for corresponding code in decoder.value() + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + e.value(v) + } else { + e.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch v.Type().Kind() { + case reflect.Int8: + e.int8(int8(v.Int())) + case reflect.Int16: + e.int16(int16(v.Int())) + case reflect.Int32: + e.int32(int32(v.Int())) + case reflect.Int64: + e.int64(v.Int()) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch v.Type().Kind() { + case reflect.Uint8: + e.uint8(uint8(v.Uint())) + case reflect.Uint16: + e.uint16(uint16(v.Uint())) + case reflect.Uint32: + e.uint32(uint32(v.Uint())) + case reflect.Uint64: + e.uint64(v.Uint()) + } + + case reflect.Float32, reflect.Float64: + switch v.Type().Kind() { + case reflect.Float32: + e.uint32(math.Float32bits(float32(v.Float()))) + case reflect.Float64: + e.uint64(math.Float64bits(v.Float())) + } + + case reflect.Complex64, reflect.Complex128: + switch v.Type().Kind() { + case reflect.Complex64: + x := v.Complex() + e.uint32(math.Float32bits(float32(real(x)))) + e.uint32(math.Float32bits(float32(imag(x)))) + case reflect.Complex128: + x := v.Complex() + e.uint64(math.Float64bits(real(x))) + e.uint64(math.Float64bits(imag(x))) + } + } +} + +func (d *decoder) skip(v reflect.Value) { + d.buf = d.buf[dataSize(v):] +} + +func (e *encoder) skip(v reflect.Value) { + n := dataSize(v) + for i := range e.buf[0:n] { + e.buf[i] = 0 + } + e.buf = e.buf[n:] +} + +// intDataSize returns the size of the data required to represent the data when encoded. +// It returns zero if the type cannot be implemented by the fast path in Read or Write. +func intDataSize(data interface{}) int { + switch data := data.(type) { + case int8, *int8, *uint8: + return 1 + case []int8: + return len(data) + case []uint8: + return len(data) + case int16, *int16, *uint16: + return 2 + case []int16: + return 2 * len(data) + case []uint16: + return 2 * len(data) + case int32, *int32, *uint32: + return 4 + case []int32: + return 4 * len(data) + case []uint32: + return 4 * len(data) + case int64, *int64, *uint64: + return 8 + case []int64: + return 8 * len(data) + case []uint64: + return 8 * len(data) + } + return 0 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk.go new file mode 100644 index 000000000..0aa26cd1b --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk.go @@ -0,0 +1,52 @@ +package disk + +import ( + "encoding/json" +) + +type DiskUsageStat struct { + Path string `json:"path"` + Fstype string `json:"fstype"` + Total uint64 `json:"total"` + Free uint64 `json:"free"` + Used uint64 `json:"used"` + UsedPercent float64 `json:"used_percent"` + InodesTotal uint64 `json:"inodes_total"` + InodesUsed uint64 `json:"inodes_used"` + InodesFree uint64 `json:"inodes_free"` + InodesUsedPercent float64 `json:"inodes_used_percent"` +} + +type DiskPartitionStat struct { + Device string `json:"device"` + Mountpoint string `json:"mountpoint"` + Fstype string `json:"fstype"` + Opts string `json:"opts"` +} + +type DiskIOCountersStat struct { + ReadCount uint64 `json:"read_count"` + WriteCount uint64 `json:"write_count"` + ReadBytes uint64 `json:"read_bytes"` + WriteBytes uint64 `json:"write_bytes"` + ReadTime uint64 `json:"read_time"` + WriteTime uint64 `json:"write_time"` + Name string `json:"name"` + IoTime uint64 `json:"io_time"` + SerialNumber string `json:"serial_number"` +} + +func (d DiskUsageStat) String() string { + s, _ := json.Marshal(d) + return string(s) +} + +func (d DiskPartitionStat) String() string { + s, _ := json.Marshal(d) + return string(s) +} + +func (d DiskIOCountersStat) String() string { + s, _ := json.Marshal(d) + return string(s) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_darwin.go new file mode 100644 index 000000000..095bbda63 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_darwin.go @@ -0,0 +1,104 @@ +// +build darwin + +package disk + +import ( + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +func DiskPartitions(all bool) ([]DiskPartitionStat, error) { + var ret []DiskPartitionStat + + count, err := Getfsstat(nil, MntWait) + if err != nil { + return ret, err + } + fs := make([]Statfs_t, count) + _, err = Getfsstat(fs, MntWait) + for _, stat := range fs { + opts := "rw" + if stat.Flags&MntReadOnly != 0 { + opts = "ro" + } + if stat.Flags&MntSynchronous != 0 { + opts += ",sync" + } + if stat.Flags&MntNoExec != 0 { + opts += ",noexec" + } + if stat.Flags&MntNoSuid != 0 { + opts += ",nosuid" + } + if stat.Flags&MntUnion != 0 { + opts += ",union" + } + if stat.Flags&MntAsync != 0 { + opts += ",async" + } + if stat.Flags&MntSuidDir != 0 { + opts += ",suiddir" + } + if stat.Flags&MntSoftDep != 0 { + opts += ",softdep" + } + if stat.Flags&MntNoSymFollow != 0 { + opts += ",nosymfollow" + } + if stat.Flags&MntGEOMJournal != 0 { + opts += ",gjounalc" + } + if stat.Flags&MntMultilabel != 0 { + opts += ",multilabel" + } + if stat.Flags&MntACLs != 0 { + opts += ",acls" + } + if stat.Flags&MntNoATime != 0 { + opts += ",noattime" + } + if stat.Flags&MntClusterRead != 0 { + opts += ",nocluster" + } + if stat.Flags&MntClusterWrite != 0 { + opts += ",noclusterw" + } + if stat.Flags&MntNFS4ACLs != 0 { + opts += ",nfs4acls" + } + d := DiskPartitionStat{ + Device: common.IntToString(stat.Mntfromname[:]), + Mountpoint: common.IntToString(stat.Mntonname[:]), + Fstype: common.IntToString(stat.Fstypename[:]), + Opts: opts, + } + ret = append(ret, d) + } + + return ret, nil +} + +func DiskIOCounters() (map[string]DiskIOCountersStat, error) { + return nil, common.NotImplementedError +} + +func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { + var _p0 unsafe.Pointer + var bufsize uintptr + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) + } + r0, _, e1 := syscall.Syscall(SYS_GETFSSTAT64, uintptr(_p0), bufsize, uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +func getFsType(stat syscall.Statfs_t) string { + return common.IntToString(stat.Fstypename[:]) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_darwin_amd64.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_darwin_amd64.go new file mode 100644 index 000000000..f58e21312 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_darwin_amd64.go @@ -0,0 +1,58 @@ +// +build darwin +// +build amd64 + +package disk + +const ( + MntWait = 1 + MfsNameLen = 15 /* length of fs type name, not inc. nul */ + MNameLen = 90 /* length of buffer for returned name */ + + MFSTYPENAMELEN = 16 /* length of fs type name including null */ + MAXPATHLEN = 1024 + MNAMELEN = MAXPATHLEN + + SYS_GETFSSTAT64 = 347 +) + +type Fsid struct{ val [2]int32 } /* file system id type */ +type uid_t int32 + +// sys/mount.h +const ( + MntReadOnly = 0x00000001 /* read only filesystem */ + MntSynchronous = 0x00000002 /* filesystem written synchronously */ + MntNoExec = 0x00000004 /* can't exec from filesystem */ + MntNoSuid = 0x00000008 /* don't honor setuid bits on fs */ + MntUnion = 0x00000020 /* union with underlying filesystem */ + MntAsync = 0x00000040 /* filesystem written asynchronously */ + MntSuidDir = 0x00100000 /* special handling of SUID on dirs */ + MntSoftDep = 0x00200000 /* soft updates being done */ + MntNoSymFollow = 0x00400000 /* do not follow symlinks */ + MntGEOMJournal = 0x02000000 /* GEOM journal support enabled */ + MntMultilabel = 0x04000000 /* MAC support for individual objects */ + MntACLs = 0x08000000 /* ACL support enabled */ + MntNoATime = 0x10000000 /* disable update of file access time */ + MntClusterRead = 0x40000000 /* disable cluster read */ + MntClusterWrite = 0x80000000 /* disable cluster write */ + MntNFS4ACLs = 0x00000010 +) + +type Statfs_t struct { + Bsize uint32 + Iosize int32 + Blocks uint64 + Bfree uint64 + Bavail uint64 + Files uint64 + Ffree uint64 + Fsid Fsid + Owner uint32 + Type uint32 + Flags uint32 + Fssubtype uint32 + Fstypename [16]int8 + Mntonname [1024]int8 + Mntfromname [1024]int8 + Reserved [8]uint32 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_freebsd.go new file mode 100644 index 000000000..11dd495d4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_freebsd.go @@ -0,0 +1,179 @@ +// +build freebsd + +package disk + +import ( + "bytes" + "encoding/binary" + "strconv" + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +const ( + CTLKern = 1 + // KernDevstat = 773 // for freebsd 8.4 + // KernDevstatAll = 772 // for freebsd 8.4 + KernDevstat = 974 + KernDevstatAll = 975 +) + +func DiskPartitions(all bool) ([]DiskPartitionStat, error) { + var ret []DiskPartitionStat + + // get length + count, err := syscall.Getfsstat(nil, MNT_WAIT) + if err != nil { + return ret, err + } + + fs := make([]Statfs, count) + _, err = Getfsstat(fs, MNT_WAIT) + + for _, stat := range fs { + opts := "rw" + if stat.Flags&MNT_RDONLY != 0 { + opts = "ro" + } + if stat.Flags&MNT_SYNCHRONOUS != 0 { + opts += ",sync" + } + if stat.Flags&MNT_NOEXEC != 0 { + opts += ",noexec" + } + if stat.Flags&MNT_NOSUID != 0 { + opts += ",nosuid" + } + if stat.Flags&MNT_UNION != 0 { + opts += ",union" + } + if stat.Flags&MNT_ASYNC != 0 { + opts += ",async" + } + if stat.Flags&MNT_SUIDDIR != 0 { + opts += ",suiddir" + } + if stat.Flags&MNT_SOFTDEP != 0 { + opts += ",softdep" + } + if stat.Flags&MNT_NOSYMFOLLOW != 0 { + opts += ",nosymfollow" + } + if stat.Flags&MNT_GJOURNAL != 0 { + opts += ",gjounalc" + } + if stat.Flags&MNT_MULTILABEL != 0 { + opts += ",multilabel" + } + if stat.Flags&MNT_ACLS != 0 { + opts += ",acls" + } + if stat.Flags&MNT_NOATIME != 0 { + opts += ",noattime" + } + if stat.Flags&MNT_NOCLUSTERR != 0 { + opts += ",nocluster" + } + if stat.Flags&MNT_NOCLUSTERW != 0 { + opts += ",noclusterw" + } + if stat.Flags&MNT_NFS4ACLS != 0 { + opts += ",nfs4acls" + } + + d := DiskPartitionStat{ + Device: common.IntToString(stat.Mntfromname[:]), + Mountpoint: common.IntToString(stat.Mntonname[:]), + Fstype: common.IntToString(stat.Fstypename[:]), + Opts: opts, + } + ret = append(ret, d) + } + + return ret, nil +} + +func DiskIOCounters() (map[string]DiskIOCountersStat, error) { + // statinfo->devinfo->devstat + // /usr/include/devinfo.h + + // sysctl.sysctl ('kern.devstat.all', 0) + ret := make(map[string]DiskIOCountersStat) + mib := []int32{CTLKern, KernDevstat, KernDevstatAll} + + buf, length, err := common.CallSyscall(mib) + if err != nil { + return nil, err + } + + ds := Devstat{} + devstatLen := int(unsafe.Sizeof(ds)) + count := int(length / uint64(devstatLen)) + + buf = buf[8:] // devstat.all has version in the head. + // parse buf to Devstat + for i := 0; i < count; i++ { + b := buf[i*devstatLen : i*devstatLen+devstatLen] + d, err := parseDevstat(b) + if err != nil { + continue + } + un := strconv.Itoa(int(d.Unit_number)) + name := common.IntToString(d.Device_name[:]) + un + + ds := DiskIOCountersStat{ + ReadCount: d.Operations[DEVSTAT_READ], + WriteCount: d.Operations[DEVSTAT_WRITE], + ReadBytes: d.Bytes[DEVSTAT_READ], + WriteBytes: d.Bytes[DEVSTAT_WRITE], + ReadTime: d.Duration[DEVSTAT_READ].Compute(), + WriteTime: d.Duration[DEVSTAT_WRITE].Compute(), + Name: name, + } + ret[name] = ds + } + + return ret, nil +} + +func (b Bintime) Compute() uint64 { + BINTIME_SCALE := 5.42101086242752217003726400434970855712890625e-20 + return uint64(b.Sec) + b.Frac*uint64(BINTIME_SCALE) +} + +// BT2LD(time) ((long double)(time).sec + (time).frac * BINTIME_SCALE) + +// Getfsstat is borrowed from pkg/syscall/syscall_freebsd.go +// change Statfs_t to Statfs in order to get more information +func Getfsstat(buf []Statfs, flags int) (n int, err error) { + var _p0 unsafe.Pointer + var bufsize uintptr + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf)) + } + r0, _, e1 := syscall.Syscall(syscall.SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +func parseDevstat(buf []byte) (Devstat, error) { + var ds Devstat + br := bytes.NewReader(buf) + // err := binary.Read(br, binary.LittleEndian, &ds) + err := Read(br, binary.LittleEndian, &ds) + if err != nil { + return ds, err + } + + return ds, nil +} + +func getFsType(stat syscall.Statfs_t) string { + return common.IntToString(stat.Fstypename[:]) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_freebsd_amd64.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_freebsd_amd64.go new file mode 100644 index 000000000..bbae1595c --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_freebsd_amd64.go @@ -0,0 +1,111 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package disk + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 + sizeofLongDouble = 0x8 + + DEVSTAT_NO_DATA = 0x00 + DEVSTAT_READ = 0x01 + DEVSTAT_WRITE = 0x02 + DEVSTAT_FREE = 0x03 + + MNT_RDONLY = 0x00000001 + MNT_SYNCHRONOUS = 0x00000002 + MNT_NOEXEC = 0x00000004 + MNT_NOSUID = 0x00000008 + MNT_UNION = 0x00000020 + MNT_ASYNC = 0x00000040 + MNT_SUIDDIR = 0x00100000 + MNT_SOFTDEP = 0x00200000 + MNT_NOSYMFOLLOW = 0x00400000 + MNT_GJOURNAL = 0x02000000 + MNT_MULTILABEL = 0x04000000 + MNT_ACLS = 0x08000000 + MNT_NOATIME = 0x10000000 + MNT_NOCLUSTERR = 0x40000000 + MNT_NOCLUSTERW = 0x80000000 + MNT_NFS4ACLS = 0x00000010 + + MNT_WAIT = 1 + MNT_NOWAIT = 2 + MNT_LAZY = 3 + MNT_SUSPEND = 4 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 + _C_long_double int64 +) + +type Statfs struct { + Version uint32 + Type uint32 + Flags uint64 + Bsize uint64 + Iosize uint64 + Blocks uint64 + Bfree uint64 + Bavail int64 + Files uint64 + Ffree int64 + Syncwrites uint64 + Asyncwrites uint64 + Syncreads uint64 + Asyncreads uint64 + Spare [10]uint64 + Namemax uint32 + Owner uint32 + Fsid Fsid + Charspare [80]int8 + Fstypename [16]int8 + Mntfromname [88]int8 + Mntonname [88]int8 +} +type Fsid struct { + Val [2]int32 +} + +type Devstat struct { + Sequence0 uint32 + Allocated int32 + Start_count uint32 + End_count uint32 + Busy_from Bintime + Dev_links _Ctype_struct___0 + Device_number uint32 + Device_name [16]int8 + Unit_number int32 + Bytes [4]uint64 + Operations [4]uint64 + Duration [4]Bintime + Busy_time Bintime + Creation_time Bintime + Block_size uint32 + Pad_cgo_0 [4]byte + Tag_types [3]uint64 + Flags uint32 + Device_type uint32 + Priority uint32 + Pad_cgo_1 [4]byte + Id *byte + Sequence1 uint32 + Pad_cgo_2 [4]byte +} +type Bintime struct { + Sec int64 + Frac uint64 +} + +type _Ctype_struct___0 struct { + Empty uint64 +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_linux.go new file mode 100644 index 000000000..d6c8cee7b --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_linux.go @@ -0,0 +1,327 @@ +// +build linux + +package disk + +import ( + "fmt" + "os/exec" + "strconv" + "strings" + "syscall" + + common "github.com/shirou/gopsutil/common" +) + +const ( + SectorSize = 512 +) +const ( + // man statfs + ADFS_SUPER_MAGIC = 0xadf5 + AFFS_SUPER_MAGIC = 0xADFF + BDEVFS_MAGIC = 0x62646576 + BEFS_SUPER_MAGIC = 0x42465331 + BFS_MAGIC = 0x1BADFACE + BINFMTFS_MAGIC = 0x42494e4d + BTRFS_SUPER_MAGIC = 0x9123683E + CGROUP_SUPER_MAGIC = 0x27e0eb + CIFS_MAGIC_NUMBER = 0xFF534D42 + CODA_SUPER_MAGIC = 0x73757245 + COH_SUPER_MAGIC = 0x012FF7B7 + CRAMFS_MAGIC = 0x28cd3d45 + DEBUGFS_MAGIC = 0x64626720 + DEVFS_SUPER_MAGIC = 0x1373 + DEVPTS_SUPER_MAGIC = 0x1cd1 + EFIVARFS_MAGIC = 0xde5e81e4 + EFS_SUPER_MAGIC = 0x00414A53 + EXT_SUPER_MAGIC = 0x137D + EXT2_OLD_SUPER_MAGIC = 0xEF51 + EXT2_SUPER_MAGIC = 0xEF53 + EXT3_SUPER_MAGIC = 0xEF53 + EXT4_SUPER_MAGIC = 0xEF53 + FUSE_SUPER_MAGIC = 0x65735546 + FUTEXFS_SUPER_MAGIC = 0xBAD1DEA + HFS_SUPER_MAGIC = 0x4244 + HOSTFS_SUPER_MAGIC = 0x00c0ffee + HPFS_SUPER_MAGIC = 0xF995E849 + HUGETLBFS_MAGIC = 0x958458f6 + ISOFS_SUPER_MAGIC = 0x9660 + JFFS2_SUPER_MAGIC = 0x72b6 + JFS_SUPER_MAGIC = 0x3153464a + MINIX_SUPER_MAGIC = 0x137F /* orig. minix */ + MINIX_SUPER_MAGIC2 = 0x138F /* 30 char minix */ + MINIX2_SUPER_MAGIC = 0x2468 /* minix V2 */ + MINIX2_SUPER_MAGIC2 = 0x2478 /* minix V2, 30 char names */ + MINIX3_SUPER_MAGIC = 0x4d5a /* minix V3 fs, 60 char names */ + MQUEUE_MAGIC = 0x19800202 + MSDOS_SUPER_MAGIC = 0x4d44 + NCP_SUPER_MAGIC = 0x564c + NFS_SUPER_MAGIC = 0x6969 + NILFS_SUPER_MAGIC = 0x3434 + NTFS_SB_MAGIC = 0x5346544e + OCFS2_SUPER_MAGIC = 0x7461636f + OPENPROM_SUPER_MAGIC = 0x9fa1 + PIPEFS_MAGIC = 0x50495045 + PROC_SUPER_MAGIC = 0x9fa0 + PSTOREFS_MAGIC = 0x6165676C + QNX4_SUPER_MAGIC = 0x002f + QNX6_SUPER_MAGIC = 0x68191122 + RAMFS_MAGIC = 0x858458f6 + REISERFS_SUPER_MAGIC = 0x52654973 + ROMFS_MAGIC = 0x7275 + SELINUX_MAGIC = 0xf97cff8c + SMACK_MAGIC = 0x43415d53 + SMB_SUPER_MAGIC = 0x517B + SOCKFS_MAGIC = 0x534F434B + SQUASHFS_MAGIC = 0x73717368 + SYSFS_MAGIC = 0x62656572 + SYSV2_SUPER_MAGIC = 0x012FF7B6 + SYSV4_SUPER_MAGIC = 0x012FF7B5 + TMPFS_MAGIC = 0x01021994 + UDF_SUPER_MAGIC = 0x15013346 + UFS_MAGIC = 0x00011954 + USBDEVICE_SUPER_MAGIC = 0x9fa2 + V9FS_MAGIC = 0x01021997 + VXFS_SUPER_MAGIC = 0xa501FCF5 + XENFS_SUPER_MAGIC = 0xabba1974 + XENIX_SUPER_MAGIC = 0x012FF7B4 + XFS_SUPER_MAGIC = 0x58465342 + _XIAFS_SUPER_MAGIC = 0x012FD16D + + AFS_SUPER_MAGIC = 0x5346414F + AUFS_SUPER_MAGIC = 0x61756673 + ANON_INODE_FS_SUPER_MAGIC = 0x09041934 + CEPH_SUPER_MAGIC = 0x00C36400 + ECRYPTFS_SUPER_MAGIC = 0xF15F + FAT_SUPER_MAGIC = 0x4006 + FHGFS_SUPER_MAGIC = 0x19830326 + FUSEBLK_SUPER_MAGIC = 0x65735546 + FUSECTL_SUPER_MAGIC = 0x65735543 + GFS_SUPER_MAGIC = 0x1161970 + GPFS_SUPER_MAGIC = 0x47504653 + MTD_INODE_FS_SUPER_MAGIC = 0x11307854 + INOTIFYFS_SUPER_MAGIC = 0x2BAD1DEA + ISOFS_R_WIN_SUPER_MAGIC = 0x4004 + ISOFS_WIN_SUPER_MAGIC = 0x4000 + JFFS_SUPER_MAGIC = 0x07C0 + KAFS_SUPER_MAGIC = 0x6B414653 + LUSTRE_SUPER_MAGIC = 0x0BD00BD0 + NFSD_SUPER_MAGIC = 0x6E667364 + PANFS_SUPER_MAGIC = 0xAAD7AAEA + RPC_PIPEFS_SUPER_MAGIC = 0x67596969 + SECURITYFS_SUPER_MAGIC = 0x73636673 + UFS_BYTESWAPPED_SUPER_MAGIC = 0x54190100 + VMHGFS_SUPER_MAGIC = 0xBACBACBC + VZFS_SUPER_MAGIC = 0x565A4653 + ZFS_SUPER_MAGIC = 0x2FC12FC1 +) + +// coreutils/src/stat.c +var fsTypeMap = map[int64]string{ + ADFS_SUPER_MAGIC: "adfs", /* 0xADF5 local */ + AFFS_SUPER_MAGIC: "affs", /* 0xADFF local */ + AFS_SUPER_MAGIC: "afs", /* 0x5346414F remote */ + ANON_INODE_FS_SUPER_MAGIC: "anon-inode FS", /* 0x09041934 local */ + AUFS_SUPER_MAGIC: "aufs", /* 0x61756673 remote */ + // AUTOFS_SUPER_MAGIC: "autofs", /* 0x0187 local */ + BEFS_SUPER_MAGIC: "befs", /* 0x42465331 local */ + BDEVFS_MAGIC: "bdevfs", /* 0x62646576 local */ + BFS_MAGIC: "bfs", /* 0x1BADFACE local */ + BINFMTFS_MAGIC: "binfmt_misc", /* 0x42494E4D local */ + BTRFS_SUPER_MAGIC: "btrfs", /* 0x9123683E local */ + CEPH_SUPER_MAGIC: "ceph", /* 0x00C36400 remote */ + CGROUP_SUPER_MAGIC: "cgroupfs", /* 0x0027E0EB local */ + CIFS_MAGIC_NUMBER: "cifs", /* 0xFF534D42 remote */ + CODA_SUPER_MAGIC: "coda", /* 0x73757245 remote */ + COH_SUPER_MAGIC: "coh", /* 0x012FF7B7 local */ + CRAMFS_MAGIC: "cramfs", /* 0x28CD3D45 local */ + DEBUGFS_MAGIC: "debugfs", /* 0x64626720 local */ + DEVFS_SUPER_MAGIC: "devfs", /* 0x1373 local */ + DEVPTS_SUPER_MAGIC: "devpts", /* 0x1CD1 local */ + ECRYPTFS_SUPER_MAGIC: "ecryptfs", /* 0xF15F local */ + EFS_SUPER_MAGIC: "efs", /* 0x00414A53 local */ + EXT_SUPER_MAGIC: "ext", /* 0x137D local */ + EXT2_SUPER_MAGIC: "ext2/ext3", /* 0xEF53 local */ + EXT2_OLD_SUPER_MAGIC: "ext2", /* 0xEF51 local */ + FAT_SUPER_MAGIC: "fat", /* 0x4006 local */ + FHGFS_SUPER_MAGIC: "fhgfs", /* 0x19830326 remote */ + FUSEBLK_SUPER_MAGIC: "fuseblk", /* 0x65735546 remote */ + FUSECTL_SUPER_MAGIC: "fusectl", /* 0x65735543 remote */ + FUTEXFS_SUPER_MAGIC: "futexfs", /* 0x0BAD1DEA local */ + GFS_SUPER_MAGIC: "gfs/gfs2", /* 0x1161970 remote */ + GPFS_SUPER_MAGIC: "gpfs", /* 0x47504653 remote */ + HFS_SUPER_MAGIC: "hfs", /* 0x4244 local */ + HPFS_SUPER_MAGIC: "hpfs", /* 0xF995E849 local */ + HUGETLBFS_MAGIC: "hugetlbfs", /* 0x958458F6 local */ + MTD_INODE_FS_SUPER_MAGIC: "inodefs", /* 0x11307854 local */ + INOTIFYFS_SUPER_MAGIC: "inotifyfs", /* 0x2BAD1DEA local */ + ISOFS_SUPER_MAGIC: "isofs", /* 0x9660 local */ + ISOFS_R_WIN_SUPER_MAGIC: "isofs", /* 0x4004 local */ + ISOFS_WIN_SUPER_MAGIC: "isofs", /* 0x4000 local */ + JFFS_SUPER_MAGIC: "jffs", /* 0x07C0 local */ + JFFS2_SUPER_MAGIC: "jffs2", /* 0x72B6 local */ + JFS_SUPER_MAGIC: "jfs", /* 0x3153464A local */ + KAFS_SUPER_MAGIC: "k-afs", /* 0x6B414653 remote */ + LUSTRE_SUPER_MAGIC: "lustre", /* 0x0BD00BD0 remote */ + MINIX_SUPER_MAGIC: "minix", /* 0x137F local */ + MINIX_SUPER_MAGIC2: "minix (30 char.)", /* 0x138F local */ + MINIX2_SUPER_MAGIC: "minix v2", /* 0x2468 local */ + MINIX2_SUPER_MAGIC2: "minix v2 (30 char.)", /* 0x2478 local */ + MINIX3_SUPER_MAGIC: "minix3", /* 0x4D5A local */ + MQUEUE_MAGIC: "mqueue", /* 0x19800202 local */ + MSDOS_SUPER_MAGIC: "msdos", /* 0x4D44 local */ + NCP_SUPER_MAGIC: "novell", /* 0x564C remote */ + NFS_SUPER_MAGIC: "nfs", /* 0x6969 remote */ + NFSD_SUPER_MAGIC: "nfsd", /* 0x6E667364 remote */ + NILFS_SUPER_MAGIC: "nilfs", /* 0x3434 local */ + NTFS_SB_MAGIC: "ntfs", /* 0x5346544E local */ + OPENPROM_SUPER_MAGIC: "openprom", /* 0x9FA1 local */ + OCFS2_SUPER_MAGIC: "ocfs2", /* 0x7461636f remote */ + PANFS_SUPER_MAGIC: "panfs", /* 0xAAD7AAEA remote */ + PIPEFS_MAGIC: "pipefs", /* 0x50495045 remote */ + PROC_SUPER_MAGIC: "proc", /* 0x9FA0 local */ + PSTOREFS_MAGIC: "pstorefs", /* 0x6165676C local */ + QNX4_SUPER_MAGIC: "qnx4", /* 0x002F local */ + QNX6_SUPER_MAGIC: "qnx6", /* 0x68191122 local */ + RAMFS_MAGIC: "ramfs", /* 0x858458F6 local */ + REISERFS_SUPER_MAGIC: "reiserfs", /* 0x52654973 local */ + ROMFS_MAGIC: "romfs", /* 0x7275 local */ + RPC_PIPEFS_SUPER_MAGIC: "rpc_pipefs", /* 0x67596969 local */ + SECURITYFS_SUPER_MAGIC: "securityfs", /* 0x73636673 local */ + SELINUX_MAGIC: "selinux", /* 0xF97CFF8C local */ + SMB_SUPER_MAGIC: "smb", /* 0x517B remote */ + SOCKFS_MAGIC: "sockfs", /* 0x534F434B local */ + SQUASHFS_MAGIC: "squashfs", /* 0x73717368 local */ + SYSFS_MAGIC: "sysfs", /* 0x62656572 local */ + SYSV2_SUPER_MAGIC: "sysv2", /* 0x012FF7B6 local */ + SYSV4_SUPER_MAGIC: "sysv4", /* 0x012FF7B5 local */ + TMPFS_MAGIC: "tmpfs", /* 0x01021994 local */ + UDF_SUPER_MAGIC: "udf", /* 0x15013346 local */ + UFS_MAGIC: "ufs", /* 0x00011954 local */ + UFS_BYTESWAPPED_SUPER_MAGIC: "ufs", /* 0x54190100 local */ + USBDEVICE_SUPER_MAGIC: "usbdevfs", /* 0x9FA2 local */ + V9FS_MAGIC: "v9fs", /* 0x01021997 local */ + VMHGFS_SUPER_MAGIC: "vmhgfs", /* 0xBACBACBC remote */ + VXFS_SUPER_MAGIC: "vxfs", /* 0xA501FCF5 local */ + VZFS_SUPER_MAGIC: "vzfs", /* 0x565A4653 local */ + XENFS_SUPER_MAGIC: "xenfs", /* 0xABBA1974 local */ + XENIX_SUPER_MAGIC: "xenix", /* 0x012FF7B4 local */ + XFS_SUPER_MAGIC: "xfs", /* 0x58465342 local */ + _XIAFS_SUPER_MAGIC: "xia", /* 0x012FD16D local */ + ZFS_SUPER_MAGIC: "zfs", /* 0x2FC12FC1 local */ +} + +// Get disk partitions. +// should use setmntent(3) but this implement use /etc/mtab file +func DiskPartitions(all bool) ([]DiskPartitionStat, error) { + + filename := "/etc/mtab" + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + + ret := make([]DiskPartitionStat, 0, len(lines)) + + for _, line := range lines { + fields := strings.Fields(line) + d := DiskPartitionStat{ + Device: fields[0], + Mountpoint: fields[1], + Fstype: fields[2], + Opts: fields[3], + } + ret = append(ret, d) + } + + return ret, nil +} + +func DiskIOCounters() (map[string]DiskIOCountersStat, error) { + filename := "/proc/diskstats" + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + ret := make(map[string]DiskIOCountersStat, 0) + empty := DiskIOCountersStat{} + + for _, line := range lines { + fields := strings.Fields(line) + name := fields[2] + reads, err := strconv.ParseUint((fields[3]), 10, 64) + if err != nil { + return ret, err + } + rbytes, err := strconv.ParseUint((fields[5]), 10, 64) + if err != nil { + return ret, err + } + rtime, err := strconv.ParseUint((fields[6]), 10, 64) + if err != nil { + return ret, err + } + writes, err := strconv.ParseUint((fields[7]), 10, 64) + if err != nil { + return ret, err + } + wbytes, err := strconv.ParseUint((fields[9]), 10, 64) + if err != nil { + return ret, err + } + wtime, err := strconv.ParseUint((fields[10]), 10, 64) + if err != nil { + return ret, err + } + iotime, err := strconv.ParseUint((fields[12]), 10, 64) + if err != nil { + return ret, err + } + d := DiskIOCountersStat{ + ReadBytes: rbytes * SectorSize, + WriteBytes: wbytes * SectorSize, + ReadCount: reads, + WriteCount: writes, + ReadTime: rtime, + WriteTime: wtime, + IoTime: iotime, + } + if d == empty { + continue + } + d.Name = name + + d.SerialNumber = GetDiskSerialNumber(name) + ret[name] = d + } + return ret, nil +} + +func GetDiskSerialNumber(name string) string { + n := fmt.Sprintf("--name=%s", name) + out, err := exec.Command("/sbin/udevadm", "info", "--query=property", n).Output() + + // does not return error, just an empty string + if err != nil { + return "" + } + lines := strings.Split(string(out), "\n") + for _, line := range lines { + values := strings.Split(line, "=") + if len(values) < 2 || values[0] != "ID_SERIAL" { + // only get ID_SERIAL, not ID_SERIAL_SHORT + continue + } + return values[1] + } + return "" +} + +func getFsType(stat syscall.Statfs_t) string { + t := stat.Type + ret, ok := fsTypeMap[t] + if !ok { + return "" + } + return ret +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_test.go new file mode 100644 index 000000000..70eb675f4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_test.go @@ -0,0 +1,97 @@ +package disk + +import ( + "fmt" + "runtime" + "testing" +) + +func TestDisk_usage(t *testing.T) { + path := "/" + if runtime.GOOS == "windows" { + path = "C:" + } + v, err := DiskUsage(path) + if err != nil { + t.Errorf("error %v", err) + } + if v.Path != path { + t.Errorf("error %v", err) + } +} + +func TestDisk_partitions(t *testing.T) { + ret, err := DiskPartitions(false) + if err != nil || len(ret) == 0 { + t.Errorf("error %v", err) + } + empty := DiskPartitionStat{} + for _, disk := range ret { + if disk == empty { + t.Errorf("Could not get device info %v", disk) + } + } +} + +func TestDisk_io_counters(t *testing.T) { + ret, err := DiskIOCounters() + if err != nil { + t.Errorf("error %v", err) + } + if len(ret) == 0 { + t.Errorf("ret is empty, %v", ret) + } + empty := DiskIOCountersStat{} + for part, io := range ret { + if io == empty { + t.Errorf("io_counter error %v, %v", part, io) + } + } +} + +func TestDiskUsageStat_String(t *testing.T) { + v := DiskUsageStat{ + Path: "/", + Total: 1000, + Free: 2000, + Used: 3000, + UsedPercent: 50.1, + InodesTotal: 4000, + InodesUsed: 5000, + InodesFree: 6000, + InodesUsedPercent: 49.1, + Fstype: "ext4", + } + e := `{"path":"/","fstype":"ext4","total":1000,"free":2000,"used":3000,"used_percent":50.1,"inodes_total":4000,"inodes_used":5000,"inodes_free":6000,"inodes_used_percent":49.1}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("DiskUsageStat string is invalid: %v", v) + } +} + +func TestDiskPartitionStat_String(t *testing.T) { + v := DiskPartitionStat{ + Device: "sd01", + Mountpoint: "/", + Fstype: "ext4", + Opts: "ro", + } + e := `{"device":"sd01","mountpoint":"/","fstype":"ext4","opts":"ro"}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("DiskUsageStat string is invalid: %v", v) + } +} + +func TestDiskIOCountersStat_String(t *testing.T) { + v := DiskIOCountersStat{ + Name: "sd01", + ReadCount: 100, + WriteCount: 200, + ReadBytes: 300, + WriteBytes: 400, + SerialNumber: "SERIAL", + } + e := `{"read_count":100,"write_count":200,"read_bytes":300,"write_bytes":400,"read_time":0,"write_time":0,"name":"sd01","io_time":0,"serial_number":"SERIAL"}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("DiskUsageStat string is invalid: %v", v) + } +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_unix.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_unix.go new file mode 100644 index 000000000..f006c1ac5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_unix.go @@ -0,0 +1,30 @@ +// +build freebsd linux darwin + +package disk + +import "syscall" + +func DiskUsage(path string) (*DiskUsageStat, error) { + stat := syscall.Statfs_t{} + err := syscall.Statfs(path, &stat) + if err != nil { + return nil, err + } + bsize := stat.Bsize + + ret := &DiskUsageStat{ + Path: path, + Fstype: getFsType(stat), + Total: (uint64(stat.Blocks) * uint64(bsize)), + Free: (uint64(stat.Bfree) * uint64(bsize)), + InodesTotal: (uint64(stat.Files)), + InodesFree: (uint64(stat.Ffree)), + } + + ret.InodesUsed = (ret.InodesTotal - ret.InodesFree) + ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0 + ret.Used = (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(bsize) + ret.UsedPercent = (float64(ret.Used) / float64(ret.Total)) * 100.0 + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_windows.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_windows.go new file mode 100644 index 000000000..24f8b170e --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/disk_windows.go @@ -0,0 +1,155 @@ +// +build windows + +package disk + +import ( + "bytes" + "syscall" + "unsafe" + + "github.com/StackExchange/wmi" + + common "github.com/shirou/gopsutil/common" +) + +var ( + procGetDiskFreeSpaceExW = common.Modkernel32.NewProc("GetDiskFreeSpaceExW") + procGetLogicalDriveStringsW = common.Modkernel32.NewProc("GetLogicalDriveStringsW") + procGetDriveType = common.Modkernel32.NewProc("GetDriveTypeW") + provGetVolumeInformation = common.Modkernel32.NewProc("GetVolumeInformationW") +) + +var ( + FileFileCompression = int64(16) // 0x00000010 + FileReadOnlyVolume = int64(524288) // 0x00080000 +) + +type Win32_PerfFormattedData struct { + Name string + AvgDiskBytesPerRead uint64 + AvgDiskBytesPerWrite uint64 + AvgDiskReadQueueLength uint64 + AvgDiskWriteQueueLength uint64 + AvgDisksecPerRead uint64 + AvgDisksecPerWrite uint64 +} + +const WaitMSec = 500 + +func DiskUsage(path string) (*DiskUsageStat, error) { + ret := &DiskUsageStat{} + + lpFreeBytesAvailable := int64(0) + lpTotalNumberOfBytes := int64(0) + lpTotalNumberOfFreeBytes := int64(0) + diskret, _, err := procGetDiskFreeSpaceExW.Call( + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), + uintptr(unsafe.Pointer(&lpFreeBytesAvailable)), + uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)), + uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes))) + if diskret == 0 { + return nil, err + } + ret = &DiskUsageStat{ + Path: path, + Total: uint64(lpTotalNumberOfBytes), + Free: uint64(lpTotalNumberOfFreeBytes), + Used: uint64(lpTotalNumberOfBytes) - uint64(lpTotalNumberOfFreeBytes), + UsedPercent: (float64(lpTotalNumberOfBytes) - float64(lpTotalNumberOfFreeBytes)) / float64(lpTotalNumberOfBytes) * 100, + // InodesTotal: 0, + // InodesFree: 0, + // InodesUsed: 0, + // InodesUsedPercent: 0, + } + return ret, nil +} + +func DiskPartitions(all bool) ([]DiskPartitionStat, error) { + var ret []DiskPartitionStat + lpBuffer := make([]byte, 254) + diskret, _, err := procGetLogicalDriveStringsW.Call( + uintptr(len(lpBuffer)), + uintptr(unsafe.Pointer(&lpBuffer[0]))) + if diskret == 0 { + return ret, err + } + for _, v := range lpBuffer { + if v >= 65 && v <= 90 { + path := string(v) + ":" + if path == "A:" || path == "B:" { // skip floppy drives + continue + } + typepath, _ := syscall.UTF16PtrFromString(path) + typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath))) + if typeret == 0 { + return ret, syscall.GetLastError() + } + // 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 5: DRIVE_CDROM + + if typeret == 2 || typeret == 3 || typeret == 5 { + lpVolumeNameBuffer := make([]byte, 256) + lpVolumeSerialNumber := int64(0) + lpMaximumComponentLength := int64(0) + lpFileSystemFlags := int64(0) + lpFileSystemNameBuffer := make([]byte, 256) + volpath, _ := syscall.UTF16PtrFromString(string(v) + ":/") + driveret, _, err := provGetVolumeInformation.Call( + uintptr(unsafe.Pointer(volpath)), + uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])), + uintptr(len(lpVolumeNameBuffer)), + uintptr(unsafe.Pointer(&lpVolumeSerialNumber)), + uintptr(unsafe.Pointer(&lpMaximumComponentLength)), + uintptr(unsafe.Pointer(&lpFileSystemFlags)), + uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])), + uintptr(len(lpFileSystemNameBuffer))) + if driveret == 0 { + if typeret == 5 { + continue //device is not ready will happen if there is no disk in the drive + } + return ret, err + } + opts := "rw" + if lpFileSystemFlags&FileReadOnlyVolume != 0 { + opts = "ro" + } + if lpFileSystemFlags&FileFileCompression != 0 { + opts += ".compress" + } + + d := DiskPartitionStat{ + Mountpoint: path, + Device: path, + Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)), + Opts: opts, + } + ret = append(ret, d) + } + } + } + return ret, nil +} + +func DiskIOCounters() (map[string]DiskIOCountersStat, error) { + ret := make(map[string]DiskIOCountersStat, 0) + var dst []Win32_PerfFormattedData + + err := wmi.Query("SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk ", &dst) + if err != nil { + return ret, err + } + for _, d := range dst { + if len(d.Name) > 3 { // not get _Total or Harddrive + continue + } + ret[d.Name] = DiskIOCountersStat{ + Name: d.Name, + ReadCount: uint64(d.AvgDiskReadQueueLength), + WriteCount: d.AvgDiskWriteQueueLength, + ReadBytes: uint64(d.AvgDiskBytesPerRead), + WriteBytes: uint64(d.AvgDiskBytesPerWrite), + ReadTime: d.AvgDisksecPerRead, + WriteTime: d.AvgDisksecPerWrite, + } + } + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/types_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/types_freebsd.go new file mode 100644 index 000000000..44869042f --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/disk/types_freebsd.go @@ -0,0 +1,85 @@ +// +build ignore +// Hand writing: _Ctype_struct___0 + +/* +Input to cgo -godefs. + +*/ + +package disk + +/* +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +// because statinfo has long double snap_time, redefine with changing long long +struct statinfo2 { + long cp_time[CPUSTATES]; + long tk_nin; + long tk_nout; + struct devinfo *dinfo; + long long snap_time; +}; +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + sizeofPtr = C.sizeofPtr + sizeofShort = C.sizeof_short + sizeofInt = C.sizeof_int + sizeofLong = C.sizeof_long + sizeofLongLong = C.sizeof_longlong + sizeofLongDouble = C.sizeof_longlong + + DEVSTAT_NO_DATA = 0x00 + DEVSTAT_READ = 0x01 + DEVSTAT_WRITE = 0x02 + DEVSTAT_FREE = 0x03 + + // from sys/mount.h + MNT_RDONLY = 0x00000001 /* read only filesystem */ + MNT_SYNCHRONOUS = 0x00000002 /* filesystem written synchronously */ + MNT_NOEXEC = 0x00000004 /* can't exec from filesystem */ + MNT_NOSUID = 0x00000008 /* don't honor setuid bits on fs */ + MNT_UNION = 0x00000020 /* union with underlying filesystem */ + MNT_ASYNC = 0x00000040 /* filesystem written asynchronously */ + MNT_SUIDDIR = 0x00100000 /* special handling of SUID on dirs */ + MNT_SOFTDEP = 0x00200000 /* soft updates being done */ + MNT_NOSYMFOLLOW = 0x00400000 /* do not follow symlinks */ + MNT_GJOURNAL = 0x02000000 /* GEOM journal support enabled */ + MNT_MULTILABEL = 0x04000000 /* MAC support for individual objects */ + MNT_ACLS = 0x08000000 /* ACL support enabled */ + MNT_NOATIME = 0x10000000 /* disable update of file access time */ + MNT_NOCLUSTERR = 0x40000000 /* disable cluster read */ + MNT_NOCLUSTERW = 0x80000000 /* disable cluster write */ + MNT_NFS4ACLS = 0x00000010 + + MNT_WAIT = 1 /* synchronously wait for I/O to complete */ + MNT_NOWAIT = 2 /* start all I/O, but do not wait for it */ + MNT_LAZY = 3 /* push data not written by filesystem syncer */ + MNT_SUSPEND = 4 /* Suspend file system after sync */ + +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong + _C_long_double C.longlong +) + +type Statfs C.struct_statfs +type Fsid C.struct_fsid + +type Devstat C.struct_devstat +type Bintime C.struct_bintime diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker.go new file mode 100644 index 000000000..83a92be24 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker.go @@ -0,0 +1,37 @@ +package docker + +import "errors" + +var ErrDockerNotAvailable = errors.New("docker not available") +var ErrCgroupNotAvailable = errors.New("cgroup not available") + +type CgroupMemStat struct { + ContainerID string `json:"container_id"` + Cache uint64 `json:"cache"` + RSS uint64 `json:"rss"` + RSSHuge uint64 `json:"rss_huge"` + MappedFile uint64 `json:"mapped_file"` + Pgpgin uint64 `json:"pgpgin"` + Pgpgout uint64 `json:"pgpgout"` + Pgfault uint64 `json:"pgfault"` + Pgmajfault uint64 `json:"pgmajfault"` + InactiveAnon uint64 `json:"inactive_anon"` + ActiveAnon uint64 `json:"active_anon"` + InactiveFile uint64 `json:"inactive_file"` + ActiveFile uint64 `json:"active_file"` + Unevictable uint64 `json:"unevictable"` + HierarchicalMemoryLimit uint64 `json:"hierarchical_memory_limit"` + TotalCache uint64 `json:"total_cache"` + TotalRSS uint64 `json:"total_rss"` + TotalRSSHuge uint64 `json:"total_rss_huge"` + TotalMappedFile uint64 `json:"total_mapped_file"` + TotalPgpgIn uint64 `json:"total_pgpgin"` + TotalPgpgOut uint64 `json:"total_pgpgout"` + TotalPgFault uint64 `json:"total_pgfault"` + TotalPgMajFault uint64 `json:"total_pgmajfault"` + TotalInactiveAnon uint64 `json:"total_inactive_anon"` + TotalActiveAnon uint64 `json:"total_active_anon"` + TotalInactiveFile uint64 `json:"total_inactive_file"` + TotalActiveFile uint64 `json:"total_active_file"` + TotalUnevictable uint64 `json:"total_unevictable"` +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_linux.go new file mode 100644 index 000000000..1113e4cce --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_linux.go @@ -0,0 +1,170 @@ +// +build linux + +package docker + +import ( + "encoding/json" + "os/exec" + "path" + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" + cpu "github.com/shirou/gopsutil/cpu" +) + +// GetDockerIDList returnes a list of DockerID. +// This requires certain permission. +func GetDockerIDList() ([]string, error) { + path, err := exec.LookPath("docker") + if err != nil { + return nil, ErrDockerNotAvailable + } + + out, err := exec.Command(path, "ps", "-q", "--no-trunc").Output() + if err != nil { + return []string{}, err + } + lines := strings.Split(string(out), "\n") + ret := make([]string, 0, len(lines)) + + for _, l := range lines { + if l == "" { + continue + } + ret = append(ret, l) + } + + return ret, nil +} + +// CgroupCPU returnes specified cgroup id CPU status. +// containerid is same as docker id if you use docker. +// If you use container via systemd.slice, you could use +// containerid = docker-.scope and base=/sys/fs/cgroup/cpuacct/system.slice/ +func CgroupCPU(containerid string, base string) (*cpu.CPUTimesStat, error) { + if len(base) == 0 { + base = "/sys/fs/cgroup/cpuacct/docker" + } + path := path.Join(base, containerid, "cpuacct.stat") + + lines, err := common.ReadLines(path) + if err != nil { + return nil, err + } + // empty containerid means all cgroup + if len(containerid) == 0 { + containerid = "all" + } + ret := &cpu.CPUTimesStat{CPU: containerid} + for _, line := range lines { + fields := strings.Split(line, " ") + if fields[0] == "user" { + user, err := strconv.ParseFloat(fields[1], 64) + if err == nil { + ret.User = float64(user) + } + } + if fields[0] == "system" { + system, err := strconv.ParseFloat(fields[1], 64) + if err == nil { + ret.System = float64(system) + } + } + } + + return ret, nil +} + +func CgroupCPUDocker(containerid string) (*cpu.CPUTimesStat, error) { + return CgroupCPU(containerid, "/sys/fs/cgroup/cpuacct/docker") +} + +func CgroupMem(containerid string, base string) (*CgroupMemStat, error) { + if len(base) == 0 { + base = "/sys/fs/cgroup/memory/docker" + } + path := path.Join(base, containerid, "memory.stat") + // empty containerid means all cgroup + if len(containerid) == 0 { + containerid = "all" + } + lines, err := common.ReadLines(path) + if err != nil { + return nil, err + } + ret := &CgroupMemStat{ContainerID: containerid} + for _, line := range lines { + fields := strings.Split(line, " ") + v, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + switch fields[0] { + case "cache": + ret.Cache = v + case "rss": + ret.RSS = v + case "rss_huge": + ret.RSSHuge = v + case "mapped_file": + ret.MappedFile = v + case "pgpgin": + ret.Pgpgin = v + case "pgpgout": + ret.Pgpgout = v + case "pgfault": + ret.Pgfault = v + case "pgmajfault": + ret.Pgmajfault = v + case "inactive_anon": + ret.InactiveAnon = v + case "active_anon": + ret.ActiveAnon = v + case "inactive_file": + ret.InactiveFile = v + case "active_file": + ret.ActiveFile = v + case "unevictable": + ret.Unevictable = v + case "hierarchical_memory_limit": + ret.HierarchicalMemoryLimit = v + case "total_cache": + ret.TotalCache = v + case "total_rss": + ret.TotalRSS = v + case "total_rss_huge": + ret.TotalRSSHuge = v + case "total_mapped_file": + ret.TotalMappedFile = v + case "total_pgpgin": + ret.TotalPgpgIn = v + case "total_pgpgout": + ret.TotalPgpgOut = v + case "total_pgfault": + ret.TotalPgFault = v + case "total_pgmajfault": + ret.TotalPgMajFault = v + case "total_inactive_anon": + ret.TotalInactiveAnon = v + case "total_active_anon": + ret.TotalActiveAnon = v + case "total_inactive_file": + ret.TotalInactiveFile = v + case "total_active_file": + ret.TotalActiveFile = v + case "total_unevictable": + ret.TotalUnevictable = v + } + } + return ret, nil +} + +func CgroupMemDocker(containerid string) (*CgroupMemStat, error) { + return CgroupMem(containerid, "/sys/fs/cgroup/memory/docker") +} + +func (m CgroupMemStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_linux_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_linux_test.go new file mode 100644 index 000000000..48cedfba7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_linux_test.go @@ -0,0 +1,60 @@ +// +build linux + +package docker + +import ( + "testing" +) + +func TestGetDockerIDList(t *testing.T) { + // If there is not docker environment, this test always fail. + // not tested here + /* + _, err := GetDockerIDList() + if err != nil { + t.Errorf("error %v", err) + } + */ +} + +func TestCgroupCPU(t *testing.T) { + v, _ := GetDockerIDList() + for _, id := range v { + v, err := CgroupCPUDocker(id) + if err != nil { + t.Errorf("error %v", err) + } + if v.CPU == "" { + t.Errorf("could not get CgroupCPU %v", v) + } + + } +} + +func TestCgroupCPUInvalidId(t *testing.T) { + _, err := CgroupCPUDocker("bad id") + if err == nil { + t.Error("Expected path does not exist error") + } +} + +func TestCgroupMem(t *testing.T) { + v, _ := GetDockerIDList() + for _, id := range v { + v, err := CgroupMemDocker(id) + if err != nil { + t.Errorf("error %v", err) + } + empty := &CgroupMemStat{} + if v == empty { + t.Errorf("Could not CgroupMemStat %v", v) + } + } +} + +func TestCgroupMemInvalidId(t *testing.T) { + _, err := CgroupMemDocker("bad id") + if err == nil { + t.Error("Expected path does not exist error") + } +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_notlinux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_notlinux.go new file mode 100644 index 000000000..fb59e21cc --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/docker/docker_notlinux.go @@ -0,0 +1,40 @@ +// +build !linux + +package docker + +import ( + "encoding/json" + + "github.com/shirou/gopsutil/cpu" +) + +// GetDockerIDList returnes a list of DockerID. +// This requires certain permission. +func GetDockerIDList() ([]string, error) { + return nil, ErrDockerNotAvailable +} + +// CgroupCPU returnes specified cgroup id CPU status. +// containerid is same as docker id if you use docker. +// If you use container via systemd.slice, you could use +// containerid = docker-.scope and base=/sys/fs/cgroup/cpuacct/system.slice/ +func CgroupCPU(containerid string, base string) (*cpu.CPUTimesStat, error) { + return nil, ErrCgroupNotAvailable +} + +func CgroupCPUDocker(containerid string) (*cpu.CPUTimesStat, error) { + return CgroupCPU(containerid, "/sys/fs/cgroup/cpuacct/docker") +} + +func CgroupMem(containerid string, base string) (*CgroupMemStat, error) { + return nil, ErrCgroupNotAvailable +} + +func CgroupMemDocker(containerid string) (*CgroupMemStat, error) { + return CgroupMem(containerid, "/sys/fs/cgroup/memory/docker") +} + +func (m CgroupMemStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load.go new file mode 100644 index 000000000..58746e72e --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load.go @@ -0,0 +1,16 @@ +package load + +import ( + "encoding/json" +) + +type LoadAvgStat struct { + Load1 float64 `json:"load1"` + Load5 float64 `json:"load5"` + Load15 float64 `json:"load15"` +} + +func (l LoadAvgStat) String() string { + s, _ := json.Marshal(l) + return string(s) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_darwin.go new file mode 100644 index 000000000..d90a42902 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_darwin.go @@ -0,0 +1,37 @@ +// +build darwin + +package load + +import ( + "strconv" + + common "github.com/shirou/gopsutil/common" +) + +func LoadAvg() (*LoadAvgStat, error) { + values, err := common.DoSysctrl("vm.loadavg") + if err != nil { + return nil, err + } + + load1, err := strconv.ParseFloat(values[0], 64) + if err != nil { + return nil, err + } + load5, err := strconv.ParseFloat(values[1], 64) + if err != nil { + return nil, err + } + load15, err := strconv.ParseFloat(values[2], 64) + if err != nil { + return nil, err + } + + ret := &LoadAvgStat{ + Load1: float64(load1), + Load5: float64(load5), + Load15: float64(load15), + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_freebsd.go new file mode 100644 index 000000000..d82afbecc --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_freebsd.go @@ -0,0 +1,37 @@ +// +build freebsd + +package load + +import ( + "strconv" + + common "github.com/shirou/gopsutil/common" +) + +func LoadAvg() (*LoadAvgStat, error) { + values, err := common.DoSysctrl("vm.loadavg") + if err != nil { + return nil, err + } + + load1, err := strconv.ParseFloat(values[0], 64) + if err != nil { + return nil, err + } + load5, err := strconv.ParseFloat(values[1], 64) + if err != nil { + return nil, err + } + load15, err := strconv.ParseFloat(values[2], 64) + if err != nil { + return nil, err + } + + ret := &LoadAvgStat{ + Load1: float64(load1), + Load5: float64(load5), + Load15: float64(load15), + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_linux.go new file mode 100644 index 000000000..6c9926b3b --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_linux.go @@ -0,0 +1,40 @@ +// +build linux + +package load + +import ( + "io/ioutil" + "strconv" + "strings" +) + +func LoadAvg() (*LoadAvgStat, error) { + filename := "/proc/loadavg" + line, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + values := strings.Fields(string(line)) + + load1, err := strconv.ParseFloat(values[0], 64) + if err != nil { + return nil, err + } + load5, err := strconv.ParseFloat(values[1], 64) + if err != nil { + return nil, err + } + load15, err := strconv.ParseFloat(values[2], 64) + if err != nil { + return nil, err + } + + ret := &LoadAvgStat{ + Load1: load1, + Load5: load5, + Load15: load15, + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_test.go new file mode 100644 index 000000000..39cee399f --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_test.go @@ -0,0 +1,30 @@ +package load + +import ( + "fmt" + "testing" +) + +func TestLoad(t *testing.T) { + v, err := LoadAvg() + if err != nil { + t.Errorf("error %v", err) + } + + empty := &LoadAvgStat{} + if v == empty { + t.Errorf("error load: %v", v) + } +} + +func TestLoadAvgStat_String(t *testing.T) { + v := LoadAvgStat{ + Load1: 10.1, + Load5: 20.1, + Load15: 30.1, + } + e := `{"load1":10.1,"load5":20.1,"load15":30.1}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("LoadAvgStat string is invalid: %v", v) + } +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_windows.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_windows.go new file mode 100644 index 000000000..70ad565d8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/load/load_windows.go @@ -0,0 +1,13 @@ +// +build windows + +package load + +import ( + common "github.com/shirou/gopsutil/common" +) + +func LoadAvg() (*LoadAvgStat, error) { + ret := LoadAvgStat{} + + return &ret, common.NotImplementedError +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem.go new file mode 100644 index 000000000..67f8741e7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem.go @@ -0,0 +1,38 @@ +package mem + +import ( + "encoding/json" +) + +type VirtualMemoryStat struct { + Total uint64 `json:"total"` + Available uint64 `json:"available"` + Used uint64 `json:"used"` + UsedPercent float64 `json:"used_percent"` + Free uint64 `json:"free"` + Active uint64 `json:"active"` + Inactive uint64 `json:"inactive"` + Buffers uint64 `json:"buffers"` + Cached uint64 `json:"cached"` + Wired uint64 `json:"wired"` + Shared uint64 `json:"shared"` +} + +type SwapMemoryStat struct { + Total uint64 `json:"total"` + Used uint64 `json:"used"` + Free uint64 `json:"free"` + UsedPercent float64 `json:"used_percent"` + Sin uint64 `json:"sin"` + Sout uint64 `json:"sout"` +} + +func (m VirtualMemoryStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + +func (m SwapMemoryStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_darwin.go new file mode 100644 index 000000000..bd6d2e0d8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_darwin.go @@ -0,0 +1,109 @@ +// +build darwin + +package mem + +import ( + "os/exec" + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" +) + +func getPageSize() (uint64, error) { + out, err := exec.Command("pagesize").Output() + if err != nil { + return 0, err + } + o := strings.TrimSpace(string(out)) + p, err := strconv.ParseUint(o, 10, 64) + if err != nil { + return 0, err + } + + return p, nil +} + +// VirtualMemory returns VirtualmemoryStat. +func VirtualMemory() (*VirtualMemoryStat, error) { + p, err := getPageSize() + if err != nil { + return nil, err + } + + total, err := common.DoSysctrl("hw.memsize") + if err != nil { + return nil, err + } + free, err := common.DoSysctrl("vm.page_free_count") + if err != nil { + return nil, err + } + parsed := make([]uint64, 0, 7) + vv := []string{ + total[0], + free[0], + } + for _, target := range vv { + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + ret := &VirtualMemoryStat{ + Total: parsed[0], + Free: parsed[1] * p, + } + + // TODO: platform independent (worked freebsd?) + ret.Available = ret.Free + ret.Buffers + ret.Cached + + ret.Used = ret.Total - ret.Free + ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 + + return ret, nil +} + +// SwapMemory returns swapinfo. +func SwapMemory() (*SwapMemoryStat, error) { + var ret *SwapMemoryStat + + swapUsage, err := common.DoSysctrl("vm.swapusage") + if err != nil { + return ret, err + } + + total := strings.Replace(swapUsage[2], "M", "", 1) + used := strings.Replace(swapUsage[5], "M", "", 1) + free := strings.Replace(swapUsage[8], "M", "", 1) + + total_v, err := strconv.ParseFloat(total, 64) + if err != nil { + return nil, err + } + used_v, err := strconv.ParseFloat(used, 64) + if err != nil { + return nil, err + } + free_v, err := strconv.ParseFloat(free, 64) + if err != nil { + return nil, err + } + + u := float64(0) + if total_v != 0 { + u = ((total_v - free_v) / total_v) * 100.0 + } + + // vm.swapusage shows "M", multiply 1000 + ret = &SwapMemoryStat{ + Total: uint64(total_v * 1000), + Used: uint64(used_v * 1000), + Free: uint64(free_v * 1000), + UsedPercent: u, + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_freebsd.go new file mode 100644 index 000000000..a380519f9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_freebsd.go @@ -0,0 +1,131 @@ +// +build freebsd + +package mem + +import ( + "os/exec" + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" +) + +func VirtualMemory() (*VirtualMemoryStat, error) { + pageSize, err := common.DoSysctrl("vm.stats.vm.v_page_size") + if err != nil { + return nil, err + } + p, err := strconv.ParseUint(pageSize[0], 10, 64) + if err != nil { + return nil, err + } + + pageCount, err := common.DoSysctrl("vm.stats.vm.v_page_count") + if err != nil { + return nil, err + } + free, err := common.DoSysctrl("vm.stats.vm.v_free_count") + if err != nil { + return nil, err + } + active, err := common.DoSysctrl("vm.stats.vm.v_active_count") + if err != nil { + return nil, err + } + inactive, err := common.DoSysctrl("vm.stats.vm.v_inactive_count") + if err != nil { + return nil, err + } + cache, err := common.DoSysctrl("vm.stats.vm.v_cache_count") + if err != nil { + return nil, err + } + buffer, err := common.DoSysctrl("vfs.bufspace") + if err != nil { + return nil, err + } + wired, err := common.DoSysctrl("vm.stats.vm.v_wire_count") + if err != nil { + return nil, err + } + + parsed := make([]uint64, 0, 7) + vv := []string{ + pageCount[0], + free[0], + active[0], + inactive[0], + cache[0], + buffer[0], + wired[0], + } + for _, target := range vv { + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + ret := &VirtualMemoryStat{ + Total: parsed[0] * p, + Free: parsed[1] * p, + Active: parsed[2] * p, + Inactive: parsed[3] * p, + Cached: parsed[4] * p, + Buffers: parsed[5], + Wired: parsed[6] * p, + } + + // TODO: platform independent (worked freebsd?) + ret.Available = ret.Free + ret.Buffers + ret.Cached + + ret.Used = ret.Total - ret.Free + ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 + + return ret, nil +} + +// Return swapinfo +// FreeBSD can have multiple swap devices. but use only first device +func SwapMemory() (*SwapMemoryStat, error) { + out, err := exec.Command("swapinfo").Output() + if err != nil { + return nil, err + } + var ret *SwapMemoryStat + for _, line := range strings.Split(string(out), "\n") { + values := strings.Fields(line) + // skip title line + if len(values) == 0 || values[0] == "Device" { + continue + } + + u := strings.Replace(values[4], "%", "", 1) + total_v, err := strconv.ParseUint(values[1], 10, 64) + if err != nil { + return nil, err + } + used_v, err := strconv.ParseUint(values[2], 10, 64) + if err != nil { + return nil, err + } + free_v, err := strconv.ParseUint(values[3], 10, 64) + if err != nil { + return nil, err + } + up_v, err := strconv.ParseFloat(u, 64) + if err != nil { + return nil, err + } + + ret = &SwapMemoryStat{ + Total: total_v, + Used: used_v, + Free: free_v, + UsedPercent: up_v, + } + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_linux.go new file mode 100644 index 000000000..d27f231e0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_linux.go @@ -0,0 +1,92 @@ +// +build linux + +package mem + +import ( + "strconv" + "strings" + "syscall" + + common "github.com/shirou/gopsutil/common" +) + +func VirtualMemory() (*VirtualMemoryStat, error) { + filename := "/proc/meminfo" + lines, _ := common.ReadLines(filename) + + ret := &VirtualMemoryStat{} + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) != 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + value = strings.Replace(value, " kB", "", -1) + + t, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return ret, err + } + switch key { + case "MemTotal": + ret.Total = t * 1024 + case "MemFree": + ret.Free = t * 1024 + case "Buffers": + ret.Buffers = t * 1024 + case "Cached": + ret.Cached = t * 1024 + case "Active": + ret.Active = t * 1024 + case "Inactive": + ret.Inactive = t * 1024 + } + } + ret.Available = ret.Free + ret.Buffers + ret.Cached + ret.Used = ret.Total - ret.Free + ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 + + return ret, nil +} + +func SwapMemory() (*SwapMemoryStat, error) { + sysinfo := &syscall.Sysinfo_t{} + + if err := syscall.Sysinfo(sysinfo); err != nil { + return nil, err + } + ret := &SwapMemoryStat{ + Total: uint64(sysinfo.Totalswap), + Free: uint64(sysinfo.Freeswap), + } + ret.Used = ret.Total - ret.Free + //check Infinity + if ret.Total != 0 { + ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0 + } else { + ret.UsedPercent = 0 + } + lines, _ := common.ReadLines("/proc/vmstat") + for _, l := range lines { + fields := strings.Fields(l) + if len(fields) < 2 { + continue + } + switch fields[0] { + case "pswpin": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.Sin = value * 4 * 1024 + case "pswpout": + value, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + continue + } + ret.Sout = value * 4 * 1024 + } + } + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_test.go new file mode 100644 index 000000000..28693574a --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_test.go @@ -0,0 +1,55 @@ +package mem + +import ( + "fmt" + "testing" +) + +func TestVirtual_memory(t *testing.T) { + v, err := VirtualMemory() + if err != nil { + t.Errorf("error %v", err) + } + empty := &VirtualMemoryStat{} + if v == empty { + t.Errorf("error %v", v) + } +} + +func TestSwap_memory(t *testing.T) { + v, err := SwapMemory() + if err != nil { + t.Errorf("error %v", err) + } + empty := &SwapMemoryStat{} + if v == empty { + t.Errorf("error %v", v) + } +} + +func TestVirtualMemoryStat_String(t *testing.T) { + v := VirtualMemoryStat{ + Total: 10, + Available: 20, + Used: 30, + UsedPercent: 30.1, + Free: 40, + } + e := `{"total":10,"available":20,"used":30,"used_percent":30.1,"free":40,"active":0,"inactive":0,"buffers":0,"cached":0,"wired":0,"shared":0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("VirtualMemoryStat string is invalid: %v", v) + } +} + +func TestSwapMemoryStat_String(t *testing.T) { + v := SwapMemoryStat{ + Total: 10, + Used: 30, + Free: 40, + UsedPercent: 30.1, + } + e := `{"total":10,"used":30,"free":40,"used_percent":30.1,"sin":0,"sout":0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("SwapMemoryStat string is invalid: %v", v) + } +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_windows.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_windows.go new file mode 100644 index 000000000..985ab8fdf --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/mem/mem_windows.go @@ -0,0 +1,50 @@ +// +build windows + +package mem + +import ( + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +var ( + procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx") +) + +type MEMORYSTATUSEX struct { + cbSize uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 // in bytes + ullAvailPhys uint64 + ullTotalPageFile uint64 + ullAvailPageFile uint64 + ullTotalVirtual uint64 + ullAvailVirtual uint64 + ullAvailExtendedVirtual uint64 +} + +func VirtualMemory() (*VirtualMemoryStat, error) { + var memInfo MEMORYSTATUSEX + memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) + mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) + if mem == 0 { + return nil, syscall.GetLastError() + } + + ret := &VirtualMemoryStat{ + Total: memInfo.ullTotalPhys, + Available: memInfo.ullAvailPhys, + UsedPercent: float64(memInfo.dwMemoryLoad), + } + + ret.Used = ret.Total - ret.Available + return ret, nil +} + +func SwapMemory() (*SwapMemoryStat, error) { + ret := &SwapMemoryStat{} + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net.go new file mode 100644 index 000000000..506fe57e0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net.go @@ -0,0 +1,227 @@ +package net + +import ( + "encoding/json" + "fmt" + "net" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/common" +) + +var invoke common.Invoker + +func init() { + invoke = common.Invoke{} +} + +type NetIOCountersStat struct { + Name string `json:"name"` // interface name + BytesSent uint64 `json:"bytes_sent"` // number of bytes sent + BytesRecv uint64 `json:"bytes_recv"` // number of bytes received + PacketsSent uint64 `json:"packets_sent"` // number of packets sent + PacketsRecv uint64 `json:"packets_recv"` // 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) +} + +// Addr is implemented compatibility to psutil +type Addr struct { + IP string `json:"ip"` + Port uint32 `json:"port"` +} + +type NetConnectionStat struct { + Fd uint32 `json:"fd"` + Family uint32 `json:"family"` + Type uint32 `json:"type"` + Laddr Addr `json:"localaddr"` + Raddr Addr `json:"remoteaddr"` + Status string `json:"status"` + Pid int32 `json:"pid"` +} + +// NetInterfaceAddr is designed for represent interface addresses +type NetInterfaceAddr struct { + Addr string `json:"addr"` +} + +type NetInterfaceStat struct { + MTU int `json:"mtu"` // maximum transmission unit + Name string `json:"name"` // e.g., "en0", "lo0", "eth0.100" + HardwareAddr string `json:"hardwareaddr"` // IEEE MAC-48, EUI-48 and EUI-64 form + Flags []string `json:"flags"` // e.g., FlagUp, FlagLoopback, FlagMulticast + Addrs []NetInterfaceAddr `json:"addrs"` +} + +var constMap = map[string]int{ + "TCP": syscall.SOCK_STREAM, + "UDP": syscall.SOCK_DGRAM, + "IPv4": syscall.AF_INET, + "IPv6": syscall.AF_INET6, +} + +func (n NetIOCountersStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n NetConnectionStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (a Addr) String() string { + s, _ := json.Marshal(a) + return string(s) +} + +func (n NetInterfaceStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func (n NetInterfaceAddr) String() string { + s, _ := json.Marshal(n) + return string(s) +} + +func NetInterfaces() ([]NetInterfaceStat, error) { + is, err := net.Interfaces() + if err != nil { + return nil, err + } + ret := make([]NetInterfaceStat, 0, len(is)) + for _, ifi := range is { + + var flags []string + if ifi.Flags&net.FlagUp != 0 { + flags = append(flags, "up") + } + if ifi.Flags&net.FlagBroadcast != 0 { + flags = append(flags, "broadcast") + } + if ifi.Flags&net.FlagLoopback != 0 { + flags = append(flags, "loopback") + } + if ifi.Flags&net.FlagPointToPoint != 0 { + flags = append(flags, "pointtopoint") + } + if ifi.Flags&net.FlagMulticast != 0 { + flags = append(flags, "multicast") + } + + r := NetInterfaceStat{ + Name: ifi.Name, + MTU: ifi.MTU, + HardwareAddr: ifi.HardwareAddr.String(), + Flags: flags, + } + addrs, err := ifi.Addrs() + if err == nil { + r.Addrs = make([]NetInterfaceAddr, 0, len(addrs)) + for _, addr := range addrs { + r.Addrs = append(r.Addrs, NetInterfaceAddr{ + Addr: addr.String(), + }) + } + + } + ret = append(ret, r) + } + + return ret, nil +} + +func getNetIOCountersAll(n []NetIOCountersStat) ([]NetIOCountersStat, error) { + r := NetIOCountersStat{ + Name: "all", + } + for _, nic := range n { + r.BytesRecv += nic.BytesRecv + r.PacketsRecv += nic.PacketsRecv + r.Errin += nic.Errin + r.Dropin += nic.Dropin + r.BytesSent += nic.BytesSent + r.PacketsSent += nic.PacketsSent + r.Errout += nic.Errout + r.Dropout += nic.Dropout + } + + return []NetIOCountersStat{r}, nil +} + +func parseNetLine(line string) (NetConnectionStat, error) { + f := strings.Fields(line) + if len(f) < 9 { + return NetConnectionStat{}, fmt.Errorf("wrong line,%s", line) + } + + pid, err := strconv.Atoi(f[1]) + if err != nil { + return NetConnectionStat{}, err + } + fd, err := strconv.Atoi(strings.Trim(f[3], "u")) + if err != nil { + return NetConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3]) + } + netFamily, ok := constMap[f[4]] + if !ok { + return NetConnectionStat{}, fmt.Errorf("unknown family, %s", f[4]) + } + netType, ok := constMap[f[7]] + if !ok { + return NetConnectionStat{}, fmt.Errorf("unknown type, %s", f[7]) + } + + laddr, raddr, err := parseNetAddr(f[8]) + if err != nil { + return NetConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8]) + } + + n := NetConnectionStat{ + Fd: uint32(fd), + Family: uint32(netFamily), + Type: uint32(netType), + Laddr: laddr, + Raddr: raddr, + Pid: int32(pid), + } + if len(f) == 10 { + n.Status = strings.Trim(f[9], "()") + } + + return n, nil +} + +func parseNetAddr(line string) (laddr Addr, raddr Addr, err error) { + parse := func(l string) (Addr, error) { + host, port, err := net.SplitHostPort(l) + if err != nil { + return Addr{}, fmt.Errorf("wrong addr, %s", l) + } + lport, err := strconv.Atoi(port) + if err != nil { + return Addr{}, err + } + return Addr{IP: host, Port: uint32(lport)}, nil + } + + addrs := strings.Split(line, "->") + if len(addrs) == 0 { + return laddr, raddr, fmt.Errorf("wrong netaddr, %s", line) + } + laddr, err = parse(addrs[0]) + if len(addrs) == 2 { // remote addr exists + raddr, err = parse(addrs[1]) + if err != nil { + return laddr, raddr, err + } + } + + return laddr, raddr, err +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_darwin.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_darwin.go new file mode 100644 index 000000000..da722dc6e --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_darwin.go @@ -0,0 +1,147 @@ +// +build darwin + +package net + +import ( + "os/exec" + "strconv" + "strings" + + "github.com/shirou/gopsutil/common" +) + +// example of netstat -idbn output on yosemite +// Name Mtu Network Address Ipkts Ierrs Ibytes Opkts Oerrs Obytes Coll Drop +// lo0 16384 869107 0 169411755 869107 0 169411755 0 0 +// lo0 16384 ::1/128 ::1 869107 - 169411755 869107 - 169411755 - - +// lo0 16384 127 127.0.0.1 869107 - 169411755 869107 - 169411755 - - +func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { + out, err := exec.Command("/usr/sbin/netstat", "-ibdn").Output() + if err != nil { + return nil, err + } + + lines := strings.Split(string(out), "\n") + ret := make([]NetIOCountersStat, 0, len(lines)-1) + exists := make([]string, 0, len(ret)) + + for _, line := range lines { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + // skip first line + continue + } + if common.StringsHas(exists, values[0]) { + // skip if already get + continue + } + exists = append(exists, values[0]) + + base := 1 + // sometimes Address is ommitted + if len(values) < 11 { + base = 0 + } + + parsed := make([]uint64, 0, 7) + vv := []string{ + values[base+3], // Ipkts == PacketsRecv + values[base+4], // Ierrs == Errin + values[base+5], // Ibytes == BytesRecv + values[base+6], // Opkts == PacketsSent + values[base+7], // Oerrs == Errout + values[base+8], // Obytes == BytesSent + } + if len(values) == 12 { + vv = append(vv, values[base+10]) + } + + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + n := NetIOCountersStat{ + Name: values[0], + PacketsRecv: parsed[0], + Errin: parsed[1], + BytesRecv: parsed[2], + PacketsSent: parsed[3], + Errout: parsed[4], + BytesSent: parsed[5], + } + if len(parsed) == 7 { + n.Dropout = parsed[6] + } + ret = append(ret, n) + } + + if pernic == false { + return getNetIOCountersAll(ret) + } + + return ret, nil +} + +// Return a list of network connections opened by a process +func NetConnections(kind string) ([]NetConnectionStat, error) { + var ret []NetConnectionStat + + args := []string{"-i"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + fallthrough + case "all": + fallthrough + case "inet": + args = append(args, "tcp") + case "inet4": + args = append(args, "4") + case "inet6": + args = append(args, "6") + case "tcp": + args = append(args, "tcp") + case "tcp4": + args = append(args, "4tcp") + case "tcp6": + args = append(args, "6tcp") + case "udp": + args = append(args, "udp") + case "udp4": + args = append(args, "6udp") + case "udp6": + args = append(args, "6udp") + case "unix": + return ret, common.NotImplementedError + } + + // we can not use -F filter to get all of required information at once. + r, err := common.CallLsof(invoke, 0, args...) + if err != nil { + return nil, err + } + for _, rr := range r { + if strings.HasPrefix(rr, "COMMAND") { + continue + } + n, err := parseNetLine(rr) + if err != nil { + // fmt.Println(err) // TODO: should debug print? + continue + } + + ret = append(ret, n) + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_freebsd.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_freebsd.go new file mode 100644 index 000000000..7cc93a5cd --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_freebsd.go @@ -0,0 +1,89 @@ +// +build freebsd + +package net + +import ( + "os/exec" + "strconv" + "strings" + + "github.com/shirou/gopsutil/common" +) + +func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { + out, err := exec.Command("/usr/bin/netstat", "-ibdn").Output() + if err != nil { + return nil, err + } + + lines := strings.Split(string(out), "\n") + ret := make([]NetIOCountersStat, 0, len(lines)-1) + exists := make([]string, 0, len(ret)) + + for _, line := range lines { + values := strings.Fields(line) + if len(values) < 1 || values[0] == "Name" { + continue + } + if common.StringsHas(exists, values[0]) { + // skip if already get + continue + } + exists = append(exists, values[0]) + + base := 1 + // sometimes Address is ommitted + if len(values) < 13 { + base = 0 + } + + parsed := make([]uint64, 0, 8) + vv := []string{ + values[base+3], // PacketsRecv + values[base+4], // Errin + values[base+5], // Dropin + values[base+6], // BytesRecvn + values[base+7], // PacketSent + values[base+8], // Errout + values[base+9], // BytesSent + values[base+11], // Dropout + } + for _, target := range vv { + if target == "-" { + parsed = append(parsed, 0) + continue + } + + t, err := strconv.ParseUint(target, 10, 64) + if err != nil { + return nil, err + } + parsed = append(parsed, t) + } + + n := NetIOCountersStat{ + Name: values[0], + PacketsRecv: parsed[0], + Errin: parsed[1], + Dropin: parsed[2], + BytesRecv: parsed[3], + PacketsSent: parsed[4], + Errout: parsed[5], + BytesSent: parsed[6], + Dropout: parsed[7], + } + ret = append(ret, n) + } + + if pernic == false { + return getNetIOCountersAll(ret) + } + + return ret, nil +} + +func NetConnections(kind string) ([]NetConnectionStat, error) { + var ret []NetConnectionStat + + return ret, common.NotImplementedError +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_linux.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_linux.go new file mode 100644 index 000000000..de0d65a46 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_linux.go @@ -0,0 +1,145 @@ +// +build linux + +package net + +import ( + "strconv" + "strings" + + common "github.com/shirou/gopsutil/common" +) + +// NetIOCounters returnes network I/O statistics for every network +// interface installed on the system. If pernic argument is false, +// return only sum of all information (which name is 'all'). If true, +// every network interface installed on the system is returned +// separately. +func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { + filename := "/proc/net/dev" + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + + statlen := len(lines) - 1 + + ret := make([]NetIOCountersStat, 0, statlen) + + for _, line := range lines[2:] { + parts := strings.SplitN(line, ":", 2) + if len(parts) != 2 { + continue + } + interfaceName := strings.TrimSpace(parts[0]) + if interfaceName == "" { + continue + } + + fields := strings.Fields(strings.TrimSpace(parts[1])) + bytesRecv, err := strconv.ParseUint(fields[0], 10, 64) + if err != nil { + return ret, err + } + packetsRecv, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return ret, err + } + errIn, err := strconv.ParseUint(fields[2], 10, 64) + if err != nil { + return ret, err + } + dropIn, err := strconv.ParseUint(fields[3], 10, 64) + if err != nil { + return ret, err + } + bytesSent, err := strconv.ParseUint(fields[8], 10, 64) + if err != nil { + return ret, err + } + packetsSent, err := strconv.ParseUint(fields[9], 10, 64) + if err != nil { + return ret, err + } + errOut, err := strconv.ParseUint(fields[10], 10, 64) + if err != nil { + return ret, err + } + dropOut, err := strconv.ParseUint(fields[13], 10, 64) + if err != nil { + return ret, err + } + + nic := NetIOCountersStat{ + Name: interfaceName, + BytesRecv: bytesRecv, + PacketsRecv: packetsRecv, + Errin: errIn, + Dropin: dropIn, + BytesSent: bytesSent, + PacketsSent: packetsSent, + Errout: errOut, + Dropout: dropOut, + } + ret = append(ret, nic) + } + + if pernic == false { + return getNetIOCountersAll(ret) + } + + return ret, nil +} + +func NetConnections(kind string) ([]NetConnectionStat, error) { + var ret []NetConnectionStat + + args := []string{"-i"} + switch strings.ToLower(kind) { + default: + fallthrough + case "": + fallthrough + case "all": + fallthrough + case "inet": + args = append(args, "tcp") + case "inet4": + args = append(args, "4") + case "inet6": + args = append(args, "6") + case "tcp": + args = append(args, "tcp") + case "tcp4": + args = append(args, "4tcp") + case "tcp6": + args = append(args, "6tcp") + case "udp": + args = append(args, "udp") + case "udp4": + args = append(args, "6udp") + case "udp6": + args = append(args, "6udp") + case "unix": + return ret, common.NotImplementedError + } + + // we can not use -F filter to get all of required information at once. + r, err := common.CallLsof(invoke, 0, args...) + if err != nil { + return nil, err + } + for _, rr := range r { + if strings.HasPrefix(rr, "COMMAND") { + continue + } + n, err := parseNetLine(rr) + if err != nil { + // fmt.Println(err) // TODO: should debug print? + continue + } + + ret = append(ret, n) + } + + return ret, nil +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_test.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_test.go new file mode 100644 index 000000000..9337e53f9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_test.go @@ -0,0 +1,143 @@ +package net + +import ( + "fmt" + "os" + "testing" +) + +func TestAddrString(t *testing.T) { + v := Addr{IP: "192.168.0.1", Port: 8000} + + s := fmt.Sprintf("%v", v) + if s != "{\"ip\":\"192.168.0.1\",\"port\":8000}" { + t.Errorf("Addr string is invalid: %v", v) + } +} + +func TestNetIOCountersStatString(t *testing.T) { + v := NetIOCountersStat{ + Name: "test", + BytesSent: 100, + } + e := `{"name":"test","bytes_sent":100,"bytes_recv":0,"packets_sent":0,"packets_recv":0,"errin":0,"errout":0,"dropin":0,"dropout":0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("NetIOCountersStat string is invalid: %v", v) + } +} + +func TestNetConnectionStatString(t *testing.T) { + v := NetConnectionStat{ + Fd: 10, + Family: 10, + Type: 10, + } + e := `{"fd":10,"family":10,"type":10,"localaddr":{"ip":"","port":0},"remoteaddr":{"ip":"","port":0},"status":"","pid":0}` + if e != fmt.Sprintf("%v", v) { + t.Errorf("NetConnectionStat string is invalid: %v", v) + } + +} + +func TestNetIOCountersAll(t *testing.T) { + v, err := NetIOCounters(false) + per, err := NetIOCounters(true) + if err != nil { + t.Errorf("Could not get NetIOCounters: %v", err) + } + if len(v) != 1 { + t.Errorf("Could not get NetIOCounters: %v", v) + } + if v[0].Name != "all" { + t.Errorf("Invalid NetIOCounters: %v", v) + } + var pr uint64 + for _, p := range per { + pr += p.PacketsRecv + } + if v[0].PacketsRecv != pr { + t.Errorf("invalid sum value: %v, %v", v[0].PacketsRecv, pr) + } +} + +func TestNetIOCountersPerNic(t *testing.T) { + v, err := NetIOCounters(true) + if err != nil { + t.Errorf("Could not get NetIOCounters: %v", err) + } + if len(v) == 0 { + t.Errorf("Could not get NetIOCounters: %v", v) + } + for _, vv := range v { + if vv.Name == "" { + t.Errorf("Invalid NetIOCounters: %v", vv) + } + } +} + +func Test_getNetIOCountersAll(t *testing.T) { + n := []NetIOCountersStat{ + NetIOCountersStat{ + Name: "a", + BytesRecv: 10, + PacketsRecv: 10, + }, + NetIOCountersStat{ + Name: "b", + BytesRecv: 10, + PacketsRecv: 10, + Errin: 10, + }, + } + ret, err := getNetIOCountersAll(n) + if err != nil { + t.Error(err) + } + if len(ret) != 1 { + t.Errorf("invalid return count") + } + if ret[0].Name != "all" { + t.Errorf("invalid return name") + } + if ret[0].BytesRecv != 20 { + t.Errorf("invalid count bytesrecv") + } + if ret[0].Errin != 10 { + t.Errorf("invalid count errin") + } +} + +func TestNetInterfaces(t *testing.T) { + v, err := NetInterfaces() + if err != nil { + t.Errorf("Could not get NetInterfaceStat: %v", err) + } + if len(v) == 0 { + t.Errorf("Could not get NetInterfaceStat: %v", err) + } + for _, vv := range v { + if vv.Name == "" { + t.Errorf("Invalid NetInterface: %v", vv) + } + } +} + +func TestNetConnections(t *testing.T) { + if ci := os.Getenv("CI"); ci != "" { // skip if test on drone.io + return + } + + v, err := NetConnections("inet") + if err != nil { + t.Errorf("could not get NetConnections: %v", err) + } + if len(v) == 0 { + t.Errorf("could not get NetConnections: %v", v) + } + for _, vv := range v { + if vv.Family == 0 { + t.Errorf("invalid NetConnections: %v", vv) + } + } + +} diff --git a/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_windows.go b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_windows.go new file mode 100644 index 000000000..8937c4e89 --- /dev/null +++ b/Godeps/_workspace/src/github.com/shirou/gopsutil/net/net_windows.go @@ -0,0 +1,98 @@ +// +build windows + +package net + +import ( + "net" + "os" + "syscall" + "unsafe" + + common "github.com/shirou/gopsutil/common" +) + +var ( + modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll") + procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable") + procGetExtendedUdpTable = modiphlpapi.NewProc("GetExtendedUdpTable") +) + +const ( + TCPTableBasicListener = iota + TCPTableBasicConnections + TCPTableBasicAll + TCPTableOwnerPIDListener + TCPTableOwnerPIDConnections + TCPTableOwnerPIDAll + TCPTableOwnerModuleListener + TCPTableOwnerModuleConnections + TCPTableOwnerModuleAll +) + +func NetIOCounters(pernic bool) ([]NetIOCountersStat, error) { + ifs, err := net.Interfaces() + if err != nil { + return nil, err + } + + ai, err := getAdapterList() + if err != nil { + return nil, err + } + var ret []NetIOCountersStat + + for _, ifi := range ifs { + name := ifi.Name + for ; ai != nil; ai = ai.Next { + name = common.BytePtrToString(&ai.Description[0]) + c := NetIOCountersStat{ + Name: name, + } + + row := syscall.MibIfRow{Index: ai.Index} + e := syscall.GetIfEntry(&row) + if e != nil { + return nil, os.NewSyscallError("GetIfEntry", e) + } + c.BytesSent = uint64(row.OutOctets) + c.BytesRecv = uint64(row.InOctets) + c.PacketsSent = uint64(row.OutUcastPkts) + c.PacketsRecv = uint64(row.InUcastPkts) + c.Errin = uint64(row.InErrors) + c.Errout = uint64(row.OutErrors) + c.Dropin = uint64(row.InDiscards) + c.Dropout = uint64(row.OutDiscards) + + ret = append(ret, c) + } + } + + if pernic == false { + return getNetIOCountersAll(ret) + } + return ret, nil +} + +// Return a list of network connections opened by a process +func NetConnections(kind string) ([]NetConnectionStat, error) { + var ret []NetConnectionStat + + return ret, common.NotImplementedError +} + +// borrowed from src/pkg/net/interface_windows.go +func getAdapterList() (*syscall.IpAdapterInfo, error) { + b := make([]byte, 1000) + l := uint32(len(b)) + a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) + err := syscall.GetAdaptersInfo(a, &l) + if err == syscall.ERROR_BUFFER_OVERFLOW { + b = make([]byte, l) + a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) + err = syscall.GetAdaptersInfo(a, &l) + } + if err != nil { + return nil, os.NewSyscallError("GetAdaptersInfo", err) + } + return a, nil +}