Vendor psutils and remove neko

This commit is contained in:
Evan Phoenix 2015-04-03 17:22:31 -07:00
parent db74acb86d
commit 9c42aea28c
84 changed files with 9370 additions and 74 deletions

View File

@ -1,8 +1,9 @@
package system
import "github.com/stretchr/testify/mock"
import "github.com/shirou/gopsutil/load"
import (
"github.com/influxdb/tivan/plugins/system/ps/load"
"github.com/stretchr/testify/mock"
)
type MockPS struct {
mock.Mock

3
plugins/system/ps/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*~
#*
_obj

27
plugins/system/ps/LICENSE Normal file
View File

@ -0,0 +1,27 @@
gopsutil is distributed under BSD license reproduced below.
Copyright (c) 2014, WAKAYAMA Shirou
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the psutil authors nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,267 @@
gopsutil: psutil for golang
==============================
.. image:: https://drone.io/github.com/shirou/gopsutil/status.png
:target: https://drone.io/github.com/shirou/gopsutil
.. image:: https://coveralls.io/repos/shirou/gopsutil/badge.png?branch=master
:target: https://coveralls.io/r/shirou/gopsutil?branch=master
This is a port of psutil (http://pythonhosted.org/psutil/). The challenge is porting all
psutil functions on some architectures...
.. highlights:: Package Structure Changed!
Package (a.k.a. directory) structure has been changed!! see `issue 24 <https://github.com/shirou/gopsutil/issues/24>`_
.. highlights:: golang 1.4 will become REQUIRED!
Since syscall package becomes frozen, we should use golang/x/sys of golang 1.4 as soon as possible.
Available Architectures
------------------------------------
- FreeBSD i386/amd64
- Linux i386/amd64/arm(raspberry pi)
- Windows/amd64
- Darwin/amd64
All works are implemented without cgo by porting c struct to golang struct.
Usage
---------
.. code:: go
import (
"fmt"
"github.com/shirou/gopsutil/mem"
)
func main() {
v, _ := mem.VirtualMemory()
// almost every return value is a struct
fmt.Printf("Total: %v, Free:%v, UsedPercent:%f%%\n", v.Total, v.Free, v.UsedPercent)
// convert to JSON. String() is also implemented
fmt.Println(v)
}
The output is below.
::
Total: 3179569152, Free:284233728, UsedPercent:84.508194%
{"total":3179569152,"available":492572672,"used":2895335424,"usedPercent":84.50819439828305, (snip)}
Documentation
------------------------
see http://godoc.org/github.com/shirou/gopsutil
More Info
--------------------
Several methods have been added which are not present in psutil, but will provide useful information.
- host/HostInfo() (linux)
- Hostname
- Uptime
- Procs
- OS (ex: "linux")
- Platform (ex: "ubuntu", "arch")
- PlatformFamily (ex: "debian")
- PlatformVersion (ex: "Ubuntu 13.10")
- VirtualizationSystem (ex: "LXC")
- VirtualizationRole (ex: "guest"/"host")
- cpu/CPUInfo() (linux, freebsd)
- CPU (ex: 0, 1, ...)
- VendorID (ex: "GenuineIntel")
- Family
- Model
- Stepping
- PhysicalID
- CoreID
- Cores (ex: 2)
- ModelName (ex: "Intel(R) Core(TM) i7-2640M CPU @ 2.80GHz")
- Mhz
- CacheSize
- Flags (ex: "fpu vme de pse tsc msr pae mce cx8 ...")
- load/LoadAvg() (linux, freebsd)
- Load1
- Load5
- Load15
- docker/GetDockerIDList() (linux only)
- container id list ([]string)
- docker/CgroupCPU() (linux only)
- user
- system
- docker/CgroupMem() (linux only)
- various status
Some codes are ported from Ohai. many thanks.
Current Status
------------------
- x: work
- b: almost work but something broken
================= ====== ======= ====== =======
name Linux FreeBSD MacOSX Windows
cpu_times x x x
cpu_count x x x x
cpu_percent x x x
cpu_times_percent x x x
virtual_memory x x x x
swap_memory x x x
disk_partitions x x x x
disk_io_counters x x
disk_usage x x x x
net_io_counters x x b x
boot_time x x x x
users x x x x
pids x x x x
pid_exists x x x x
net_connections
================= ====== ======= ====== =======
Process class
^^^^^^^^^^^^^^^
================ ===== ======= ====== =======
name Linux FreeBSD MacOSX Windows
pid x x x x
ppid x x x x
name x x x x
cmdline x x x
create_time x
status x x x
cwd x
exe x x x
uids x x x
gids x x x
terminal x x x
io_counters x
nice x x x
num_fds x
num_ctx_switches x
num_threads x x x x
cpu_times x
memory_info x x x
memory_info_ex x
memory_maps x
open_files x
send_signal x x x
suspend x x x
resume x x x
terminate x x x
kill x x x
username x
ionice
rlimit
num_handlres
threads
cpu_percent
cpu_affinity
memory_percent
children
connections
is_running
================ ===== ======= ====== =======
Original Metrics
^^^^^^^^^^^^^^^^^^^
================== ===== ======= ====== =======
item Linux FreeBSD MacOSX Windows
**HostInfo**
hostname x x x x
uptime x x x
proces x x
os x x x x
platform x x x
platformfamiliy x x x
virtualization x
**CPU**
VendorID x x x x
Family x x x x
Model x x x x
Stepping x x x x
PhysicalID x
CoreID x
Cores x x
ModelName x x x x
**LoadAvg**
Load1 x x x
Load5 x x x
Load15 x x x
**GetDockerID**
container id x no no no
**CgroupsCPU**
user x no no no
system x no no no
**CgroupsMem**
various x no no no
================== ===== ======= ====== =======
- future work
- process_iter
- wait_procs
- Process class
- parent (use ppid instead)
- as_dict
- wait
License
------------
New BSD License (same as psutil)
Related Works
-----------------------
I have been influenced by the following great works:
- psutil: http://pythonhosted.org/psutil/
- dstat: https://github.com/dagwieers/dstat
- gosiger: https://github.com/cloudfoundry/gosigar/
- goprocinfo: https://github.com/c9s/goprocinfo
- go-ps: https://github.com/mitchellh/go-ps
- ohai: https://github.com/opscode/ohai/
- bosun: https://github.com/bosun-monitor/bosun/tree/master/cmd/scollector/collectors
- mackerel: https://github.com/mackerelio/mackerel-agent/tree/master/metrics
How to Contribute
---------------------------
1. Fork it
2. Create your feature branch (git checkout -b my-new-feature)
3. Commit your changes (git commit -am 'Add some feature')
4. Push to the branch (git push origin my-new-feature)
5. Create new Pull Request
My English is terrible, so documentation or correcting comments are also
welcome.

View File

@ -0,0 +1,151 @@
//
// 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"
"os"
"reflect"
"strconv"
"strings"
)
var NotImplementedError = errors.New("not implemented yet")
// ReadLines reads contents from file and splits them by new line.
// 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
}
// Check the target string slice containes src or not
func StringContains(target []string, src string) bool {
for _, t := range target {
if strings.TrimSpace(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
}

View File

@ -0,0 +1,60 @@
// +build darwin
package common
import (
"os/exec"
"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
}

View File

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

View File

@ -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 TestStringContains(t *testing.T) {
target, err := ReadLines("common_test.go")
if err != nil {
t.Error(err)
}
if !StringContains(target, "func TestStringContains(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")
}
}

View File

@ -0,0 +1,144 @@
// +build windows
package common
import (
"fmt"
"os/exec"
"strings"
"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])
}
// exec wmic and return lines splited by newline
func GetWmic(target string, query ...string) ([][]string, error) {
cmd := []string{target}
cmd = append(cmd, query...)
cmd = append(cmd, "/format:csv")
out, err := exec.Command("wmic", cmd...).Output()
if err != nil {
return [][]string{}, err
}
lines := strings.Split(string(out), "\r\r\n")
if len(lines) <= 2 {
return [][]string{}, fmt.Errorf("wmic result malformed: [%q]", lines)
}
var ret [][]string
for _, l := range lines[2:] { // skip first two lines
var lr []string
for _, r := range strings.Split(l, ",") {
if r == "" {
continue
}
lr = append(lr, strings.TrimSpace(r))
}
if len(lr) != 0 {
ret = append(ret, lr)
}
}
return ret, nil
}
// 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
}

View File

@ -0,0 +1,26 @@
#/bin/sh
# see http://www.songmu.jp/riji/entry/2015-01-15-goveralls-multi-package.html
set -e
# cleanup
cleanup() {
if [ $tmpprof != "" ] && [ -f $tmpprof ]; then
rm -f $tmpprof
fi
exit
}
trap cleanup INT QUIT TERM EXIT
# メインの処理
prof=${1:-".profile.cov"}
echo "mode: count" > $prof
gopath1=$(echo $GOPATH | cut -d: -f1)
for pkg in $(go list ./...); do
tmpprof=$gopath1/src/$pkg/profile.tmp
go test -covermode=count -coverprofile=$tmpprof $pkg
if [ -f $tmpprof ]; then
cat $tmpprof | tail -n +2 >> $prof
rm $tmpprof
fi
done

View File

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

View File

@ -0,0 +1,149 @@
// +build darwin
package cpu
import (
"fmt"
"os/exec"
"strconv"
"strings"
common "github.com/shirou/gopsutil/common"
)
// sys/resource.h
const (
CPUser = 0
CPNice = 1
CPSys = 2
CPIntr = 3
CPIdle = 4
CPUStates = 5
)
// time.h
const (
ClocksPerSec = 128
)
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) {
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]
}
// TODO:
// c.Mhz = mustParseFloat64(values[1])
}
return append(ret, c), nil
}

View File

@ -0,0 +1,134 @@
// +build freebsd
package cpu
import (
"fmt"
"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
)
// time.h
const (
ClocksPerSec = 128
)
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
}

View File

@ -0,0 +1,206 @@
// +build linux
package cpu
import (
"errors"
"os/exec"
"strconv"
"strings"
common "github.com/shirou/gopsutil/common"
)
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
}
var CLK_TCK = 100
func init() {
out, err := exec.Command("getconf", "CLK_TCK").CombinedOutput()
if err == nil {
i, err := strconv.Atoi(strings.TrimSpace(string(out)))
if err == nil {
CLK_TCK = i
}
}
}
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
}
stolen, err := strconv.ParseFloat(fields[8], 64)
if err != nil {
return nil, err
}
cpu_tick := float64(CLK_TCK)
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,
Stolen: float64(stolen) / cpu_tick,
}
if len(fields) > 9 { // Linux >= 2.6.11
steal, err := strconv.ParseFloat(fields[9], 64)
if err != nil {
return nil, err
}
ct.Steal = float64(steal)
}
if len(fields) > 10 { // Linux >= 2.6.24
guest, err := strconv.ParseFloat(fields[10], 64)
if err != nil {
return nil, err
}
ct.Guest = float64(guest)
}
if len(fields) > 11 { // Linux >= 3.2.0
guestNice, err := strconv.ParseFloat(fields[11], 64)
if err != nil {
return nil, err
}
ct.GuestNice = float64(guestNice)
}
return ct, nil
}

View File

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

View File

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

View File

@ -0,0 +1,101 @@
// +build windows
package cpu
import (
"strconv"
"syscall"
"time"
"unsafe"
common "github.com/shirou/gopsutil/common"
)
// 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
lines, err := common.GetWmic("cpu", "get", "Family,L2CacheSize,Manufacturer,Name,NumberOfLogicalProcessors,ProcessorId,Stepping")
if err != nil {
return ret, err
}
for i, t := range lines {
cache, err := strconv.Atoi(t[2])
if err != nil {
cache = 0
}
cores, err := strconv.Atoi(t[5])
if err != nil {
cores = 0
}
stepping := 0
if len(t) > 7 {
stepping, err = strconv.Atoi(t[6])
if err != nil {
stepping = 0
}
}
cpu := CPUInfoStat{
CPU: int32(i),
Family: t[1],
CacheSize: int32(cache),
VendorID: t[3],
ModelName: t[4],
Cores: int32(cores),
PhysicalID: t[6],
Stepping: int32(stepping),
Flags: []string{},
}
ret = append(ret, cpu)
}
return ret, nil
}
func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) {
ret := []float64{}
lines, err := common.GetWmic("cpu", "get", "loadpercentage")
if err != nil {
return ret, err
}
for _, l := range lines {
if len(l) < 2 {
continue
}
p, err := strconv.Atoi(l[1])
if err != nil {
p = 0
}
// but windows can only get one percent.
ret = append(ret, float64(p)/100.0)
}
return ret, nil
}

View File

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

View File

@ -0,0 +1,51 @@
package disk
import (
"encoding/json"
)
type DiskUsageStat struct {
Path string `json:"path"`
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)
}

View File

@ -0,0 +1,100 @@
// +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
}

View File

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

View File

@ -0,0 +1,173 @@
// +build freebsd
package disk
import (
"bytes"
"encoding/binary"
"strconv"
"syscall"
"unsafe"
common "github.com/shirou/gopsutil/common"
)
const (
CTLKern = 1
KernDevstat = 773
KernDevstatAll = 772
)
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
}

View File

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

View File

@ -0,0 +1,122 @@
// +build linux
package disk
import (
"fmt"
"os/exec"
"strconv"
"strings"
common "github.com/shirou/gopsutil/common"
)
const (
SectorSize = 512
)
// 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 ""
}

View File

@ -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", ret)
}
empty := DiskIOCountersStat{}
for part, io := range ret {
fmt.Println(io)
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,
}
e := `{"path":"/","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)
}
}

View File

@ -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,
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 = (ret.Total - ret.Free)
ret.UsedPercent = (float64(ret.Used) / float64(ret.Total)) * 100.0
return ret, nil
}

View File

@ -0,0 +1,196 @@
// +build windows
package disk
import (
"bytes"
"fmt"
"syscall"
"time"
"unsafe"
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
)
const WaitMSec = 500
func DiskUsage(path string) (DiskUsageStat, error) {
ret := DiskUsageStat{}
ret.Path = path
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 ret, err
}
ret.Total = uint64(lpTotalNumberOfBytes)
// ret.Free = uint64(lpFreeBytesAvailable) // python psutil does not use this
ret.Free = uint64(lpTotalNumberOfFreeBytes)
ret.Used = ret.Total - ret.Free
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
//TODO: implement inodes stat
ret.InodesTotal = 0
ret.InodesUsed = 0
ret.InodesFree = 0
ret.InodesUsedPercent = 0.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 {
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)
query, err := common.CreateQuery()
if err != nil {
return ret, err
}
drivebuf := make([]byte, 256)
r, _, err := procGetLogicalDriveStringsW.Call(
uintptr(len(drivebuf)),
uintptr(unsafe.Pointer(&drivebuf[0])))
if r == 0 {
return ret, err
}
drivemap := make(map[string][]*common.CounterInfo, 0)
for _, v := range drivebuf {
if v >= 65 && v <= 90 {
drive := string(v)
r, _, err = procGetDriveType.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(drive + `:\`))))
if r != common.DRIVE_FIXED {
continue
}
drivemap[drive] = make([]*common.CounterInfo, 0, 2)
var counter *common.CounterInfo
counter, err = common.CreateCounter(query,
"read",
fmt.Sprintf(`\PhysicalDisk(0 %s:)\Disk Reads/sec`, drive))
if err != nil {
return nil, err
}
drivemap[drive] = append(drivemap[drive], counter)
counter, err = common.CreateCounter(query,
"write",
fmt.Sprintf(`\PhysicalDisk(0 %s:)\Disk Writes/sec`, drive))
if err != nil {
return nil, err
}
drivemap[drive] = append(drivemap[drive], counter)
}
}
r, _, err = common.PdhCollectQueryData.Call(uintptr(query))
if r != 0 && err != nil {
return nil, err
}
time.Sleep(time.Duration(WaitMSec) * time.Millisecond)
r, _, err = common.PdhCollectQueryData.Call(uintptr(query))
if r != 0 && err != nil {
return nil, err
}
for drive, counters := range drivemap {
stat := DiskIOCountersStat{}
for _, v := range counters {
var fmtValue common.PDH_FMT_COUNTERVALUE_LARGE
r, _, err := common.PdhGetFormattedCounterValue.Call(uintptr(v.Counter), common.PDH_FMT_LARGE, uintptr(0), uintptr(unsafe.Pointer(&fmtValue)))
if r != 0 && r != common.PDH_INVALID_DATA {
return nil, err
}
switch v.PostName {
case "read":
stat.ReadCount = uint64(fmtValue.LargeValue)
case "write":
stat.WriteCount = uint64(fmtValue.LargeValue)
default:
return ret, fmt.Errorf("unknown postname: %s", v.PostName)
}
stat.Name = drive
}
ret[drive] = stat
}
return ret, nil
}

View File

@ -0,0 +1,85 @@
// +build ignore
// Hand writing: _Ctype_struct___0
/*
Input to cgo -godefs.
*/
package disk
/*
#include <sys/types.h>
#include <sys/mount.h>
#include <devstat.h>
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

1
plugins/system/ps/doc.go Normal file
View File

@ -0,0 +1 @@
package gopsutil

View File

@ -0,0 +1,193 @@
// +build linux
package docker
import (
"encoding/json"
"os/exec"
"path"
"strconv"
"strings"
common "github.com/shirou/gopsutil/common"
cpu "github.com/shirou/gopsutil/cpu"
)
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"`
InctiveFile 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"`
}
// GetDockerIDList returnes a list of DockerID.
// This requires certain permission.
func GetDockerIDList() ([]string, error) {
out, err := exec.Command("docker", "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 {
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-<container id>.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.InctiveFile = 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)
}

View File

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

View File

@ -0,0 +1,37 @@
package host
import (
"encoding/json"
)
// A HostInfoStat describes the host status.
// This is not in the psutil but it useful.
type HostInfoStat struct {
Hostname string `json:"hostname"`
Uptime uint64 `json:"uptime"`
Procs uint64 `json:"procs"` // number of processes
OS string `json:"os"` // ex: freebsd, linux
Platform string `json:"platform"` // ex: ubuntu, linuxmint
PlatformFamily string `json:"platform_family"` // ex: debian, rhel
PlatformVersion string `json:"platform_version"`
VirtualizationSystem string `json:"virtualization_system"`
VirtualizationRole string `json:"virtualization_role"` // guest or host
}
type UserStat struct {
User string `json:"user"`
Terminal string `json:"terminal"`
Host string `json:"host"`
Started int `json:"started"`
}
func (h HostInfoStat) String() string {
s, _ := json.Marshal(h)
return string(s)
}
func (u UserStat) String() string {
s, _ := json.Marshal(u)
return string(s)
}

View File

@ -0,0 +1,139 @@
// +build darwin
package host
import (
"bytes"
"encoding/binary"
"io/ioutil"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"unsafe"
common "github.com/shirou/gopsutil/common"
)
func HostInfo() (*HostInfoStat, error) {
ret := &HostInfoStat{
OS: runtime.GOOS,
PlatformFamily: "darwin",
}
hostname, err := os.Hostname()
if err != nil {
return ret, err
}
ret.Hostname = hostname
platform, family, version, err := GetPlatformInformation()
if err == nil {
ret.Platform = platform
ret.PlatformFamily = family
ret.PlatformVersion = version
}
system, role, err := GetVirtualization()
if err == nil {
ret.VirtualizationSystem = system
ret.VirtualizationRole = role
}
values, err := common.DoSysctrl("kern.boottime")
if err == nil {
// ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014
v := strings.Replace(values[2], ",", "", 1)
t, err := strconv.ParseUint(v, 10, 64)
if err != nil {
return ret, err
}
ret.Uptime = t
}
return ret, nil
}
func BootTime() (uint64, error) {
values, err := common.DoSysctrl("kern.boottime")
if err != nil {
return 0, err
}
// ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014
v := strings.Replace(values[2], ",", "", 1)
boottime, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return 0, err
}
return uint64(boottime), nil
}
func Users() ([]UserStat, error) {
utmpfile := "/var/run/utmpx"
var ret []UserStat
file, err := os.Open(utmpfile)
if err != nil {
return ret, err
}
buf, err := ioutil.ReadAll(file)
if err != nil {
return ret, err
}
u := Utmpx{}
entrySize := int(unsafe.Sizeof(u))
count := len(buf) / entrySize
for i := 0; i < count; i++ {
b := buf[i*entrySize : i*entrySize+entrySize]
var u Utmpx
br := bytes.NewReader(b)
err := binary.Read(br, binary.LittleEndian, &u)
if err != nil {
continue
}
if u.Type != 7 { // skip if not USERPROCESS
continue
}
user := UserStat{
User: common.IntToString(u.User[:]),
Terminal: common.IntToString(u.Line[:]),
Host: common.IntToString(u.Host[:]),
Started: int(u.Tv.Sec),
}
ret = append(ret, user)
}
return ret, nil
}
func GetPlatformInformation() (string, string, string, error) {
platform := ""
family := ""
version := ""
out, err := exec.Command("uname", "-s").Output()
if err == nil {
platform = strings.ToLower(strings.TrimSpace(string(out)))
}
out, err = exec.Command("uname", "-r").Output()
if err == nil {
version = strings.ToLower(strings.TrimSpace(string(out)))
}
return platform, family, version, nil
}
func GetVirtualization() (string, string, error) {
system := ""
role := ""
return system, role, nil
}

View File

@ -0,0 +1,19 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_darwin.go
package host
type Utmpx struct {
User [256]int8
Id [4]int8
Line [32]int8
Pid int32
Type int16
Pad_cgo_0 [6]byte
Tv Timeval
Host [256]int8
Pad [16]uint32
}
type Timeval struct {
Sec int32
}

View File

@ -0,0 +1,143 @@
// +build freebsd
package host
import (
"bytes"
"encoding/binary"
"io/ioutil"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"unsafe"
common "github.com/shirou/gopsutil/common"
)
const (
UTNameSize = 16 /* see MAXLOGNAME in <sys/param.h> */
UTLineSize = 8
UTHostSize = 16
)
func HostInfo() (*HostInfoStat, error) {
ret := &HostInfoStat{
OS: runtime.GOOS,
PlatformFamily: "freebsd",
}
hostname, err := os.Hostname()
if err != nil {
return ret, err
}
ret.Hostname = hostname
platform, family, version, err := GetPlatformInformation()
if err == nil {
ret.Platform = platform
ret.PlatformFamily = family
ret.PlatformVersion = version
}
system, role, err := GetVirtualization()
if err == nil {
ret.VirtualizationSystem = system
ret.VirtualizationRole = role
}
values, err := common.DoSysctrl("kern.boottime")
if err == nil {
// ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014
v := strings.Replace(values[2], ",", "", 1)
t, err := strconv.ParseUint(v, 10, 64)
if err != nil {
return ret, err
}
ret.Uptime = t
}
return ret, nil
}
func BootTime() (int64, error) {
values, err := common.DoSysctrl("kern.boottime")
if err != nil {
return 0, err
}
// ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014
v := strings.Replace(values[2], ",", "", 1)
boottime, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return 0, err
}
return boottime, nil
}
func Users() ([]UserStat, error) {
utmpfile := "/var/run/utmp"
var ret []UserStat
file, err := os.Open(utmpfile)
if err != nil {
return ret, err
}
buf, err := ioutil.ReadAll(file)
if err != nil {
return ret, err
}
u := Utmp{}
entrySize := int(unsafe.Sizeof(u))
count := len(buf) / entrySize
for i := 0; i < count; i++ {
b := buf[i*entrySize : i*entrySize+entrySize]
var u Utmp
br := bytes.NewReader(b)
err := binary.Read(br, binary.LittleEndian, &u)
if err != nil || u.Time == 0 {
continue
}
user := UserStat{
User: common.IntToString(u.Name[:]),
Terminal: common.IntToString(u.Line[:]),
Host: common.IntToString(u.Host[:]),
Started: int(u.Time),
}
ret = append(ret, user)
}
return ret, nil
}
func GetPlatformInformation() (string, string, string, error) {
platform := ""
family := ""
version := ""
out, err := exec.Command("uname", "-s").Output()
if err == nil {
platform = strings.ToLower(strings.TrimSpace(string(out)))
}
out, err = exec.Command("uname", "-r").Output()
if err == nil {
version = strings.ToLower(strings.TrimSpace(string(out)))
}
return platform, family, version, nil
}
func GetVirtualization() (string, string, error) {
system := ""
role := ""
return system, role, nil
}

View File

@ -0,0 +1,28 @@
// +build freebsd
// +build amd64
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs host/types_freebsd.go
package host
const (
sizeofPtr = 0x8
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x8
sizeofLongLong = 0x8
)
type (
_C_short int16
_C_int int32
_C_long int64
_C_long_long int64
)
type Utmp struct {
Line [8]int8
Name [16]int8
Host [16]int8
Time int32
}

View File

@ -0,0 +1,362 @@
// +build linux
package host
import (
"bytes"
"encoding/binary"
"io/ioutil"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"syscall"
"unsafe"
common "github.com/shirou/gopsutil/common"
)
type LSB struct {
ID string
Release string
Codename string
Description string
}
func HostInfo() (*HostInfoStat, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
ret := &HostInfoStat{
Hostname: hostname,
OS: runtime.GOOS,
}
platform, family, version, err := GetPlatformInformation()
if err == nil {
ret.Platform = platform
ret.PlatformFamily = family
ret.PlatformVersion = version
}
system, role, err := GetVirtualization()
if err == nil {
ret.VirtualizationSystem = system
ret.VirtualizationRole = role
}
uptime, err := BootTime()
if err == nil {
ret.Uptime = uptime
}
return ret, nil
}
func BootTime() (uint64, error) {
sysinfo := &syscall.Sysinfo_t{}
if err := syscall.Sysinfo(sysinfo); err != nil {
return 0, err
}
return uint64(sysinfo.Uptime), nil
}
func Users() ([]UserStat, error) {
utmpfile := "/var/run/utmp"
file, err := os.Open(utmpfile)
if err != nil {
return nil, err
}
buf, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
u := utmp{}
entrySize := int(unsafe.Sizeof(u))
count := len(buf) / entrySize
ret := make([]UserStat, 0, count)
for i := 0; i < count; i++ {
b := buf[i*entrySize : i*entrySize+entrySize]
var u utmp
br := bytes.NewReader(b)
err := binary.Read(br, binary.LittleEndian, &u)
if err != nil {
continue
}
user := UserStat{
User: common.IntToString(u.User[:]),
Terminal: common.IntToString(u.Line[:]),
Host: common.IntToString(u.Host[:]),
Started: int(u.Tv.TvSec),
}
ret = append(ret, user)
}
return ret, nil
}
func getLSB() (*LSB, error) {
ret := &LSB{}
if common.PathExists("/etc/lsb-release") {
contents, err := common.ReadLines("/etc/lsb-release")
if err != nil {
return ret, err // return empty
}
for _, line := range contents {
field := strings.Split(line, "=")
if len(field) < 2 {
continue
}
switch field[0] {
case "DISTRIB_ID":
ret.ID = field[1]
case "DISTRIB_RELEASE":
ret.Release = field[1]
case "DISTRIB_CODENAME":
ret.Codename = field[1]
case "DISTRIB_DESCRIPTION":
ret.Description = field[1]
}
}
} else if common.PathExists("/usr/bin/lsb_release") {
out, err := exec.Command("/usr/bin/lsb_release").Output()
if err != nil {
return ret, err
}
for _, line := range strings.Split(string(out), "\n") {
field := strings.Split(line, ":")
if len(field) < 2 {
continue
}
switch field[0] {
case "Distributor ID":
ret.ID = field[1]
case "Release":
ret.Release = field[1]
case "Codename":
ret.Codename = field[1]
case "Description":
ret.Description = field[1]
}
}
}
return ret, nil
}
func GetPlatformInformation() (platform string, family string, version string, err error) {
lsb, err := getLSB()
if err != nil {
lsb = &LSB{}
}
if common.PathExists("/etc/oracle-release") {
platform = "oracle"
contents, err := common.ReadLines("/etc/oracle-release")
if err == nil {
version = getRedhatishVersion(contents)
}
} else if common.PathExists("/etc/enterprise-release") {
platform = "oracle"
contents, err := common.ReadLines("/etc/enterprise-release")
if err == nil {
version = getRedhatishVersion(contents)
}
} else if common.PathExists("/etc/debian_version") {
if lsb.ID == "Ubuntu" {
platform = "ubuntu"
version = lsb.Release
} else if lsb.ID == "LinuxMint" {
platform = "linuxmint"
version = lsb.Release
} else {
if common.PathExists("/usr/bin/raspi-config") {
platform = "raspbian"
} else {
platform = "debian"
}
contents, err := common.ReadLines("/etc/debian_version")
if err == nil {
version = contents[0]
}
}
} else if common.PathExists("/etc/redhat-release") {
contents, err := common.ReadLines("/etc/redhat-release")
if err == nil {
version = getRedhatishVersion(contents)
platform = getRedhatishPlatform(contents)
}
} else if common.PathExists("/etc/system-release") {
contents, err := common.ReadLines("/etc/system-release")
if err == nil {
version = getRedhatishVersion(contents)
platform = getRedhatishPlatform(contents)
}
} else if common.PathExists("/etc/gentoo-release") {
platform = "gentoo"
contents, err := common.ReadLines("/etc/gentoo-release")
if err == nil {
version = getRedhatishVersion(contents)
}
// TODO: suse detection
// TODO: slackware detecion
} else if common.PathExists("/etc/arch-release") {
platform = "arch"
// TODO: exherbo detection
} else if lsb.ID == "RedHat" {
platform = "redhat"
version = lsb.Release
} else if lsb.ID == "Amazon" {
platform = "amazon"
version = lsb.Release
} else if lsb.ID == "ScientificSL" {
platform = "scientific"
version = lsb.Release
} else if lsb.ID == "XenServer" {
platform = "xenserver"
version = lsb.Release
} else if lsb.ID != "" {
platform = strings.ToLower(lsb.ID)
version = lsb.Release
}
switch platform {
case "debian", "ubuntu", "linuxmint", "raspbian":
family = "debian"
case "fedora":
family = "fedora"
case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm":
family = "rhel"
case "suse":
family = "suse"
case "gentoo":
family = "gentoo"
case "slackware":
family = "slackware"
case "arch":
family = "arch"
case "exherbo":
family = "exherbo"
}
return platform, family, version, nil
}
func getRedhatishVersion(contents []string) string {
c := strings.ToLower(strings.Join(contents, ""))
if strings.Contains(c, "rawhide") {
return "rawhide"
}
if matches := regexp.MustCompile(`release (\d[\d.]*)`).FindStringSubmatch(c); matches != nil {
return matches[1]
}
return ""
}
func getRedhatishPlatform(contents []string) string {
c := strings.ToLower(strings.Join(contents, ""))
if strings.Contains(c, "red hat") {
return "redhat"
}
f := strings.Split(c, " ")
return f[0]
}
func GetVirtualization() (string, string, error) {
var system string
var role string
if common.PathExists("/proc/xen") {
system = "xen"
role = "guest" // assume guest
if common.PathExists("/proc/xen/capabilities") {
contents, err := common.ReadLines("/proc/xen/capabilities")
if err == nil {
if common.StringContains(contents, "control_d") {
role = "host"
}
}
}
}
if common.PathExists("/proc/modules") {
contents, err := common.ReadLines("/proc/modules")
if err == nil {
if common.StringContains(contents, "kvm") {
system = "kvm"
role = "host"
} else if common.StringContains(contents, "vboxdrv") {
system = "vbox"
role = "host"
} else if common.StringContains(contents, "vboxguest") {
system = "vbox"
role = "guest"
}
}
}
if common.PathExists("/proc/cpuinfo") {
contents, err := common.ReadLines("/proc/cpuinfo")
if err == nil {
if common.StringContains(contents, "QEMU Virtual CPU") ||
common.StringContains(contents, "Common KVM processor") ||
common.StringContains(contents, "Common 32-bit KVM processor") {
system = "kvm"
role = "guest"
}
}
}
if common.PathExists("/proc/bc/0") {
system = "openvz"
role = "host"
} else if common.PathExists("/proc/vz") {
system = "openvz"
role = "guest"
}
// not use dmidecode because it requires root
if common.PathExists("/proc/self/status") {
contents, err := common.ReadLines("/proc/self/status")
if err == nil {
if common.StringContains(contents, "s_context:") ||
common.StringContains(contents, "VxID:") {
system = "linux-vserver"
}
// TODO: guest or host
}
}
if common.PathExists("/proc/self/cgroup") {
contents, err := common.ReadLines("/proc/self/cgroup")
if err == nil {
if common.StringContains(contents, "lxc") ||
common.StringContains(contents, "docker") {
system = "lxc"
role = "guest"
} else if common.PathExists("/usr/bin/lxc-version") { // TODO: which
system = "lxc"
role = "host"
}
}
}
return system, role, nil
}

View File

@ -0,0 +1,44 @@
// ATTENTION - FILE MANUAL FIXED AFTER CGO.
// Fixed line: Tv _Ctype_struct_timeval -> Tv UtTv
// Created by cgo -godefs, MANUAL FIXED
// cgo -godefs types_linux.go
package host
const (
sizeofPtr = 0x4
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x4
sizeofLongLong = 0x8
)
type (
_C_short int16
_C_int int32
_C_long int32
_C_long_long int64
)
type utmp struct {
Type int16
Pad_cgo_0 [2]byte
Pid int32
Line [32]int8
Id [4]int8
User [32]int8
Host [256]int8
Exit exit_status
Session int32
Tv UtTv
Addr_v6 [4]int32
X__unused [20]int8
}
type exit_status struct {
Termination int16
Exit int16
}
type UtTv struct {
TvSec int32
TvUsec int32
}

View File

@ -0,0 +1,42 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
package host
const (
sizeofPtr = 0x8
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x8
sizeofLongLong = 0x8
)
type (
_C_short int16
_C_int int32
_C_long int64
_C_long_long int64
)
type utmp struct {
Type int16
Pad_cgo_0 [2]byte
Pid int32
Line [32]int8
Id [4]int8
User [32]int8
Host [256]int8
Exit exit_status
Session int32
Tv UtTv
Addr_v6 [4]int32
X__glibc_reserved [20]int8
}
type exit_status struct {
Termination int16
Exit int16
}
type UtTv struct {
TvSec int32
TvUsec int32
}

View File

@ -0,0 +1,27 @@
// +build linux
// +build arm
package host
type exitStatus struct {
Etermination int16 // Process termination status.
Eexit int16 // Process exit status.
}
type timeval struct {
TvSec uint32 // Seconds.
TvUsec uint32 // Microseconds.
}
type utmp struct {
Type int16 // Type of login.
Pid int32 // Process ID of login process.
Line [32]byte // Devicename.
ID [4]byte // Inittab ID.
User [32]byte // Username.
Host [256]byte // Hostname for remote login.
Exit exitStatus // Exit status of a process marked
Session int32 // Session ID, used for windowing.
Tv timeval // Time entry was made.
AddrV6 [16]byte // Internet address of remote host.
Unused [20]byte // Reserved for future use. // original is 20
}

View File

@ -0,0 +1,61 @@
// +build linux
package host
import (
"testing"
)
func TestGetRedhatishVersion(t *testing.T) {
var ret string
c := []string{"Rawhide"}
ret = getRedhatishVersion(c)
if ret != "rawhide" {
t.Errorf("Could not get version rawhide: %v", ret)
}
c = []string{"Fedora release 15 (Lovelock)"}
ret = getRedhatishVersion(c)
if ret != "15" {
t.Errorf("Could not get version fedora: %v", ret)
}
c = []string{"Enterprise Linux Server release 5.5 (Carthage)"}
ret = getRedhatishVersion(c)
if ret != "5.5" {
t.Errorf("Could not get version redhat enterprise: %v", ret)
}
c = []string{""}
ret = getRedhatishVersion(c)
if ret != "" {
t.Errorf("Could not get version with no value: %v", ret)
}
}
func TestGetRedhatishPlatform(t *testing.T) {
var ret string
c := []string{"red hat"}
ret = getRedhatishPlatform(c)
if ret != "redhat" {
t.Errorf("Could not get platform redhat: %v", ret)
}
c = []string{"Fedora release 15 (Lovelock)"}
ret = getRedhatishPlatform(c)
if ret != "fedora" {
t.Errorf("Could not get platform fedora: %v", ret)
}
c = []string{"Enterprise Linux Server release 5.5 (Carthage)"}
ret = getRedhatishPlatform(c)
if ret != "enterprise" {
t.Errorf("Could not get platform redhat enterprise: %v", ret)
}
c = []string{""}
ret = getRedhatishPlatform(c)
if ret != "" {
t.Errorf("Could not get platform with no value: %v", ret)
}
}

View File

@ -0,0 +1,67 @@
package host
import (
"fmt"
"testing"
)
func TestHostInfo(t *testing.T) {
v, err := HostInfo()
if err != nil {
t.Errorf("error %v", err)
}
empty := &HostInfoStat{}
if v == empty {
t.Errorf("Could not get hostinfo %v", v)
}
}
func TestBoot_time(t *testing.T) {
v, err := BootTime()
if err != nil {
t.Errorf("error %v", err)
}
if v == 0 {
t.Errorf("Could not boot time %v", v)
}
}
func TestUsers(t *testing.T) {
v, err := Users()
if err != nil {
t.Errorf("error %v", err)
}
empty := UserStat{}
for _, u := range v {
if u == empty {
t.Errorf("Could not Users %v", v)
}
}
}
func TestHostInfoStat_String(t *testing.T) {
v := HostInfoStat{
Hostname: "test",
Uptime: 3000,
Procs: 100,
OS: "linux",
Platform: "ubuntu",
}
e := `{"hostname":"test","uptime":3000,"procs":100,"os":"linux","platform":"ubuntu","platform_family":"","platform_version":"","virtualization_system":"","virtualization_role":""}`
if e != fmt.Sprintf("%v", v) {
t.Errorf("HostInfoStat string is invalid: %v", v)
}
}
func TestUserStat_String(t *testing.T) {
v := UserStat{
User: "user",
Terminal: "term",
Host: "host",
Started: 100,
}
e := `{"user":"user","terminal":"term","host":"host","started":100}`
if e != fmt.Sprintf("%v", v) {
t.Errorf("UserStat string is invalid: %v", v)
}
}

View File

@ -0,0 +1,64 @@
// +build windows
package host
import (
"fmt"
"os"
"strings"
"time"
common "github.com/shirou/gopsutil/common"
process "github.com/shirou/gopsutil/process"
)
var (
procGetSystemTimeAsFileTime = common.Modkernel32.NewProc("GetSystemTimeAsFileTime")
)
func HostInfo() (*HostInfoStat, error) {
ret := &HostInfoStat{}
hostname, err := os.Hostname()
if err != nil {
return ret, err
}
ret.Hostname = hostname
uptime, err := BootTime()
if err == nil {
ret.Uptime = uptime
}
procs, err := process.Pids()
if err != nil {
return ret, err
}
ret.Procs = uint64(len(procs))
return ret, nil
}
func BootTime() (uint64, error) {
lines, err := common.GetWmic("os", "get", "LastBootUpTime")
if err != nil {
return 0, err
}
if len(lines) == 0 || len(lines[0]) != 2 {
return 0, fmt.Errorf("could not get LastBootUpTime")
}
format := "20060102150405"
t, err := time.Parse(format, strings.Split(lines[0][1], ".")[0])
if err != nil {
return 0, err
}
now := time.Now()
return uint64(now.Sub(t).Seconds()), nil
}
func Users() ([]UserStat, error) {
var ret []UserStat
return ret, nil
}

View File

@ -0,0 +1,17 @@
// +build ignore
// plus hand editing about timeval
/*
Input to cgo -godefs.
*/
package host
/*
#include <sys/time.h>
#include <utmpx.h>
*/
import "C"
type Utmpx C.struct_utmpx
type Timeval C.struct_timeval

View File

@ -0,0 +1,40 @@
// +build ignore
/*
Input to cgo -godefs.
*/
package host
/*
#define KERNEL
#include <sys/types.h>
#include <utmp.h>
enum {
sizeofPtr = sizeof(void*),
};
*/
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
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
type Utmp C.struct_utmp

View File

@ -0,0 +1,45 @@
// +build ignore
/*
Input to cgo -godefs.
*/
package host
/*
#define KERNEL
#include <sys/types.h>
#include <utmp.h>
enum {
sizeofPtr = sizeof(void*),
};
*/
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
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
type utmp C.struct_utmp
type exit_status C.struct_exit_status
type UtTv struct {
TvSec int32
TvUsec int32
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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] * p,
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
}

View File

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

View File

@ -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 * 1000
case "MemFree":
ret.Free = t * 1000
case "Buffers":
ret.Buffers = t * 1000
case "Cached":
ret.Cached = t * 1000
case "Active":
ret.Active = t * 1000
case "Inactive":
ret.Inactive = t * 1000
}
}
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
}

View File

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

View File

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

View File

@ -0,0 +1,37 @@
DIRS="cpu disk docker host load mem net process"
GOOS=`uname | tr '[:upper:]' '[:lower:]'`
ARCH=`uname -m`
case $ARCH in
amd64)
GOARCH="amd64"
;;
x86_64)
GOARCH="amd64"
;;
i386)
GOARCH="386"
;;
i686)
GOARCH="386"
;;
arm)
GOARCH="arm"
;;
*)
echo "unknown arch: $ARCH"
exit 1
esac
for DIR in $DIRS
do
if [ -e ${DIR}/types_${GOOS}.go ]; then
echo "// +build $GOOS" > ${DIR}/${DIR}_${GOOS}_${GOARCH}.go
echo "// +build $GOARCH" >> ${DIR}/${DIR}_${GOOS}_${GOARCH}.go
go tool cgo -godefs ${DIR}/types_${GOOS}.go >> ${DIR}/${DIR}_${GOOS}_${GOARCH}.go
fi
done

View File

@ -0,0 +1,137 @@
package net
import (
"encoding/json"
"net"
)
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"`
}
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
}

View File

@ -0,0 +1,74 @@
// +build darwin
package net
import (
"os/exec"
"strconv"
"strings"
"github.com/shirou/gopsutil/common"
)
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.StringContains(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, 3)
vv := []string{
values[base+3], // PacketsRecv
values[base+4], // Errin
values[base+5], // Dropin
}
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],
}
ret = append(ret, n)
}
if pernic == false {
return getNetIOCountersAll(ret)
}
return ret, nil
}

View File

@ -0,0 +1,83 @@
// +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.StringContains(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
}

View File

@ -0,0 +1,91 @@
// +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
}

View File

@ -0,0 +1,122 @@
package net
import (
"fmt"
"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)
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,142 @@
package process
import (
"encoding/json"
"runtime"
"time"
cpu "github.com/shirou/gopsutil/cpu"
)
type Process struct {
Pid int32 `json:"pid"`
name string
status string
numCtxSwitches *NumCtxSwitchesStat
uids []int32
gids []int32
numThreads int32
memInfo *MemoryInfoStat
lastCPUTimes *cpu.CPUTimesStat
lastCPUTime time.Time
}
type OpenFilesStat struct {
Path string `json:"path"`
Fd uint64 `json:"fd"`
}
type MemoryInfoStat struct {
RSS uint64 `json:"rss"` // bytes
VMS uint64 `json:"vms"` // bytes
Swap uint64 `json:"swap"` // bytes
}
type RlimitStat struct {
Resource int32 `json:"resource"`
Soft int32 `json:"soft"`
Hard int32 `json:"hard"`
}
type IOCountersStat struct {
ReadCount uint64 `json:"read_count"`
WriteCount uint64 `json:"write_count"`
ReadBytes uint64 `json:"read_bytes"`
WriteBytes uint64 `json:"write_bytes"`
}
type NumCtxSwitchesStat struct {
Voluntary int64 `json:"voluntary"`
Involuntary int64 `json:"involuntary"`
}
func (p Process) String() string {
s, _ := json.Marshal(p)
return string(s)
}
func (o OpenFilesStat) String() string {
s, _ := json.Marshal(o)
return string(s)
}
func (m MemoryInfoStat) String() string {
s, _ := json.Marshal(m)
return string(s)
}
func (r RlimitStat) String() string {
s, _ := json.Marshal(r)
return string(s)
}
func (i IOCountersStat) String() string {
s, _ := json.Marshal(i)
return string(s)
}
func (p NumCtxSwitchesStat) String() string {
s, _ := json.Marshal(p)
return string(s)
}
func PidExists(pid int32) (bool, error) {
pids, err := Pids()
if err != nil {
return false, err
}
for _, i := range pids {
if i == pid {
return true, err
}
}
return false, err
}
// If interval is 0, return difference from last call(non-blocking).
// If interval > 0, wait interval sec and return diffrence between start and end.
func (p *Process) CPUPercent(interval time.Duration) (float64, error) {
numcpu := runtime.NumCPU()
calculate := func(t1, t2 *cpu.CPUTimesStat, delta float64) float64 {
if delta == 0 {
return 0
}
delta_proc := (t2.User - t1.User) + (t2.System - t1.System)
overall_percent := ((delta_proc / delta) * 100) * float64(numcpu)
return overall_percent
}
cpuTimes, err := p.CPUTimes()
if err != nil {
return 0, err
}
if interval > 0 {
p.lastCPUTimes = cpuTimes
p.lastCPUTime = time.Now()
time.Sleep(interval)
cpuTimes, err = p.CPUTimes()
if err != nil {
return 0, err
}
} else {
if p.lastCPUTimes == nil {
// invoked first time
p.lastCPUTimes, err = p.CPUTimes()
if err != nil {
return 0, err
}
p.lastCPUTime = time.Now()
return 0, nil
}
}
delta := (time.Now().Sub(p.lastCPUTime).Seconds()) * float64(numcpu)
ret := calculate(p.lastCPUTimes, cpuTimes, float64(delta))
p.lastCPUTimes = cpuTimes
p.lastCPUTime = time.Now()
return ret, nil
}

View File

@ -0,0 +1,359 @@
// +build darwin
package process
import (
"bytes"
"os/exec"
"strconv"
"strings"
"syscall"
"unsafe"
common "github.com/shirou/gopsutil/common"
cpu "github.com/shirou/gopsutil/cpu"
net "github.com/shirou/gopsutil/net"
)
// copied from sys/sysctl.h
const (
CTLKern = 1 // "high kernel": proc, limits
KernProc = 14 // struct: process entries
KernProcPID = 1 // by process id
KernProcProc = 8 // only return procs
KernProcAll = 0 // everything
KernProcPathname = 12 // path to executable
)
type _Ctype_struct___0 struct {
Pad uint64
}
// MemoryInfoExStat is different between OSes
type MemoryInfoExStat struct {
}
type MemoryMapsStat struct {
}
func Pids() ([]int32, error) {
var ret []int32
pids, err := callPs("pid", 0)
if err != nil {
return ret, err
}
for _, pid := range pids {
v, err := strconv.Atoi(pid[0])
if err != nil {
return ret, err
}
ret = append(ret, int32(v))
}
return ret, nil
}
func (p *Process) Ppid() (int32, error) {
r, err := callPs("ppid", p.Pid)
v, err := strconv.Atoi(r[0][0])
if err != nil {
return 0, err
}
return int32(v), err
}
func (p *Process) Name() (string, error) {
k, err := p.getKProc()
if err != nil {
return "", err
}
return common.IntToString(k.Proc.P_comm[:]), nil
}
func (p *Process) Exe() (string, error) {
return "", common.NotImplementedError
}
func (p *Process) Cmdline() (string, error) {
r, err := callPs("command", p.Pid)
if err != nil {
return "", err
}
return strings.Join(r[0], " "), err
}
func (p *Process) CreateTime() (int64, error) {
return 0, common.NotImplementedError
}
func (p *Process) Cwd() (string, error) {
return "", common.NotImplementedError
}
func (p *Process) Parent() (*Process, error) {
return p, common.NotImplementedError
}
func (p *Process) Status() (string, error) {
r, err := callPs("state", p.Pid)
if err != nil {
return "", err
}
return r[0][0], err
}
func (p *Process) Uids() ([]int32, error) {
k, err := p.getKProc()
if err != nil {
return nil, err
}
uids := make([]int32, 0, 3)
uids = append(uids, int32(k.Eproc.Pcred.P_ruid), int32(k.Eproc.Ucred.Uid), int32(k.Eproc.Pcred.P_svuid))
return uids, nil
}
func (p *Process) Gids() ([]int32, error) {
k, err := p.getKProc()
if err != nil {
return nil, err
}
gids := make([]int32, 0, 3)
gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Ucred.Ngroups), int32(k.Eproc.Pcred.P_svgid))
return gids, nil
}
func (p *Process) Terminal() (string, error) {
return "", common.NotImplementedError
/*
k, err := p.getKProc()
if err != nil {
return "", err
}
ttyNr := uint64(k.Eproc.Tdev)
termmap, err := getTerminalMap()
if err != nil {
return "", err
}
return termmap[ttyNr], nil
*/
}
func (p *Process) Nice() (int32, error) {
k, err := p.getKProc()
if err != nil {
return 0, err
}
return int32(k.Proc.P_nice), nil
}
func (p *Process) IOnice() (int32, error) {
return 0, common.NotImplementedError
}
func (p *Process) Rlimit() ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.NotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) NumFDs() (int32, error) {
return 0, common.NotImplementedError
}
func (p *Process) NumThreads() (int32, error) {
return 0, common.NotImplementedError
/*
k, err := p.getKProc()
if err != nil {
return 0, err
}
return k.KiNumthreads, nil
*/
}
func (p *Process) Threads() (map[string]string, error) {
ret := make(map[string]string, 0)
return ret, common.NotImplementedError
}
func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) CPUAffinity() ([]int32, error) {
return nil, common.NotImplementedError
}
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
r, err := callPs("rss,vsize,pagein", p.Pid)
if err != nil {
return nil, err
}
rss, err := strconv.Atoi(r[0][0])
if err != nil {
return nil, err
}
vms, err := strconv.Atoi(r[0][1])
if err != nil {
return nil, err
}
pagein, err := strconv.Atoi(r[0][2])
if err != nil {
return nil, err
}
ret := &MemoryInfoStat{
RSS: uint64(rss),
VMS: uint64(vms),
Swap: uint64(pagein),
}
return ret, nil
}
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) MemoryPercent() (float32, error) {
return 0, common.NotImplementedError
}
func (p *Process) Children() ([]*Process, error) {
return nil, common.NotImplementedError
}
func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) Connections() ([]net.NetConnectionStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) IsRunning() (bool, error) {
return true, common.NotImplementedError
}
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
var ret []MemoryMapsStat
return &ret, common.NotImplementedError
}
func copyParams(k *KinfoProc, p *Process) error {
return nil
}
func processes() ([]Process, error) {
results := make([]Process, 0, 50)
mib := []int32{CTLKern, KernProc, KernProcAll, 0}
buf, length, err := common.CallSyscall(mib)
if err != nil {
return results, err
}
// get kinfo_proc size
k := KinfoProc{}
procinfoLen := int(unsafe.Sizeof(k))
count := int(length / uint64(procinfoLen))
/*
fmt.Println(length, procinfoLen, count)
b := buf[0*procinfoLen : 0*procinfoLen+procinfoLen]
fmt.Println(b)
kk, err := parseKinfoProc(b)
fmt.Printf("%#v", kk)
*/
// parse buf to procs
for i := 0; i < count; i++ {
b := buf[i*procinfoLen : i*procinfoLen+procinfoLen]
k, err := parseKinfoProc(b)
if err != nil {
continue
}
p, err := NewProcess(int32(k.Proc.P_pid))
if err != nil {
continue
}
copyParams(&k, p)
results = append(results, *p)
}
return results, nil
}
func parseKinfoProc(buf []byte) (KinfoProc, error) {
var k KinfoProc
br := bytes.NewReader(buf)
err := Read(br, LittleEndian, &k)
if err != nil {
return k, err
}
return k, nil
}
func (p *Process) getKProc() (*KinfoProc, error) {
mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid}
procK := KinfoProc{}
length := uint64(unsafe.Sizeof(procK))
buf := make([]byte, length)
_, _, syserr := syscall.Syscall6(
syscall.SYS___SYSCTL,
uintptr(unsafe.Pointer(&mib[0])),
uintptr(len(mib)),
uintptr(unsafe.Pointer(&buf[0])),
uintptr(unsafe.Pointer(&length)),
0,
0)
if syserr != 0 {
return nil, syserr
}
k, err := parseKinfoProc(buf)
if err != nil {
return nil, err
}
return &k, nil
}
func NewProcess(pid int32) (*Process, error) {
p := &Process{Pid: pid}
return p, nil
}
// call ps command.
// Return value deletes Header line(you must not input wrong arg).
// And splited by Space. Caller have responsibility to manage.
// If passed arg pid is 0, get information from all process.
func callPs(arg string, pid int32) ([][]string, error) {
var cmd []string
if pid == 0 { // will get from all processes.
cmd = []string{"-x", "-o", arg}
} else {
cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
}
out, err := exec.Command("/bin/ps", cmd...).Output()
if err != nil {
return [][]string{}, err
}
lines := strings.Split(string(out), "\n")
var ret [][]string
for _, l := range lines[1:] {
var lr []string
for _, r := range strings.Split(l, " ") {
if r == "" {
continue
}
lr = append(lr, strings.TrimSpace(r))
}
if len(lr) != 0 {
ret = append(ret, lr)
}
}
return ret, nil
}

View File

@ -0,0 +1,234 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_darwin.go
package process
const (
sizeofPtr = 0x8
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x8
sizeofLongLong = 0x8
)
type (
_C_short int16
_C_int int32
_C_long int64
_C_long_long int64
)
type Timespec struct {
Sec int64
Nsec int64
}
type Timeval struct {
Sec int64
Usec int32
Pad_cgo_0 [4]byte
}
type Rusage struct {
Utime Timeval
Stime Timeval
Maxrss int64
Ixrss int64
Idrss int64
Isrss int64
Minflt int64
Majflt int64
Nswap int64
Inblock int64
Oublock int64
Msgsnd int64
Msgrcv int64
Nsignals int64
Nvcsw int64
Nivcsw int64
}
type Rlimit struct {
Cur uint64
Max uint64
}
type UGid_t uint32
type KinfoProc struct {
Proc ExternProc
Eproc Eproc
}
type Eproc struct {
Paddr *uint64
Sess *Session
Pcred Upcred
Ucred Uucred
Pad_cgo_0 [4]byte
Vm Vmspace
Ppid int32
Pgid int32
Jobc int16
Pad_cgo_1 [2]byte
Tdev int32
Tpgid int32
Pad_cgo_2 [4]byte
Tsess *Session
Wmesg [8]int8
Xsize int32
Xrssize int16
Xccount int16
Xswrss int16
Pad_cgo_3 [2]byte
Flag int32
Login [12]int8
Spare [4]int32
Pad_cgo_4 [4]byte
}
type Proc struct{}
type Session struct{}
type ucred struct {
Link _Ctype_struct___0
Ref uint64
Posix Posix_cred
Label *Label
Audit Au_session
}
type Uucred struct {
Ref int32
Uid uint32
Ngroups int16
Pad_cgo_0 [2]byte
Groups [16]uint32
}
type Upcred struct {
Pc_lock [72]int8
Pc_ucred *ucred
P_ruid uint32
P_svuid uint32
P_rgid uint32
P_svgid uint32
P_refcnt int32
Pad_cgo_0 [4]byte
}
type Vmspace struct {
Dummy int32
Pad_cgo_0 [4]byte
Dummy2 *int8
Dummy3 [5]int32
Pad_cgo_1 [4]byte
Dummy4 [3]*int8
}
type Sigacts struct{}
type ExternProc struct {
P_un [16]byte
P_vmspace uint64
P_sigacts uint64
Pad_cgo_0 [3]byte
P_flag int32
P_stat int8
P_pid int32
P_oppid int32
P_dupfd int32
Pad_cgo_1 [4]byte
User_stack uint64
Exit_thread uint64
P_debugger int32
Sigwait int32
P_estcpu uint32
P_cpticks int32
P_pctcpu uint32
Pad_cgo_2 [4]byte
P_wchan uint64
P_wmesg uint64
P_swtime uint32
P_slptime uint32
P_realtimer Itimerval
P_rtime Timeval
P_uticks uint64
P_sticks uint64
P_iticks uint64
P_traceflag int32
Pad_cgo_3 [4]byte
P_tracep uint64
P_siglist int32
Pad_cgo_4 [4]byte
P_textvp uint64
P_holdcnt int32
P_sigmask uint32
P_sigignore uint32
P_sigcatch uint32
P_priority uint8
P_usrpri uint8
P_nice int8
P_comm [17]int8
Pad_cgo_5 [4]byte
P_pgrp uint64
P_addr uint64
P_xstat uint16
P_acflag uint16
Pad_cgo_6 [4]byte
P_ru uint64
}
type Itimerval struct {
Interval Timeval
Value Timeval
}
type Vnode struct{}
type Pgrp struct{}
type UserStruct struct{}
type Au_session struct {
Aia_p *AuditinfoAddr
Mask AuMask
}
type Posix_cred struct {
Uid uint32
Ruid uint32
Svuid uint32
Ngroups int16
Pad_cgo_0 [2]byte
Groups [16]uint32
Rgid uint32
Svgid uint32
Gmuid uint32
Flags int32
}
type Label struct{}
type AuditinfoAddr struct {
Auid uint32
Mask AuMask
Termid AuTidAddr
Asid int32
Flags uint64
}
type AuMask struct {
Success uint32
Failure uint32
}
type AuTidAddr struct {
Port int32
Type uint32
Addr [4]uint32
}
type UcredQueue struct {
Next *ucred
Prev **ucred
}

View File

@ -0,0 +1,263 @@
// +build freebsd
package process
import (
"bytes"
"encoding/binary"
"unsafe"
common "github.com/shirou/gopsutil/common"
cpu "github.com/shirou/gopsutil/cpu"
net "github.com/shirou/gopsutil/net"
)
// MemoryInfoExStat is different between OSes
type MemoryInfoExStat struct {
}
type MemoryMapsStat struct {
}
func Pids() ([]int32, error) {
var ret []int32
procs, err := processes()
if err != nil {
return ret, nil
}
for _, p := range procs {
ret = append(ret, p.Pid)
}
return ret, nil
}
func (p *Process) Ppid() (int32, error) {
k, err := p.getKProc()
if err != nil {
return 0, err
}
return k.KiPpid, nil
}
func (p *Process) Name() (string, error) {
k, err := p.getKProc()
if err != nil {
return "", err
}
return string(k.KiComm[:]), nil
}
func (p *Process) Exe() (string, error) {
return "", common.NotImplementedError
}
func (p *Process) Cmdline() (string, error) {
return "", common.NotImplementedError
}
func (p *Process) CreateTime() (int64, error) {
return 0, common.NotImplementedError
}
func (p *Process) Cwd() (string, error) {
return "", common.NotImplementedError
}
func (p *Process) Parent() (*Process, error) {
return p, common.NotImplementedError
}
func (p *Process) Status() (string, error) {
k, err := p.getKProc()
if err != nil {
return "", err
}
return string(k.KiStat[:]), nil
}
func (p *Process) Uids() ([]int32, error) {
k, err := p.getKProc()
if err != nil {
return nil, err
}
uids := make([]int32, 0, 3)
uids = append(uids, int32(k.KiRuid), int32(k.KiUID), int32(k.KiSvuid))
return uids, nil
}
func (p *Process) Gids() ([]int32, error) {
k, err := p.getKProc()
if err != nil {
return nil, err
}
gids := make([]int32, 0, 3)
gids = append(gids, int32(k.KiRgid), int32(k.KiNgroups[0]), int32(k.KiSvuid))
return gids, nil
}
func (p *Process) Terminal() (string, error) {
k, err := p.getKProc()
if err != nil {
return "", err
}
ttyNr := uint64(k.KiTdev)
termmap, err := getTerminalMap()
if err != nil {
return "", err
}
return termmap[ttyNr], nil
}
func (p *Process) Nice() (int32, error) {
return 0, common.NotImplementedError
}
func (p *Process) IOnice() (int32, error) {
return 0, common.NotImplementedError
}
func (p *Process) Rlimit() ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.NotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) NumFDs() (int32, error) {
return 0, common.NotImplementedError
}
func (p *Process) NumThreads() (int32, error) {
k, err := p.getKProc()
if err != nil {
return 0, err
}
return k.KiNumthreads, nil
}
func (p *Process) Threads() (map[string]string, error) {
ret := make(map[string]string, 0)
return ret, common.NotImplementedError
}
func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) CPUAffinity() ([]int32, error) {
return nil, common.NotImplementedError
}
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
k, err := p.getKProc()
if err != nil {
return nil, err
}
ret := &MemoryInfoStat{
RSS: uint64(k.KiRssize),
VMS: uint64(k.KiSize),
}
return ret, nil
}
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) MemoryPercent() (float32, error) {
return 0, common.NotImplementedError
}
func (p *Process) Children() ([]*Process, error) {
return nil, common.NotImplementedError
}
func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) Connections() ([]net.NetConnectionStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) IsRunning() (bool, error) {
return true, common.NotImplementedError
}
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
var ret []MemoryMapsStat
return &ret, common.NotImplementedError
}
func copyParams(k *KinfoProc, p *Process) error {
return nil
}
func processes() ([]Process, error) {
results := make([]Process, 0, 50)
mib := []int32{CTLKern, KernProc, KernProcProc, 0}
buf, length, err := common.CallSyscall(mib)
if err != nil {
return results, err
}
// get kinfo_proc size
k := KinfoProc{}
procinfoLen := int(unsafe.Sizeof(k))
count := int(length / uint64(procinfoLen))
// parse buf to procs
for i := 0; i < count; i++ {
b := buf[i*procinfoLen : i*procinfoLen+procinfoLen]
k, err := parseKinfoProc(b)
if err != nil {
continue
}
p, err := NewProcess(int32(k.KiPid))
if err != nil {
continue
}
copyParams(&k, p)
results = append(results, *p)
}
return results, nil
}
func parseKinfoProc(buf []byte) (KinfoProc, error) {
var k KinfoProc
br := bytes.NewReader(buf)
err := binary.Read(br, binary.LittleEndian, &k)
if err != nil {
return k, err
}
return k, nil
}
func (p *Process) getKProc() (*KinfoProc, error) {
mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid}
buf, length, err := common.CallSyscall(mib)
if err != nil {
return nil, err
}
procK := KinfoProc{}
if length != uint64(unsafe.Sizeof(procK)) {
return nil, err
}
k, err := parseKinfoProc(buf)
if err != nil {
return nil, err
}
return &k, nil
}
func NewProcess(pid int32) (*Process, error) {
p := &Process{Pid: pid}
return p, nil
}

View File

@ -0,0 +1,96 @@
// +build freebsd
// +build 386
package process
// copied from sys/sysctl.h
const (
CTLKern = 1 // "high kernel": proc, limits
KernProc = 14 // struct: process entries
KernProcPID = 1 // by process id
KernProcProc = 8 // only return procs
KernProcPathname = 12 // path to executable
)
// copied from sys/user.h
type KinfoProc struct {
KiStructsize int32
KiLayout int32
KiArgs int32
KiPaddr int32
KiAddr int32
KiTracep int32
KiTextvp int32
KiFd int32
KiVmspace int32
KiWchan int32
KiPid int32
KiPpid int32
KiPgid int32
KiTpgid int32
KiSid int32
KiTsid int32
KiJobc [2]byte
KiSpareShort1 [2]byte
KiTdev int32
KiSiglist [16]byte
KiSigmask [16]byte
KiSigignore [16]byte
KiSigcatch [16]byte
KiUID int32
KiRuid int32
KiSvuid int32
KiRgid int32
KiSvgid int32
KiNgroups [2]byte
KiSpareShort2 [2]byte
KiGroups [64]byte
KiSize int32
KiRssize int32
KiSwrss int32
KiTsize int32
KiDsize int32
KiSsize int32
KiXstat [2]byte
KiAcflag [2]byte
KiPctcpu int32
KiEstcpu int32
KiSlptime int32
KiSwtime int32
KiCow int32
KiRuntime int64
KiStart [8]byte
KiChildtime [8]byte
KiFlag int32
KiKflag int32
KiTraceflag int32
KiStat [1]byte
KiNice [1]byte
KiLock [1]byte
KiRqindex [1]byte
KiOncpu [1]byte
KiLastcpu [1]byte
KiOcomm [17]byte
KiWmesg [9]byte
KiLogin [18]byte
KiLockname [9]byte
KiComm [20]byte
KiEmul [17]byte
KiSparestrings [68]byte
KiSpareints [36]byte
KiCrFlags int32
KiJid int32
KiNumthreads int32
KiTid int32
KiPri int32
KiRusage [72]byte
KiRusageCh [72]byte
KiPcb int32
KiKstack int32
KiUdata int32
KiTdaddr int32
KiSpareptrs [24]byte
KiSpareint64s [48]byte
KiSflag int32
KiTdflags int32
}

View File

@ -0,0 +1,96 @@
// +build freebsd
// +build amd64
package process
// copied from sys/sysctl.h
const (
CTLKern = 1 // "high kernel": proc, limits
KernProc = 14 // struct: process entries
KernProcPID = 1 // by process id
KernProcProc = 8 // only return procs
KernProcPathname = 12 // path to executable
)
// copied from sys/user.h
type KinfoProc struct {
KiStructsize int32
KiLayout int32
KiArgs int64
KiPaddr int64
KiAddr int64
KiTracep int64
KiTextvp int64
KiFd int64
KiVmspace int64
KiWchan int64
KiPid int32
KiPpid int32
KiPgid int32
KiTpgid int32
KiSid int32
KiTsid int32
KiJobc [2]byte
KiSpareShort1 [2]byte
KiTdev int32
KiSiglist [16]byte
KiSigmask [16]byte
KiSigignore [16]byte
KiSigcatch [16]byte
KiUID int32
KiRuid int32
KiSvuid int32
KiRgid int32
KiSvgid int32
KiNgroups [2]byte
KiSpareShort2 [2]byte
KiGroups [64]byte
KiSize int64
KiRssize int64
KiSwrss int64
KiTsize int64
KiDsize int64
KiSsize int64
KiXstat [2]byte
KiAcflag [2]byte
KiPctcpu int32
KiEstcpu int32
KiSlptime int32
KiSwtime int32
KiCow int32
KiRuntime int64
KiStart [16]byte
KiChildtime [16]byte
KiFlag int64
KiKflag int64
KiTraceflag int32
KiStat [1]byte
KiNice [1]byte
KiLock [1]byte
KiRqindex [1]byte
KiOncpu [1]byte
KiLastcpu [1]byte
KiOcomm [17]byte
KiWmesg [9]byte
KiLogin [18]byte
KiLockname [9]byte
KiComm [20]byte
KiEmul [17]byte
KiSparestrings [68]byte
KiSpareints [36]byte
KiCrFlags int32
KiJid int32
KiNumthreads int32
KiTid int32
KiPri int32
KiRusage [144]byte
KiRusageCh [144]byte
KiPcb int64
KiKstack int64
KiUdata int64
KiTdaddr int64
KiSpareptrs [48]byte
KiSpareint64s [96]byte
KiSflag int64
KiTdflags int64
}

View File

@ -0,0 +1,597 @@
// +build linux
package process
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"
common "github.com/shirou/gopsutil/common"
cpu "github.com/shirou/gopsutil/cpu"
host "github.com/shirou/gopsutil/host"
net "github.com/shirou/gopsutil/net"
)
const (
PrioProcess = 0 // linux/resource.h
)
// MemoryInfoExStat is different between OSes
type MemoryInfoExStat struct {
RSS uint64 `json:"rss"` // bytes
VMS uint64 `json:"vms"` // bytes
Shared uint64 `json:"shared"` // bytes
Text uint64 `json:"text"` // bytes
Lib uint64 `json:"lib"` // bytes
Data uint64 `json:"data"` // bytes
Dirty uint64 `json:"dirty"` // bytes
}
func (m MemoryInfoExStat) String() string {
s, _ := json.Marshal(m)
return string(s)
}
type MemoryMapsStat struct {
Path string `json:"path"`
Rss uint64 `json:"rss"`
Size uint64 `json:"size"`
Pss uint64 `json:"pss"`
SharedClean uint64 `json:"shared_clean"`
SharedDirty uint64 `json:"shared_dirty"`
PrivateClean uint64 `json:"private_clean"`
PrivateDirty uint64 `json:"private_dirty"`
Referenced uint64 `json:"referenced"`
Anonymous uint64 `json:"anonymous"`
Swap uint64 `json:"swap"`
}
func (m MemoryMapsStat) String() string {
s, _ := json.Marshal(m)
return string(s)
}
// Create new Process instance
// This only stores Pid
func NewProcess(pid int32) (*Process, error) {
p := &Process{
Pid: int32(pid),
}
err := p.fillFromStatus()
return p, err
}
func (p *Process) Ppid() (int32, error) {
_, ppid, _, _, _, err := p.fillFromStat()
if err != nil {
return -1, err
}
return ppid, nil
}
func (p *Process) Name() (string, error) {
return p.name, nil
}
func (p *Process) Exe() (string, error) {
return p.fillFromExe()
}
func (p *Process) Cmdline() (string, error) {
return p.fillFromCmdline()
}
func (p *Process) CreateTime() (int64, error) {
_, _, _, createTime, _, err := p.fillFromStat()
if err != nil {
return 0, err
}
return createTime, nil
}
func (p *Process) Cwd() (string, error) {
return p.fillFromCwd()
}
func (p *Process) Parent() (*Process, error) {
return nil, common.NotImplementedError
}
func (p *Process) Status() (string, error) {
return p.status, nil
}
func (p *Process) Uids() ([]int32, error) {
return p.uids, nil
}
func (p *Process) Gids() ([]int32, error) {
return p.gids, nil
}
func (p *Process) Terminal() (string, error) {
terminal, _, _, _, _, err := p.fillFromStat()
if err != nil {
return "", err
}
return terminal, nil
}
func (p *Process) Nice() (int32, error) {
_, _, _, _, nice, err := p.fillFromStat()
if err != nil {
return 0, err
}
return nice, nil
}
func (p *Process) IOnice() (int32, error) {
return 0, common.NotImplementedError
}
func (p *Process) Rlimit() ([]RlimitStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
return p.fillFromIO()
}
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return p.numCtxSwitches, nil
}
func (p *Process) NumFDs() (int32, error) {
return 0, common.NotImplementedError
}
func (p *Process) NumThreads() (int32, error) {
return p.numThreads, nil
}
func (p *Process) Threads() (map[string]string, error) {
ret := make(map[string]string, 0)
return ret, nil
}
func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) {
_, _, cpuTimes, _, _, err := p.fillFromStat()
if err != nil {
return nil, err
}
return cpuTimes, nil
}
func (p *Process) CPUAffinity() ([]int32, error) {
return nil, common.NotImplementedError
}
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return p.memInfo, nil
}
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
_, memInfoEx, err := p.fillFromStatm()
if err != nil {
return nil, err
}
return memInfoEx, nil
}
func (p *Process) MemoryPercent() (float32, error) {
return 0, common.NotImplementedError
}
func (p *Process) Children() ([]*Process, error) {
return nil, common.NotImplementedError
}
func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) Connections() ([]net.NetConnectionStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) IsRunning() (bool, error) {
return true, common.NotImplementedError
}
// MemoryMaps get memory maps from /proc/(pid)/smaps
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
pid := p.Pid
var ret []MemoryMapsStat
smapsPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "smaps")
contents, err := ioutil.ReadFile(smapsPath)
if err != nil {
return nil, err
}
lines := strings.Split(string(contents), "\n")
// function of parsing a block
getBlock := func(first_line []string, block []string) (MemoryMapsStat, error) {
m := MemoryMapsStat{}
m.Path = first_line[len(first_line)-1]
for _, line := range block {
if strings.Contains(line, "VmFlags") {
continue
}
field := strings.Split(line, ":")
if len(field) < 2 {
continue
}
v := strings.Trim(field[1], " kB") // remove last "kB"
t, err := strconv.ParseUint(v, 10, 64)
if err != nil {
return m, err
}
switch field[0] {
case "Size":
m.Size = t
case "Rss":
m.Rss = t
case "Pss":
m.Pss = t
case "Shared_Clean":
m.SharedClean = t
case "Shared_Dirty":
m.SharedDirty = t
case "Private_Clean":
m.PrivateClean = t
case "Private_Dirty":
m.PrivateDirty = t
case "Referenced":
m.Referenced = t
case "Anonymous":
m.Anonymous = t
case "Swap":
m.Swap = t
}
}
return m, nil
}
blocks := make([]string, 16)
for _, line := range lines {
field := strings.Split(line, " ")
if strings.HasSuffix(field[0], ":") == false {
// new block section
if len(blocks) > 0 {
g, err := getBlock(field, blocks)
if err != nil {
return &ret, err
}
ret = append(ret, g)
}
// starts new block
blocks = make([]string, 16)
} else {
blocks = append(blocks, line)
}
}
return &ret, nil
}
/**
** Internal functions
**/
// Get num_fds from /proc/(pid)/fd
func (p *Process) fillFromfd() (int32, []*OpenFilesStat, error) {
pid := p.Pid
statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "fd")
d, err := os.Open(statPath)
if err != nil {
return 0, nil, err
}
defer d.Close()
fnames, err := d.Readdirnames(-1)
numFDs := int32(len(fnames))
openfiles := make([]*OpenFilesStat, numFDs)
for _, fd := range fnames {
fpath := filepath.Join(statPath, fd)
filepath, err := os.Readlink(fpath)
if err != nil {
continue
}
t, err := strconv.ParseUint(fd, 10, 64)
if err != nil {
return numFDs, openfiles, err
}
o := &OpenFilesStat{
Path: filepath,
Fd: t,
}
openfiles = append(openfiles, o)
}
return numFDs, openfiles, nil
}
// Get cwd from /proc/(pid)/cwd
func (p *Process) fillFromCwd() (string, error) {
pid := p.Pid
cwdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cwd")
cwd, err := os.Readlink(cwdPath)
if err != nil {
return "", err
}
return string(cwd), nil
}
// Get exe from /proc/(pid)/exe
func (p *Process) fillFromExe() (string, error) {
pid := p.Pid
exePath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "exe")
exe, err := os.Readlink(exePath)
if err != nil {
return "", err
}
return string(exe), nil
}
// Get cmdline from /proc/(pid)/cmdline
func (p *Process) fillFromCmdline() (string, error) {
pid := p.Pid
cmdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cmdline")
cmdline, err := ioutil.ReadFile(cmdPath)
if err != nil {
return "", err
}
ret := strings.FieldsFunc(string(cmdline), func(r rune) bool {
if r == '\u0000' {
return true
}
return false
})
return strings.Join(ret, " "), nil
}
// Get IO status from /proc/(pid)/io
func (p *Process) fillFromIO() (*IOCountersStat, error) {
pid := p.Pid
ioPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "io")
ioline, err := ioutil.ReadFile(ioPath)
if err != nil {
return nil, err
}
lines := strings.Split(string(ioline), "\n")
ret := &IOCountersStat{}
for _, line := range lines {
field := strings.Fields(line)
if len(field) < 2 {
continue
}
t, err := strconv.ParseUint(field[1], 10, 64)
if err != nil {
return nil, err
}
param := field[0]
if strings.HasSuffix(param, ":") {
param = param[:len(param)-1]
}
switch param {
case "syscr":
ret.ReadCount = t
case "syscw":
ret.WriteCount = t
case "read_bytes":
ret.ReadBytes = t
case "write_bytes":
ret.WriteBytes = t
}
}
return ret, nil
}
// Get memory info from /proc/(pid)/statm
func (p *Process) fillFromStatm() (*MemoryInfoStat, *MemoryInfoExStat, error) {
pid := p.Pid
memPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "statm")
contents, err := ioutil.ReadFile(memPath)
if err != nil {
return nil, nil, err
}
fields := strings.Split(string(contents), " ")
vms, err := strconv.ParseUint(fields[0], 10, 64)
if err != nil {
return nil, nil, err
}
rss, err := strconv.ParseUint(fields[1], 10, 64)
if err != nil {
return nil, nil, err
}
memInfo := &MemoryInfoStat{
RSS: rss * PageSize,
VMS: vms * PageSize,
}
shared, err := strconv.ParseUint(fields[2], 10, 64)
if err != nil {
return nil, nil, err
}
text, err := strconv.ParseUint(fields[3], 10, 64)
if err != nil {
return nil, nil, err
}
lib, err := strconv.ParseUint(fields[4], 10, 64)
if err != nil {
return nil, nil, err
}
dirty, err := strconv.ParseUint(fields[5], 10, 64)
if err != nil {
return nil, nil, err
}
memInfoEx := &MemoryInfoExStat{
RSS: rss * PageSize,
VMS: vms * PageSize,
Shared: shared * PageSize,
Text: text * PageSize,
Lib: lib * PageSize,
Dirty: dirty * PageSize,
}
return memInfo, memInfoEx, nil
}
// Get various status from /proc/(pid)/status
func (p *Process) fillFromStatus() error {
pid := p.Pid
statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "status")
contents, err := ioutil.ReadFile(statPath)
if err != nil {
return err
}
lines := strings.Split(string(contents), "\n")
p.numCtxSwitches = &NumCtxSwitchesStat{}
p.memInfo = &MemoryInfoStat{}
for _, line := range lines {
tabParts := strings.SplitN(line, "\t", 2)
if len(tabParts) < 2 {
continue
}
value := tabParts[1]
switch strings.TrimRight(tabParts[0], ":") {
case "Name":
p.name = strings.Trim(value, " \t")
case "State":
// get between "(" and ")"
s := strings.Index(value, "(") + 1
e := strings.Index(value, ")")
p.status = value[s:e]
case "Uid":
p.uids = make([]int32, 0, 4)
for _, i := range strings.Split(value, "\t") {
v, err := strconv.ParseInt(i, 10, 32)
if err != nil {
return err
}
p.uids = append(p.uids, int32(v))
}
case "Gid":
p.gids = make([]int32, 0, 4)
for _, i := range strings.Split(value, "\t") {
v, err := strconv.ParseInt(i, 10, 32)
if err != nil {
return err
}
p.gids = append(p.gids, int32(v))
}
case "Threads":
v, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
p.numThreads = int32(v)
case "voluntary_ctxt_switches":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
p.numCtxSwitches.Voluntary = v
case "nonvoluntary_ctxt_switches":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
p.numCtxSwitches.Involuntary = v
case "VmRSS":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.RSS = v * 1024
case "VmSize":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.VMS = v * 1024
case "VmSwap":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Swap = v * 1024
}
}
return nil
}
func (p *Process) fillFromStat() (string, int32, *cpu.CPUTimesStat, int64, int32, error) {
pid := p.Pid
statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "stat")
contents, err := ioutil.ReadFile(statPath)
if err != nil {
return "", 0, nil, 0, 0, err
}
fields := strings.Fields(string(contents))
termmap, err := getTerminalMap()
terminal := ""
if err == nil {
t, err := strconv.ParseUint(fields[6], 10, 64)
if err != nil {
return "", 0, nil, 0, 0, err
}
terminal = termmap[t]
}
ppid, err := strconv.ParseInt(fields[3], 10, 32)
if err != nil {
return "", 0, nil, 0, 0, err
}
utime, err := strconv.ParseFloat(fields[13], 64)
if err != nil {
return "", 0, nil, 0, 0, err
}
stime, err := strconv.ParseFloat(fields[14], 64)
if err != nil {
return "", 0, nil, 0, 0, err
}
cpuTimes := &cpu.CPUTimesStat{
CPU: "cpu",
User: float64(utime / ClockTicks),
System: float64(stime / ClockTicks),
}
bootTime, _ := host.BootTime()
t, err := strconv.ParseUint(fields[21], 10, 64)
if err != nil {
return "", 0, nil, 0, 0, err
}
ctime := ((t / uint64(ClockTicks)) + uint64(bootTime)) * 1000
createTime := int64(ctime)
// p.Nice = mustParseInt32(fields[18])
// use syscall instead of parse Stat file
snice, _ := syscall.Getpriority(PrioProcess, int(pid))
nice := int32(snice) // FIXME: is this true?
return terminal, int32(ppid), cpuTimes, createTime, nice, nil
}
func Pids() ([]int32, error) {
var ret []int32
d, err := os.Open("/proc")
if err != nil {
return nil, err
}
defer d.Close()
fnames, err := d.Readdirnames(-1)
if err != nil {
return nil, err
}
for _, fname := range fnames {
pid, err := strconv.ParseInt(fname, 10, 32)
if err != nil {
// if not numeric name, just skip
continue
}
ret = append(ret, int32(pid))
}
return ret, nil
}

View File

@ -0,0 +1,9 @@
// +build linux
// +build 386
package process
const (
ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK)
PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE)
)

View File

@ -0,0 +1,9 @@
// +build linux
// +build amd64
package process
const (
ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK)
PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE)
)

View File

@ -0,0 +1,9 @@
// +build linux
// +build arm
package process
const (
ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK)
PageSize = 4096 // C.sysconf(C._SC_PAGE_SIZE)
)

View File

@ -0,0 +1,102 @@
// +build linux freebsd darwin
package process
import (
"os"
"os/exec"
"os/user"
"strconv"
"strings"
"syscall"
)
// POSIX
func getTerminalMap() (map[uint64]string, error) {
ret := make(map[uint64]string)
var termfiles []string
d, err := os.Open("/dev")
if err != nil {
return nil, err
}
defer d.Close()
devnames, err := d.Readdirnames(-1)
for _, devname := range devnames {
if strings.HasPrefix(devname, "/dev/tty") {
termfiles = append(termfiles, "/dev/tty/"+devname)
}
}
ptsd, err := os.Open("/dev/pts")
if err != nil {
return nil, err
}
defer ptsd.Close()
ptsnames, err := ptsd.Readdirnames(-1)
for _, ptsname := range ptsnames {
termfiles = append(termfiles, "/dev/pts/"+ptsname)
}
for _, name := range termfiles {
stat := syscall.Stat_t{}
if err = syscall.Stat(name, &stat); err != nil {
return nil, err
}
rdev := uint64(stat.Rdev)
ret[rdev] = strings.Replace(name, "/dev", "", -1)
}
return ret, nil
}
func (p *Process) SendSignal(sig syscall.Signal) error {
sigAsStr := "INT"
switch sig {
case syscall.SIGSTOP:
sigAsStr = "STOP"
case syscall.SIGCONT:
sigAsStr = "CONT"
case syscall.SIGTERM:
sigAsStr = "TERM"
case syscall.SIGKILL:
sigAsStr = "KILL"
}
cmd := exec.Command("kill", "-s", sigAsStr, strconv.Itoa(int(p.Pid)))
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return err
}
return nil
}
func (p *Process) Suspend() error {
return p.SendSignal(syscall.SIGSTOP)
}
func (p *Process) Resume() error {
return p.SendSignal(syscall.SIGCONT)
}
func (p *Process) Terminate() error {
return p.SendSignal(syscall.SIGTERM)
}
func (p *Process) Kill() error {
return p.SendSignal(syscall.SIGKILL)
}
func (p *Process) Username() (string, error) {
uids, err := p.Uids()
if err != nil {
return "", err
}
if len(uids) > 0 {
u, err := user.LookupId(strconv.Itoa(int(uids[0])))
if err != nil {
return "", err
}
return u.Username, nil
}
return "", nil
}

View File

@ -0,0 +1,19 @@
// +build linux freebsd
package process
import (
"os"
"syscall"
"testing"
)
func Test_SendSignal(t *testing.T) {
checkPid := os.Getpid()
p, _ := NewProcess(int32(checkPid))
err := p.SendSignal(syscall.SIGCONT)
if err != nil {
t.Errorf("send signal %v", err)
}
}

View File

@ -0,0 +1,241 @@
package process
import (
"os"
"runtime"
"strings"
"testing"
"time"
)
func testGetProcess() Process {
checkPid := os.Getpid() // process.test
ret, _ := NewProcess(int32(checkPid))
return *ret
}
func Test_Pids(t *testing.T) {
ret, err := Pids()
if err != nil {
t.Errorf("error %v", err)
}
if len(ret) == 0 {
t.Errorf("could not get pids %v", ret)
}
}
func Test_Pid_exists(t *testing.T) {
checkPid := os.Getpid()
ret, err := PidExists(int32(checkPid))
if err != nil {
t.Errorf("error %v", err)
}
if ret == false {
t.Errorf("could not get process exists: %v", ret)
}
}
func Test_NewProcess(t *testing.T) {
checkPid := os.Getpid()
ret, err := NewProcess(int32(checkPid))
if err != nil {
t.Errorf("error %v", err)
}
empty := &Process{}
if runtime.GOOS != "windows" { // Windows pid is 0
if empty == ret {
t.Errorf("error %v", ret)
}
}
}
func Test_Process_memory_maps(t *testing.T) {
checkPid := os.Getpid()
ret, err := NewProcess(int32(checkPid))
mmaps, err := ret.MemoryMaps(false)
if err != nil {
t.Errorf("memory map get error %v", err)
}
empty := MemoryMapsStat{}
for _, m := range *mmaps {
if m == empty {
t.Errorf("memory map get error %v", m)
}
}
}
func Test_Process_MemoryInfo(t *testing.T) {
p := testGetProcess()
v, err := p.MemoryInfo()
if err != nil {
t.Errorf("geting ppid error %v", err)
}
empty := MemoryInfoStat{}
if v == nil || *v == empty {
t.Errorf("could not get memory info %v", v)
}
}
func Test_Process_CmdLine(t *testing.T) {
p := testGetProcess()
v, err := p.Cmdline()
if err != nil {
t.Errorf("geting ppid error %v", err)
}
if !strings.Contains(v, "process.test") {
t.Errorf("invalid cmd line %v", v)
}
}
func Test_Process_Ppid(t *testing.T) {
p := testGetProcess()
v, err := p.Ppid()
if err != nil {
t.Errorf("geting ppid error %v", err)
}
if v == 0 {
t.Errorf("return value is 0 %v", v)
}
}
func Test_Process_Status(t *testing.T) {
p := testGetProcess()
v, err := p.Status()
if err != nil {
t.Errorf("geting ppid error %v", err)
}
if !strings.HasPrefix(v, "S") && v != "running" && v != "sleeping" {
t.Errorf("could not get state %v", v)
}
}
func Test_Process_Terminal(t *testing.T) {
p := testGetProcess()
_, err := p.Terminal()
if err != nil {
t.Errorf("geting terminal error %v", err)
}
/*
if v == "" {
t.Errorf("could not get terminal %v", v)
}
*/
}
func Test_Process_IOCounters(t *testing.T) {
p := testGetProcess()
v, err := p.IOCounters()
if err != nil {
t.Errorf("geting iocounter error %v", err)
return
}
empty := &IOCountersStat{}
if v == empty {
t.Errorf("error %v", v)
}
}
func Test_Process_NumCtx(t *testing.T) {
p := testGetProcess()
_, err := p.NumCtxSwitches()
if err != nil {
t.Errorf("geting numctx error %v", err)
return
}
}
func Test_Process_Nice(t *testing.T) {
p := testGetProcess()
n, err := p.Nice()
if err != nil {
t.Errorf("geting nice error %v", err)
}
if n != 0 && n != 20 && n != 8 {
t.Errorf("invalid nice: %d", n)
}
}
func Test_Process_NumThread(t *testing.T) {
p := testGetProcess()
n, err := p.NumThreads()
if err != nil {
t.Errorf("geting NumThread error %v", err)
}
if n < 0 {
t.Errorf("invalid NumThread: %d", n)
}
}
func Test_Process_Name(t *testing.T) {
p := testGetProcess()
n, err := p.Name()
if err != nil {
t.Errorf("geting name error %v", err)
}
if !strings.Contains(n, "process.test") {
t.Errorf("invalid Exe %s", n)
}
}
func Test_Process_Exe(t *testing.T) {
p := testGetProcess()
n, err := p.Exe()
if err != nil {
t.Errorf("geting Exe error %v", err)
}
if !strings.Contains(n, "process.test") {
t.Errorf("invalid Exe %s", n)
}
}
func Test_Process_CpuPercent(t *testing.T) {
p := testGetProcess()
percent, err := p.CPUPercent(0)
if err != nil {
t.Errorf("error %v", err)
}
duration := time.Duration(1000) * time.Microsecond
time.Sleep(duration)
percent, err = p.CPUPercent(0)
if err != nil {
t.Errorf("error %v", err)
}
numcpu := runtime.NumCPU()
// if percent < 0.0 || percent > 100.0*float64(numcpu) { // TODO
if percent < 0.0 {
t.Fatalf("CPUPercent value is invalid: %f, %d", percent, numcpu)
}
}
func Test_Process_CpuPercentLoop(t *testing.T) {
p := testGetProcess()
numcpu := runtime.NumCPU()
for i := 0; i < 2; i++ {
duration := time.Duration(100) * time.Microsecond
percent, err := p.CPUPercent(duration)
if err != nil {
t.Errorf("error %v", err)
}
// if percent < 0.0 || percent > 100.0*float64(numcpu) { // TODO
if percent < 0.0 {
t.Fatalf("CPUPercent value is invalid: %f, %d", percent, numcpu)
}
}
}

View File

@ -0,0 +1,305 @@
// +build windows
package process
import (
"errors"
"fmt"
"strconv"
"syscall"
"unsafe"
"github.com/shirou/w32"
common "github.com/shirou/gopsutil/common"
cpu "github.com/shirou/gopsutil/cpu"
net "github.com/shirou/gopsutil/net"
)
const (
NoMoreFiles = 0x12
MaxPathLength = 260
)
type SystemProcessInformation struct {
NextEntryOffset uint64
NumberOfThreads uint64
Reserved1 [48]byte
Reserved2 [3]byte
UniqueProcessID uintptr
Reserved3 uintptr
HandleCount uint64
Reserved4 [4]byte
Reserved5 [11]byte
PeakPagefileUsage uint64
PrivatePageCount uint64
Reserved6 [6]uint64
}
// Memory_info_ex is different between OSes
type MemoryInfoExStat struct {
}
type MemoryMapsStat struct {
}
func Pids() ([]int32, error) {
var ret []int32
procs, err := processes()
if err != nil {
return ret, nil
}
for _, proc := range procs {
ret = append(ret, proc.Pid)
}
return ret, nil
}
func (p *Process) Ppid() (int32, error) {
ret, _, _, err := p.getFromSnapProcess(p.Pid)
if err != nil {
return 0, err
}
return ret, nil
}
func (p *Process) Name() (string, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid)
lines, err := common.GetWmic("process", "where", query, "get", "Name")
if err != nil {
return "", err
}
if len(lines) == 0 {
return "", fmt.Errorf("could not get Name")
}
return lines[0][1], nil
}
func (p *Process) Exe() (string, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid)
lines, err := common.GetWmic("process", "where", query, "get", "ExecutablePath")
if err != nil {
return "", err
}
if len(lines) == 0 {
return "", fmt.Errorf("could not get ExecutablePath")
}
return lines[0][1], nil
}
func (p *Process) Cmdline() (string, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid)
lines, err := common.GetWmic("process", "where", query, "get", "CommandLine")
if err != nil {
return "", err
}
if len(lines) == 0 {
return "", fmt.Errorf("could not get command line")
}
return lines[0][1], nil
}
func (p *Process) Cwd() (string, error) {
return "", common.NotImplementedError
}
func (p *Process) Parent() (*Process, error) {
return p, common.NotImplementedError
}
func (p *Process) Status() (string, error) {
return "", common.NotImplementedError
}
func (p *Process) Username() (string, error) {
return "", common.NotImplementedError
}
func (p *Process) Uids() ([]int32, error) {
var uids []int32
return uids, common.NotImplementedError
}
func (p *Process) Gids() ([]int32, error) {
var gids []int32
return gids, common.NotImplementedError
}
func (p *Process) Terminal() (string, error) {
return "", common.NotImplementedError
}
// Nice returnes priority in Windows
func (p *Process) Nice() (int32, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid)
lines, err := common.GetWmic("process", "where", query, "get", "Priority")
if err != nil {
return 0, err
}
if len(lines) == 0 {
return 0, fmt.Errorf("could not get command line")
}
priority, err := strconv.Atoi(lines[0][1])
if err != nil {
return 0, err
}
return int32(priority), nil
}
func (p *Process) IOnice() (int32, error) {
return 0, common.NotImplementedError
}
func (p *Process) Rlimit() ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.NotImplementedError
}
func (p *Process) IOCounters() (*IOCountersStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) NumFDs() (int32, error) {
return 0, common.NotImplementedError
}
func (p *Process) NumThreads() (int32, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid)
lines, err := common.GetWmic("process", "where", query, "get", "ThreadCount")
if err != nil {
return 0, err
}
if len(lines) == 0 {
return 0, fmt.Errorf("could not get command line")
}
count, err := strconv.Atoi(lines[0][1])
if err != nil {
return 0, err
}
return int32(count), nil
}
func (p *Process) Threads() (map[string]string, error) {
ret := make(map[string]string, 0)
return ret, common.NotImplementedError
}
func (p *Process) CPUTimes() (*cpu.CPUTimesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) CPUAffinity() ([]int32, error) {
return nil, common.NotImplementedError
}
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) MemoryPercent() (float32, error) {
return 0, common.NotImplementedError
}
func (p *Process) Children() ([]*Process, error) {
return nil, common.NotImplementedError
}
func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) Connections() ([]net.NetConnectionStat, error) {
return nil, common.NotImplementedError
}
func (p *Process) IsRunning() (bool, error) {
return true, common.NotImplementedError
}
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
ret := make([]MemoryMapsStat, 0)
return &ret, common.NotImplementedError
}
func NewProcess(pid int32) (*Process, error) {
p := &Process{Pid: pid}
return p, nil
}
func (p *Process) SendSignal(sig syscall.Signal) error {
return common.NotImplementedError
}
func (p *Process) Suspend() error {
return common.NotImplementedError
}
func (p *Process) Resume() error {
return common.NotImplementedError
}
func (p *Process) Terminate() error {
return common.NotImplementedError
}
func (p *Process) Kill() error {
return common.NotImplementedError
}
func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) {
snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(pid))
if snap == 0 {
return 0, 0, "", syscall.GetLastError()
}
defer w32.CloseHandle(snap)
var pe32 w32.PROCESSENTRY32
pe32.DwSize = uint32(unsafe.Sizeof(pe32))
if w32.Process32First(snap, &pe32) == false {
return 0, 0, "", syscall.GetLastError()
}
if pe32.Th32ProcessID == uint32(pid) {
szexe := syscall.UTF16ToString(pe32.SzExeFile[:])
return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil
}
for w32.Process32Next(snap, &pe32) {
if pe32.Th32ProcessID == uint32(pid) {
szexe := syscall.UTF16ToString(pe32.SzExeFile[:])
return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil
}
}
return 0, 0, "", errors.New("Couldn't find pid:" + string(pid))
}
// Get processes
func processes() ([]*Process, error) {
lines, err := common.GetWmic("process", "get", "processid")
if err != nil {
return nil, err
}
var results []*Process
for _, l := range lines {
pid, err := strconv.Atoi(l[1])
if err != nil {
continue
}
p, err := NewProcess(int32(pid))
if err != nil {
continue
}
results = append(results, p)
}
return results, nil
}
func getProcInfo(pid int32) (*SystemProcessInformation, error) {
initialBufferSize := uint64(0x4000)
bufferSize := initialBufferSize
buffer := make([]byte, bufferSize)
var sysProcInfo SystemProcessInformation
ret, _, _ := common.ProcNtQuerySystemInformation.Call(
uintptr(unsafe.Pointer(&sysProcInfo)),
uintptr(unsafe.Pointer(&buffer[0])),
uintptr(unsafe.Pointer(&bufferSize)),
uintptr(unsafe.Pointer(&bufferSize)))
if ret != 0 {
return nil, syscall.GetLastError()
}
return &sysProcInfo, nil
}

View File

@ -0,0 +1,160 @@
// 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.
// Hand Writing
// - all pointer in ExternProc to uint64
// +build ignore
/*
Input to cgo -godefs.
*/
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
// +godefs map struct_ [16]byte /* in6_addr */
package process
/*
#define __DARWIN_UNIX03 0
#define KERNEL
#define _DARWIN_USE_64_BIT_INODE
#include <dirent.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <sys/event.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <net/bpf.h>
#include <net/if_dl.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <sys/sysctl.h>
#include <sys/ucred.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/_types/_timeval.h>
#include <sys/appleapiopts.h>
#include <sys/cdefs.h>
#include <sys/param.h>
#include <bsm/audit.h>
#include <sys/queue.h>
enum {
sizeofPtr = sizeof(void*),
};
union sockaddr_all {
struct sockaddr s1; // this one gets used for fields
struct sockaddr_in s2; // these pad it out
struct sockaddr_in6 s3;
struct sockaddr_un s4;
struct sockaddr_dl s5;
};
struct sockaddr_any {
struct sockaddr addr;
char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)];
};
struct ucred_queue {
struct ucred *tqe_next;
struct ucred **tqe_prev;
TRACEBUF
};
*/
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
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
// Time
type Timespec C.struct_timespec
type Timeval C.struct_timeval
// Processes
type Rusage C.struct_rusage
type Rlimit C.struct_rlimit
type UGid_t C.gid_t
type KinfoProc C.struct_kinfo_proc
type Eproc C.struct_eproc
type Proc C.struct_proc
type Session C.struct_session
type ucred C.struct_ucred
type Uucred C.struct__ucred
type Upcred C.struct__pcred
type Vmspace C.struct_vmspace
type Sigacts C.struct_sigacts
type ExternProc C.struct_extern_proc
type Itimerval C.struct_itimerval
type Vnode C.struct_vnode
type Pgrp C.struct_pgrp
type UserStruct C.struct_user
type Au_session C.struct_au_session
type Posix_cred C.struct_posix_cred
type Label C.struct_label
type AuditinfoAddr C.struct_auditinfo_addr
type AuMask C.struct_au_mask
type AuTidAddr C.struct_au_tid_addr
// TAILQ(ucred)
type UcredQueue C.struct_ucred_queue

View File

@ -0,0 +1,36 @@
Windows memo
=====================
Size
----------
DWORD
32-bit unsigned integer
DWORDLONG
64-bit unsigned integer
DWORD_PTR
unsigned long type for pointer precision
DWORD32
32-bit unsigned integer
DWORD64
64-bit unsigned integer
HALF_PTR
_WIN64 = int, else short
INT
32-bit signed integer
INT_PTR
_WIN64 = __int64 else int
LONG
32-bit signed integer
LONGLONG
64-bit signed integer
LONG_PTR
_WIN64 = __int64 else long
SHORT
16-bit integer
SIZE_T
maximum number of bytes to which a pointer can point. typedef ULONG_PTR SIZE_T;
SSIZE_T
signed version of SIZE_T. typedef LONG_PTR SSIZE_T;
WORD
16-bit unsigned integer

View File

@ -2,7 +2,7 @@ package system
import (
"github.com/influxdb/tivan/plugins"
"github.com/shirou/gopsutil/load"
"github.com/influxdb/tivan/plugins/system/ps/load"
"github.com/vektra/cypress"
)

View File

@ -3,96 +3,92 @@ package system
import (
"testing"
"github.com/shirou/gopsutil/load"
"github.com/influxdb/tivan/plugins/system/ps/load"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/vektra/neko"
)
func TestSystemStats(t *testing.T) {
n := neko.Start(t)
func TestSystemStats_GenerateLoad(t *testing.T) {
var mps MockPS
n.CheckMock(&mps.Mock)
defer mps.AssertExpectations(t)
n.It("generates metrics from the system information", func() {
ss := &SystemStats{ps: &mps}
ss := &SystemStats{ps: &mps}
lv := &load.LoadAvgStat{
Load1: 0.3,
Load5: 1.5,
Load15: 0.8,
}
lv := &load.LoadAvgStat{
Load1: 0.3,
Load5: 1.5,
Load15: 0.8,
}
mps.On("LoadAvg").Return(lv, nil)
mps.On("LoadAvg").Return(lv, nil)
msgs, err := ss.Read()
require.NoError(t, err)
msgs, err := ss.Read()
require.NoError(t, err)
name, ok := msgs[0].GetString("name")
require.True(t, ok)
name, ok := msgs[0].GetString("name")
require.True(t, ok)
assert.Equal(t, "load1", name)
assert.Equal(t, "load1", name)
val, ok := msgs[0].GetFloat("value")
require.True(t, ok)
val, ok := msgs[0].GetFloat("value")
require.True(t, ok)
assert.Equal(t, 0.3, val)
assert.Equal(t, 0.3, val)
name, ok = msgs[1].GetString("name")
require.True(t, ok)
name, ok = msgs[1].GetString("name")
require.True(t, ok)
assert.Equal(t, "load5", name)
assert.Equal(t, "load5", name)
val, ok = msgs[1].GetFloat("value")
require.True(t, ok)
val, ok = msgs[1].GetFloat("value")
require.True(t, ok)
assert.Equal(t, 1.5, val)
assert.Equal(t, 1.5, val)
name, ok = msgs[2].GetString("name")
require.True(t, ok)
name, ok = msgs[2].GetString("name")
require.True(t, ok)
assert.Equal(t, "load15", name)
assert.Equal(t, "load15", name)
val, ok = msgs[2].GetFloat("value")
require.True(t, ok)
val, ok = msgs[2].GetFloat("value")
require.True(t, ok)
assert.Equal(t, 0.8, val)
})
n.It("adds any tags registered", func() {
ss := &SystemStats{
ps: &mps,
tags: map[string]string{
"host": "my.test",
"dc": "us-west-1",
},
}
lv := &load.LoadAvgStat{
Load1: 0.3,
Load5: 1.5,
Load15: 0.8,
}
mps.On("LoadAvg").Return(lv, nil)
msgs, err := ss.Read()
require.NoError(t, err)
for _, m := range msgs {
val, ok := m.GetTag("host")
require.True(t, ok)
assert.Equal(t, val, "my.test")
val, ok = m.GetTag("dc")
require.True(t, ok)
assert.Equal(t, val, "us-west-1")
}
})
n.Meow()
assert.Equal(t, 0.8, val)
}
func TestSystemStats_AddTags(t *testing.T) {
var mps MockPS
defer mps.AssertExpectations(t)
ss := &SystemStats{
ps: &mps,
tags: map[string]string{
"host": "my.test",
"dc": "us-west-1",
},
}
lv := &load.LoadAvgStat{
Load1: 0.3,
Load5: 1.5,
Load15: 0.8,
}
mps.On("LoadAvg").Return(lv, nil)
msgs, err := ss.Read()
require.NoError(t, err)
for _, m := range msgs {
val, ok := m.GetTag("host")
require.True(t, ok)
assert.Equal(t, val, "my.test")
val, ok = m.GetTag("dc")
require.True(t, ok)
assert.Equal(t, val, "us-west-1")
}
}