Vendor psutils and remove neko
This commit is contained in:
parent
db74acb86d
commit
9c42aea28c
|
@ -1,8 +1,9 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import "github.com/stretchr/testify/mock"
|
import (
|
||||||
|
"github.com/influxdb/tivan/plugins/system/ps/load"
|
||||||
import "github.com/shirou/gopsutil/load"
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
type MockPS struct {
|
type MockPS struct {
|
||||||
mock.Mock
|
mock.Mock
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
*~
|
||||||
|
#*
|
||||||
|
_obj
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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 ""
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
package gopsutil
|
|
@ -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)
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
)
|
|
@ -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)
|
||||||
|
)
|
|
@ -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)
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -2,7 +2,7 @@ package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/influxdb/tivan/plugins"
|
"github.com/influxdb/tivan/plugins"
|
||||||
"github.com/shirou/gopsutil/load"
|
"github.com/influxdb/tivan/plugins/system/ps/load"
|
||||||
"github.com/vektra/cypress"
|
"github.com/vektra/cypress"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,96 +3,92 @@ package system
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/shirou/gopsutil/load"
|
"github.com/influxdb/tivan/plugins/system/ps/load"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/vektra/neko"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSystemStats(t *testing.T) {
|
func TestSystemStats_GenerateLoad(t *testing.T) {
|
||||||
n := neko.Start(t)
|
|
||||||
|
|
||||||
var mps MockPS
|
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{
|
lv := &load.LoadAvgStat{
|
||||||
Load1: 0.3,
|
Load1: 0.3,
|
||||||
Load5: 1.5,
|
Load5: 1.5,
|
||||||
Load15: 0.8,
|
Load15: 0.8,
|
||||||
}
|
}
|
||||||
|
|
||||||
mps.On("LoadAvg").Return(lv, nil)
|
mps.On("LoadAvg").Return(lv, nil)
|
||||||
|
|
||||||
msgs, err := ss.Read()
|
msgs, err := ss.Read()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
name, ok := msgs[0].GetString("name")
|
name, ok := msgs[0].GetString("name")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
assert.Equal(t, "load1", name)
|
assert.Equal(t, "load1", name)
|
||||||
|
|
||||||
val, ok := msgs[0].GetFloat("value")
|
val, ok := msgs[0].GetFloat("value")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
assert.Equal(t, 0.3, val)
|
assert.Equal(t, 0.3, val)
|
||||||
|
|
||||||
name, ok = msgs[1].GetString("name")
|
name, ok = msgs[1].GetString("name")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
assert.Equal(t, "load5", name)
|
assert.Equal(t, "load5", name)
|
||||||
|
|
||||||
val, ok = msgs[1].GetFloat("value")
|
val, ok = msgs[1].GetFloat("value")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
assert.Equal(t, 1.5, val)
|
assert.Equal(t, 1.5, val)
|
||||||
|
|
||||||
name, ok = msgs[2].GetString("name")
|
name, ok = msgs[2].GetString("name")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
assert.Equal(t, "load15", name)
|
assert.Equal(t, "load15", name)
|
||||||
|
|
||||||
val, ok = msgs[2].GetFloat("value")
|
val, ok = msgs[2].GetFloat("value")
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
assert.Equal(t, 0.8, val)
|
assert.Equal(t, 0.8, val)
|
||||||
})
|
}
|
||||||
|
|
||||||
n.It("adds any tags registered", func() {
|
func TestSystemStats_AddTags(t *testing.T) {
|
||||||
ss := &SystemStats{
|
var mps MockPS
|
||||||
ps: &mps,
|
defer mps.AssertExpectations(t)
|
||||||
tags: map[string]string{
|
|
||||||
"host": "my.test",
|
ss := &SystemStats{
|
||||||
"dc": "us-west-1",
|
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,
|
lv := &load.LoadAvgStat{
|
||||||
}
|
Load1: 0.3,
|
||||||
|
Load5: 1.5,
|
||||||
mps.On("LoadAvg").Return(lv, nil)
|
Load15: 0.8,
|
||||||
|
}
|
||||||
msgs, err := ss.Read()
|
|
||||||
require.NoError(t, err)
|
mps.On("LoadAvg").Return(lv, nil)
|
||||||
|
|
||||||
for _, m := range msgs {
|
msgs, err := ss.Read()
|
||||||
val, ok := m.GetTag("host")
|
require.NoError(t, err)
|
||||||
require.True(t, ok)
|
|
||||||
|
for _, m := range msgs {
|
||||||
assert.Equal(t, val, "my.test")
|
val, ok := m.GetTag("host")
|
||||||
|
require.True(t, ok)
|
||||||
val, ok = m.GetTag("dc")
|
|
||||||
require.True(t, ok)
|
assert.Equal(t, val, "my.test")
|
||||||
|
|
||||||
assert.Equal(t, val, "us-west-1")
|
val, ok = m.GetTag("dc")
|
||||||
}
|
require.True(t, ok)
|
||||||
})
|
|
||||||
|
assert.Equal(t, val, "us-west-1")
|
||||||
n.Meow()
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue