diff --git a/README.rst b/README.rst index 0acc906..dce6047 100644 --- a/README.rst +++ b/README.rst @@ -135,7 +135,7 @@ 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 +disk_io_counters x x disk_usage x x x x net_io_counters x x b x boot_time x x x x diff --git a/common/common_freebsd.go b/common/common_freebsd.go index 7201082..3c11246 100644 --- a/common/common_freebsd.go +++ b/common/common_freebsd.go @@ -3,8 +3,10 @@ package common import ( + "syscall" "os/exec" "strings" + "unsafe" ) func DoSysctrl(mib string) ([]string, error) { @@ -18,3 +20,42 @@ func DoSysctrl(mib string) ([]string, error) { return values, nil } + +func CallSyscall(mib []int32) ([]byte, uint64, error) { + miblen := uint64(len(mib)) + + // get required buffer size + length := uint64(0) + _, _, err := syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + 0, + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + var b []byte + return b, length, err + } + if length == 0 { + var b []byte + return b, length, err + } + // get proc info itself + buf := make([]byte, length) + _, _, err = syscall.Syscall6( + syscall.SYS___SYSCTL, + uintptr(unsafe.Pointer(&mib[0])), + uintptr(miblen), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if err != 0 { + return buf, length, err + } + + return buf, length, nil +} + diff --git a/disk/binary.go b/disk/binary.go new file mode 100644 index 0000000..418e591 --- /dev/null +++ b/disk/binary.go @@ -0,0 +1,634 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package binary implements simple translation between numbers and byte +// sequences and encoding and decoding of varints. +// +// Numbers are translated by reading and writing fixed-size values. +// A fixed-size value is either a fixed-size arithmetic +// type (int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// +// The varint functions encode and decode single integer values using +// a variable-length encoding; smaller values require fewer bytes. +// For a specification, see +// http://code.google.com/apis/protocolbuffers/docs/encoding.html. +// +// This package favors simplicity over efficiency. Clients that require +// high-performance serialization, especially for large data structures, +// should look at more advanced solutions such as the encoding/gob +// package or protocol buffers. +package disk + +import ( + "errors" + "io" + "math" + "reflect" +) + +// A ByteOrder specifies how to convert byte sequences into +// 16-, 32-, or 64-bit unsigned integers. +type ByteOrder interface { + Uint16([]byte) uint16 + Uint32([]byte) uint32 + Uint64([]byte) uint64 + PutUint16([]byte, uint16) + PutUint32([]byte, uint32) + PutUint64([]byte, uint64) + String() string +} + +// LittleEndian is the little-endian implementation of ByteOrder. +var LittleEndian littleEndian + +// BigEndian is the big-endian implementation of ByteOrder. +var BigEndian bigEndian + +type littleEndian struct{} + +func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } + +func (littleEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v) + b[1] = byte(v >> 8) +} + +func (littleEndian) Uint32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (littleEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) +} + +func (littleEndian) Uint64(b []byte) uint64 { + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +func (littleEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} + +func (littleEndian) String() string { return "LittleEndian" } + +func (littleEndian) GoString() string { return "binary.LittleEndian" } + +type bigEndian struct{} + +func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 } + +func (bigEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v >> 8) + b[1] = byte(v) +} + +func (bigEndian) Uint32(b []byte) uint32 { + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +func (bigEndian) PutUint32(b []byte, v uint32) { + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) +} + +func (bigEndian) Uint64(b []byte) uint64 { + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +func (bigEndian) PutUint64(b []byte, v uint64) { + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +func (bigEndian) String() string { return "BigEndian" } + +func (bigEndian) GoString() string { return "binary.BigEndian" } + +// Read reads structured binary data from r into data. +// Data must be a pointer to a fixed-size value or a slice +// of fixed-size values. +// Bytes read from r are decoded using the specified byte order +// and written to successive fields of the data. +// When reading into structs, the field data for fields with +// blank (_) field names is skipped; i.e., blank field names +// may be used for padding. +// When reading into a struct, all non-blank fields must be exported. +func Read(r io.Reader, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + if _, err := io.ReadFull(r, bs); err != nil { + return err + } + switch data := data.(type) { + case *int8: + *data = int8(b[0]) + case *uint8: + *data = b[0] + case *int16: + *data = int16(order.Uint16(bs)) + case *uint16: + *data = order.Uint16(bs) + case *int32: + *data = int32(order.Uint32(bs)) + case *uint32: + *data = order.Uint32(bs) + case *int64: + *data = int64(order.Uint64(bs)) + case *uint64: + *data = order.Uint64(bs) + case []int8: + for i, x := range bs { // Easier to loop over the input for 8-bit values. + data[i] = int8(x) + } + case []uint8: + copy(data, bs) + case []int16: + for i := range data { + data[i] = int16(order.Uint16(bs[2*i:])) + } + case []uint16: + for i := range data { + data[i] = order.Uint16(bs[2*i:]) + } + case []int32: + for i := range data { + data[i] = int32(order.Uint32(bs[4*i:])) + } + case []uint32: + for i := range data { + data[i] = order.Uint32(bs[4*i:]) + } + case []int64: + for i := range data { + data[i] = int64(order.Uint64(bs[8*i:])) + } + case []uint64: + for i := range data { + data[i] = order.Uint64(bs[8*i:]) + } + } + return nil + } + + // Fallback to reflect-based decoding. + v := reflect.ValueOf(data) + size := -1 + switch v.Kind() { + case reflect.Ptr: + v = v.Elem() + size = dataSize(v) + case reflect.Slice: + size = dataSize(v) + } + if size < 0 { + return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String()) + } + d := &decoder{order: order, buf: make([]byte, size)} + if _, err := io.ReadFull(r, d.buf); err != nil { + return err + } + d.value(v) + return nil +} + +// Write writes the binary representation of data into w. +// Data must be a fixed-size value or a slice of fixed-size +// values, or a pointer to such data. +// Bytes written to w are encoded using the specified byte order +// and read from successive fields of the data. +// When writing structs, zero values are written for fields +// with blank (_) field names. +func Write(w io.Writer, order ByteOrder, data interface{}) error { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + switch v := data.(type) { + case *int8: + bs = b[:1] + b[0] = byte(*v) + case int8: + bs = b[:1] + b[0] = byte(v) + case []int8: + for i, x := range v { + bs[i] = byte(x) + } + case *uint8: + bs = b[:1] + b[0] = *v + case uint8: + bs = b[:1] + b[0] = byte(v) + case []uint8: + bs = v + case *int16: + bs = b[:2] + order.PutUint16(bs, uint16(*v)) + case int16: + bs = b[:2] + order.PutUint16(bs, uint16(v)) + case []int16: + for i, x := range v { + order.PutUint16(bs[2*i:], uint16(x)) + } + case *uint16: + bs = b[:2] + order.PutUint16(bs, *v) + case uint16: + bs = b[:2] + order.PutUint16(bs, v) + case []uint16: + for i, x := range v { + order.PutUint16(bs[2*i:], x) + } + case *int32: + bs = b[:4] + order.PutUint32(bs, uint32(*v)) + case int32: + bs = b[:4] + order.PutUint32(bs, uint32(v)) + case []int32: + for i, x := range v { + order.PutUint32(bs[4*i:], uint32(x)) + } + case *uint32: + bs = b[:4] + order.PutUint32(bs, *v) + case uint32: + bs = b[:4] + order.PutUint32(bs, v) + case []uint32: + for i, x := range v { + order.PutUint32(bs[4*i:], x) + } + case *int64: + bs = b[:8] + order.PutUint64(bs, uint64(*v)) + case int64: + bs = b[:8] + order.PutUint64(bs, uint64(v)) + case []int64: + for i, x := range v { + order.PutUint64(bs[8*i:], uint64(x)) + } + case *uint64: + bs = b[:8] + order.PutUint64(bs, *v) + case uint64: + bs = b[:8] + order.PutUint64(bs, v) + case []uint64: + for i, x := range v { + order.PutUint64(bs[8*i:], x) + } + } + _, err := w.Write(bs) + return err + } + + // Fallback to reflect-based encoding. + v := reflect.Indirect(reflect.ValueOf(data)) + size := dataSize(v) + if size < 0 { + return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String()) + } + buf := make([]byte, size) + e := &encoder{order: order, buf: buf} + e.value(v) + _, err := w.Write(buf) + return err +} + +// Size returns how many bytes Write would generate to encode the value v, which +// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data. +// If v is neither of these, Size returns -1. +func Size(v interface{}) int { + return dataSize(reflect.Indirect(reflect.ValueOf(v))) +} + +// dataSize returns the number of bytes the actual data represented by v occupies in memory. +// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice +// it returns the length of the slice times the element size and does not count the memory +// occupied by the header. If the type of v is not acceptable, dataSize returns -1. +func dataSize(v reflect.Value) int { + if v.Kind() == reflect.Slice { + if s := sizeof(v.Type().Elem()); s >= 0 { + return s * v.Len() + } + return -1 + } + return sizeof(v.Type()) +} + +// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable. +func sizeof(t reflect.Type) int { + switch t.Kind() { + case reflect.Array: + if s := sizeof(t.Elem()); s >= 0 { + return s * t.Len() + } + + case reflect.Struct: + sum := 0 + for i, n := 0, t.NumField(); i < n; i++ { + s := sizeof(t.Field(i).Type) + if s < 0 { + return -1 + } + sum += s + } + return sum + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.Ptr: + return int(t.Size()) + } + + return -1 +} + +type coder struct { + order ByteOrder + buf []byte +} + +type decoder coder +type encoder coder + +func (d *decoder) uint8() uint8 { + x := d.buf[0] + d.buf = d.buf[1:] + return x +} + +func (e *encoder) uint8(x uint8) { + e.buf[0] = x + e.buf = e.buf[1:] +} + +func (d *decoder) uint16() uint16 { + x := d.order.Uint16(d.buf[0:2]) + d.buf = d.buf[2:] + return x +} + +func (e *encoder) uint16(x uint16) { + e.order.PutUint16(e.buf[0:2], x) + e.buf = e.buf[2:] +} + +func (d *decoder) uint32() uint32 { + x := d.order.Uint32(d.buf[0:4]) + d.buf = d.buf[4:] + return x +} + +func (e *encoder) uint32(x uint32) { + e.order.PutUint32(e.buf[0:4], x) + e.buf = e.buf[4:] +} + +func (d *decoder) uint64() uint64 { + x := d.order.Uint64(d.buf[0:8]) + d.buf = d.buf[8:] + return x +} + +func (e *encoder) uint64(x uint64) { + e.order.PutUint64(e.buf[0:8], x) + e.buf = e.buf[8:] +} + +func (d *decoder) int8() int8 { return int8(d.uint8()) } + +func (e *encoder) int8(x int8) { e.uint8(uint8(x)) } + +func (d *decoder) int16() int16 { return int16(d.uint16()) } + +func (e *encoder) int16(x int16) { e.uint16(uint16(x)) } + +func (d *decoder) int32() int32 { return int32(d.uint32()) } + +func (e *encoder) int32(x int32) { e.uint32(uint32(x)) } + +func (d *decoder) int64() int64 { return int64(d.uint64()) } + +func (e *encoder) int64(x int64) { e.uint64(uint64(x)) } + +func (d *decoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // Note: Calling v.CanSet() below is an optimization. + // It would be sufficient to check the field name, + // but creating the StructField info for each field is + // costly (run "go test -bench=ReadStruct" and compare + // results when making changes to this code). + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + d.value(v) + } else { + d.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + d.value(v.Index(i)) + } + + case reflect.Int8: + v.SetInt(int64(d.int8())) + case reflect.Int16: + v.SetInt(int64(d.int16())) + case reflect.Int32: + v.SetInt(int64(d.int32())) + case reflect.Int64: + v.SetInt(d.int64()) + + case reflect.Uint8: + v.SetUint(uint64(d.uint8())) + case reflect.Uint16: + v.SetUint(uint64(d.uint16())) + case reflect.Uint32: + v.SetUint(uint64(d.uint32())) + case reflect.Uint64: + v.SetUint(d.uint64()) + + case reflect.Float32: + v.SetFloat(float64(math.Float32frombits(d.uint32()))) + case reflect.Float64: + v.SetFloat(math.Float64frombits(d.uint64())) + + case reflect.Complex64: + v.SetComplex(complex( + float64(math.Float32frombits(d.uint32())), + float64(math.Float32frombits(d.uint32())), + )) + case reflect.Complex128: + v.SetComplex(complex( + math.Float64frombits(d.uint64()), + math.Float64frombits(d.uint64()), + )) + } +} + +func (e *encoder) value(v reflect.Value) { + switch v.Kind() { + case reflect.Array: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Struct: + t := v.Type() + l := v.NumField() + for i := 0; i < l; i++ { + // see comment for corresponding code in decoder.value() + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + e.value(v) + } else { + e.skip(v) + } + } + + case reflect.Slice: + l := v.Len() + for i := 0; i < l; i++ { + e.value(v.Index(i)) + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch v.Type().Kind() { + case reflect.Int8: + e.int8(int8(v.Int())) + case reflect.Int16: + e.int16(int16(v.Int())) + case reflect.Int32: + e.int32(int32(v.Int())) + case reflect.Int64: + e.int64(v.Int()) + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch v.Type().Kind() { + case reflect.Uint8: + e.uint8(uint8(v.Uint())) + case reflect.Uint16: + e.uint16(uint16(v.Uint())) + case reflect.Uint32: + e.uint32(uint32(v.Uint())) + case reflect.Uint64: + e.uint64(v.Uint()) + } + + case reflect.Float32, reflect.Float64: + switch v.Type().Kind() { + case reflect.Float32: + e.uint32(math.Float32bits(float32(v.Float()))) + case reflect.Float64: + e.uint64(math.Float64bits(v.Float())) + } + + case reflect.Complex64, reflect.Complex128: + switch v.Type().Kind() { + case reflect.Complex64: + x := v.Complex() + e.uint32(math.Float32bits(float32(real(x)))) + e.uint32(math.Float32bits(float32(imag(x)))) + case reflect.Complex128: + x := v.Complex() + e.uint64(math.Float64bits(real(x))) + e.uint64(math.Float64bits(imag(x))) + } + } +} + +func (d *decoder) skip(v reflect.Value) { + d.buf = d.buf[dataSize(v):] +} + +func (e *encoder) skip(v reflect.Value) { + n := dataSize(v) + for i := range e.buf[0:n] { + e.buf[i] = 0 + } + e.buf = e.buf[n:] +} + +// intDataSize returns the size of the data required to represent the data when encoded. +// It returns zero if the type cannot be implemented by the fast path in Read or Write. +func intDataSize(data interface{}) int { + switch data := data.(type) { + case int8, *int8, *uint8: + return 1 + case []int8: + return len(data) + case []uint8: + return len(data) + case int16, *int16, *uint16: + return 2 + case []int16: + return 2 * len(data) + case []uint16: + return 2 * len(data) + case int32, *int32, *uint32: + return 4 + case []int32: + return 4 * len(data) + case []uint32: + return 4 * len(data) + case int64, *int64, *uint64: + return 8 + case []int64: + return 8 * len(data) + case []uint64: + return 8 * len(data) + } + return 0 +} diff --git a/disk/disk_freebsd.go b/disk/disk_freebsd.go index 3d3fce0..56aa14f 100644 --- a/disk/disk_freebsd.go +++ b/disk/disk_freebsd.go @@ -3,79 +3,88 @@ 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, MntWait) + count, err := syscall.Getfsstat(nil, MNT_WAIT) if err != nil { return ret, err } fs := make([]Statfs, count) - _, err = Getfsstat(fs, MntWait) + _, err = Getfsstat(fs, MNT_WAIT) for _, stat := range fs { opts := "rw" - if stat.FFlags&MntReadOnly != 0 { + if stat.Flags&MNT_RDONLY != 0 { opts = "ro" } - if stat.FFlags&MntSynchronous != 0 { + if stat.Flags&MNT_SYNCHRONOUS != 0 { opts += ",sync" } - if stat.FFlags&MntNoExec != 0 { + if stat.Flags&MNT_NOEXEC != 0 { opts += ",noexec" } - if stat.FFlags&MntNoSuid != 0 { + if stat.Flags&MNT_NOSUID != 0 { opts += ",nosuid" } - if stat.FFlags&MntUnion != 0 { + if stat.Flags&MNT_UNION != 0 { opts += ",union" } - if stat.FFlags&MntAsync != 0 { + if stat.Flags&MNT_ASYNC != 0 { opts += ",async" } - if stat.FFlags&MntSuidDir != 0 { + if stat.Flags&MNT_SUIDDIR != 0 { opts += ",suiddir" } - if stat.FFlags&MntSoftDep != 0 { + if stat.Flags&MNT_SOFTDEP != 0 { opts += ",softdep" } - if stat.FFlags&MntNoSymFollow != 0 { + if stat.Flags&MNT_NOSYMFOLLOW != 0 { opts += ",nosymfollow" } - if stat.FFlags&MntGEOMJournal != 0 { + if stat.Flags&MNT_GJOURNAL != 0 { opts += ",gjounalc" } - if stat.FFlags&MntMultilabel != 0 { + if stat.Flags&MNT_MULTILABEL != 0 { opts += ",multilabel" } - if stat.FFlags&MntACLs != 0 { + if stat.Flags&MNT_ACLS != 0 { opts += ",acls" } - if stat.FFlags&MntNoATime != 0 { + if stat.Flags&MNT_NOATIME != 0 { opts += ",noattime" } - if stat.FFlags&MntClusterRead != 0 { + if stat.Flags&MNT_NOCLUSTERR != 0 { opts += ",nocluster" } - if stat.FFlags&MntClusterWrite != 0 { + if stat.Flags&MNT_NOCLUSTERW != 0 { opts += ",noclusterw" } - if stat.FFlags&MntNFS4ACLs != 0 { + if stat.Flags&MNT_NFS4ACLS != 0 { opts += ",nfs4acls" } d := DiskPartitionStat{ - Device: common.ByteToString(stat.FMntfromname[:]), - Mountpoint: common.ByteToString(stat.FMntonname[:]), - Fstype: common.ByteToString(stat.FFstypename[:]), + Device: common.IntToString(stat.Mntfromname[:]), + Mountpoint: common.IntToString(stat.Mntonname[:]), + Fstype: common.IntToString(stat.Fstypename[:]), Opts: opts, } ret = append(ret, d) @@ -85,35 +94,55 @@ func DiskPartitions(all bool) ([]DiskPartitionStat, error) { } func DiskIOCounters() (map[string]DiskIOCountersStat, error) { - return nil, common.NotImplementedError - // statinfo->devinfo->devstat // /usr/include/devinfo.h - // get length - count, err := Getfsstat(nil, MntWait) + // 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 } - fs := make([]Statfs, count) - _, err = Getfsstat(fs, MntWait) - - ret := make(map[string]DiskIOCountersStat, 0) - for _, stat := range fs { - name := common.ByteToString(stat.FMntonname[:]) - d := DiskIOCountersStat{ + 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, - ReadCount: stat.FSyncwrites + stat.FAsyncwrites, - WriteCount: stat.FSyncreads + stat.FAsyncreads, } - - ret[name] = d + 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) { @@ -130,3 +159,15 @@ func Getfsstat(buf []Statfs, flags int) (n int, err error) { } 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 +} diff --git a/disk/disk_freebsd_amd64.go b/disk/disk_freebsd_amd64.go index debc6b2..bbae159 100644 --- a/disk/disk_freebsd_amd64.go +++ b/disk/disk_freebsd_amd64.go @@ -1,104 +1,111 @@ -// +build freebsd -// +build amd64 +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go package disk const ( - MntWait = 1 - MfsNameLen = 16 /* length of type name including null */ - MNameLen = 88 /* size of on/from name bufs */ + 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 ) -// 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 ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 + _C_long_double int64 ) type Statfs struct { - FVersion uint32 /* structure version number */ - FType uint32 /* type of filesystem */ - FFlags uint64 /* copy of mount exported flags */ - FBsize uint64 /* filesystem fragment size */ - FIosize uint64 /* optimal transfer block size */ - FBlocks uint64 /* total data blocks in filesystem */ - FBfree uint64 /* free blocks in filesystem */ - FBavail int64 /* free blocks avail to non-superuser */ - FFiles uint64 /* total file nodes in filesystem */ - FFfree int64 /* free nodes avail to non-superuser */ - FSyncwrites uint64 /* count of sync writes since mount */ - FAsyncwrites uint64 /* count of async writes since mount */ - FSyncreads uint64 /* count of sync reads since mount */ - FAsyncreads uint64 /* count of async reads since mount */ - FSpare [10]uint64 /* unused spare */ - FNamemax uint32 /* maximum filename length */ - FOwner uint32 /* user that mounted the filesystem */ - FFsid int32 /* filesystem id */ - FCharspare [80]byte /* spare string space */ - FFstypename [MfsNameLen]byte /* filesystem type name */ - FMntfromname [MNameLen]byte /* mounted filesystem */ - FMntonname [MNameLen]byte /* directory on which mounted */ + 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 } -// /usr/include/devstat.h -// devstat_getdevs() -// kern.devstat.all -> devstats list struct - -// struct devinfo { -// struct devstat *devices; -// u_int8_t *mem_ptr; -// long generation; -// int numdevs; -// }; -// -// struct statinfo { -// long cp_time[CPUSTATES]; -// long tk_nin; -// long tk_nout; -// struct devinfo *dinfo; -// long double snap_time; -// }; - -// /usr/include/devinfo.h +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 +} -// struct devinfo_dev { -// devinfo_handle_t dd_handle; /* device handle */ -// devinfo_handle_t dd_parent; /* parent handle */ -// char *dd_name; /* name of device */ -// char *dd_desc; /* device description */ -// char *dd_drivername; /* name of attached driver */ -// char *dd_pnpinfo; /* pnp info from parent bus */ -// char *dd_location; /* Where bus thinks dev at */ -// uint32_t dd_devflags; /* API flags */ -// uint16_t dd_flags; /* internal dev flags */ -// device_state_t dd_state; /* attachment state of dev */ -// }; -// -// struct devinfo_rman { -// devinfo_handle_t dm_handle; /* resource manager handle */ -// u_long dm_start; /* resource start */ -// u_long dm_size; /* resource size */ -// char *dm_desc; /* resource description */ -// }; -// -// struct devinfo_res { -// devinfo_handle_t dr_handle; /* resource handle */ -// devinfo_handle_t dr_rman; /* resource manager handle */ -// devinfo_handle_t dr_device; /* owning device */ -// u_long dr_start; /* region start */ -// u_long dr_size; /* region size */ -// }; +type _Ctype_struct___0 struct { + Empty uint64 +} diff --git a/disk/disk_test.go b/disk/disk_test.go index 7c614c8..d83160e 100644 --- a/disk/disk_test.go +++ b/disk/disk_test.go @@ -35,11 +35,15 @@ func TestDisk_partitions(t *testing.T) { func TestDisk_io_counters(t *testing.T) { ret, err := DiskIOCounters() - if err != nil || len(ret) == 0 { + 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) } diff --git a/disk/types_freebsd.go b/disk/types_freebsd.go new file mode 100644 index 0000000..4486904 --- /dev/null +++ b/disk/types_freebsd.go @@ -0,0 +1,85 @@ +// +build ignore +// Hand writing: _Ctype_struct___0 + +/* +Input to cgo -godefs. + +*/ + +package disk + +/* +#include +#include +#include + +enum { + sizeofPtr = sizeof(void*), +}; + +// because statinfo has long double snap_time, redefine with changing long long +struct statinfo2 { + long cp_time[CPUSTATES]; + long tk_nin; + long tk_nout; + struct devinfo *dinfo; + long long snap_time; +}; +*/ +import "C" + +// Machine characteristics; for internal use. + +const ( + sizeofPtr = C.sizeofPtr + sizeofShort = C.sizeof_short + sizeofInt = C.sizeof_int + sizeofLong = C.sizeof_long + sizeofLongLong = C.sizeof_longlong + sizeofLongDouble = C.sizeof_longlong + + DEVSTAT_NO_DATA = 0x00 + DEVSTAT_READ = 0x01 + DEVSTAT_WRITE = 0x02 + DEVSTAT_FREE = 0x03 + + // from sys/mount.h + MNT_RDONLY = 0x00000001 /* read only filesystem */ + MNT_SYNCHRONOUS = 0x00000002 /* filesystem written synchronously */ + MNT_NOEXEC = 0x00000004 /* can't exec from filesystem */ + MNT_NOSUID = 0x00000008 /* don't honor setuid bits on fs */ + MNT_UNION = 0x00000020 /* union with underlying filesystem */ + MNT_ASYNC = 0x00000040 /* filesystem written asynchronously */ + MNT_SUIDDIR = 0x00100000 /* special handling of SUID on dirs */ + MNT_SOFTDEP = 0x00200000 /* soft updates being done */ + MNT_NOSYMFOLLOW = 0x00400000 /* do not follow symlinks */ + MNT_GJOURNAL = 0x02000000 /* GEOM journal support enabled */ + MNT_MULTILABEL = 0x04000000 /* MAC support for individual objects */ + MNT_ACLS = 0x08000000 /* ACL support enabled */ + MNT_NOATIME = 0x10000000 /* disable update of file access time */ + MNT_NOCLUSTERR = 0x40000000 /* disable cluster read */ + MNT_NOCLUSTERW = 0x80000000 /* disable cluster write */ + MNT_NFS4ACLS = 0x00000010 + + MNT_WAIT = 1 /* synchronously wait for I/O to complete */ + MNT_NOWAIT = 2 /* start all I/O, but do not wait for it */ + MNT_LAZY = 3 /* push data not written by filesystem syncer */ + MNT_SUSPEND = 4 /* Suspend file system after sync */ + +) + +// Basic types + +type ( + _C_short C.short + _C_int C.int + _C_long C.long + _C_long_long C.longlong + _C_long_double C.longlong +) + +type Statfs C.struct_statfs +type Fsid C.struct_fsid + +type Devstat C.struct_devstat +type Bintime C.struct_bintime diff --git a/process/process_freebsd.go b/process/process_freebsd.go index b3b5069..373aab5 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -5,7 +5,6 @@ package process import ( "bytes" "encoding/binary" - "syscall" "unsafe" common "github.com/shirou/gopsutil/common" @@ -200,7 +199,7 @@ func processes() ([]Process, error) { results := make([]Process, 0, 50) mib := []int32{CTLKern, KernProc, KernProcProc, 0} - buf, length, err := callSyscall(mib) + buf, length, err := common.CallSyscall(mib) if err != nil { return results, err } @@ -240,48 +239,10 @@ func parseKinfoProc(buf []byte) (KinfoProc, error) { return k, nil } -func callSyscall(mib []int32) ([]byte, uint64, error) { - miblen := uint64(len(mib)) - - // get required buffer size - length := uint64(0) - _, _, err := syscall.Syscall6( - syscall.SYS___SYSCTL, - uintptr(unsafe.Pointer(&mib[0])), - uintptr(miblen), - 0, - uintptr(unsafe.Pointer(&length)), - 0, - 0) - if err != 0 { - var b []byte - return b, length, err - } - if length == 0 { - var b []byte - return b, length, err - } - // get proc info itself - buf := make([]byte, length) - _, _, err = syscall.Syscall6( - syscall.SYS___SYSCTL, - uintptr(unsafe.Pointer(&mib[0])), - uintptr(miblen), - uintptr(unsafe.Pointer(&buf[0])), - uintptr(unsafe.Pointer(&length)), - 0, - 0) - if err != 0 { - return buf, length, err - } - - return buf, length, nil -} - func (p *Process) getKProc() (*KinfoProc, error) { mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} - buf, length, err := callSyscall(mib) + buf, length, err := common.CallSyscall(mib) if err != nil { return nil, err }