status-go/vendor/github.com/elastic/gosigar/sigar_aix.go

565 lines
13 KiB
Go
Raw Permalink Normal View History

// +build aix
package gosigar
/*
#cgo LDFLAGS: -L/usr/lib -lperfstat
#include <libperfstat.h>
#include <procinfo.h>
#include <unistd.h>
#include <utmp.h>
#include <sys/mntctl.h>
#include <sys/proc.h>
#include <sys/types.h>
#include <sys/vmount.h>
*/
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"os"
"os/user"
"runtime"
"strconv"
"syscall"
"time"
"unsafe"
)
var system struct {
ticks uint64
btime uint64
pagesize uint64
}
func init() {
// sysconf(_SC_CLK_TCK) returns the number of ticks by second.
system.ticks = uint64(C.sysconf(C._SC_CLK_TCK))
system.pagesize = uint64(os.Getpagesize())
}
// utmp can't be used by "encoding/binary" if generated by cgo,
// some pads will be explicitly missing.
type utmp struct {
User [256]uint8
ID [14]uint8
Line [64]uint8
XPad1 int16
Pid int32
Type int16
XPad2 int16
Time int64
Termination int16
Exit int16
Host [256]uint8
XdblWordPad int32
XreservedA [2]int32
XreservedV [6]int32
}
func bootTime() (uint64, error) {
if system.btime != 0 {
return system.btime, nil
}
// Get boot time from /etc/utmp
file, err := os.Open("/etc/utmp")
if err != nil {
return 0, fmt.Errorf("error while opening /etc/utmp: %s", err)
}
defer file.Close()
for {
var utmp utmp
if err := binary.Read(file, binary.BigEndian, &utmp); err != nil {
break
}
if utmp.Type == C.BOOT_TIME {
system.btime = uint64(utmp.Time)
break
}
}
return system.btime, nil
}
func tick2msec(val uint64) uint64 {
return val * 1000 / system.ticks
}
// Get returns the list of file systems
func (self *FileSystemList) Get() error {
var size C.int
_, err := C.mntctl(C.MCTL_QUERY, C.sizeof_int, (*C.char)(unsafe.Pointer(&size)))
if err != nil {
return fmt.Errorf("error while retrieving file system number: %s", err)
}
buf := make([]byte, size)
num, err := C.mntctl(C.MCTL_QUERY, C.ulong(size), (*C.char)(&buf[0]))
if err != nil {
return fmt.Errorf("error while retrieving file system list: %s", err)
}
// Vmount structure has a fixed size area for common data (type,
// offsets, etc) and another area with variable length data (devname,
// options, etc). These data can be accessed based on the offsets
// stored in an array inside the fixed part. They can be retrieve
// using index given by C define.
vmt2data := func(buf []byte, ent *C.struct_vmount, idx int, baseOff int) []byte {
off := int(ent.vmt_data[idx].vmt_off)
size := int(ent.vmt_data[idx].vmt_size)
return buf[baseOff+off : baseOff+off+size]
}
entOff := 0
fslist := make([]FileSystem, num)
for i := 0; i < int(num); i++ {
ent := (*C.struct_vmount)(unsafe.Pointer(&buf[entOff]))
fs := &fslist[i]
// Correspondances taken for /etc/vfs
switch ent.vmt_gfstype {
case C.MNT_AIX:
fs.SysTypeName = "jfs2"
case C.MNT_NAMEFS:
fs.SysTypeName = "namefs"
case C.MNT_NFS:
fs.SysTypeName = "nfs"
case C.MNT_JFS:
fs.SysTypeName = "jfs"
case C.MNT_CDROM:
fs.SysTypeName = "cdrom"
case C.MNT_PROCFS:
fs.SysTypeName = "proc"
case C.MNT_SFS:
fs.SysTypeName = "sfs"
case C.MNT_CACHEFS:
fs.SysTypeName = "cachefs"
case C.MNT_NFS3:
fs.SysTypeName = "nfs3"
case C.MNT_AUTOFS:
fs.SysTypeName = "autofs"
case C.MNT_POOLFS:
fs.SysTypeName = "poolfs"
case C.MNT_UDF:
fs.SysTypeName = "udfs"
case C.MNT_NFS4:
fs.SysTypeName = "nfs4"
case C.MNT_CIFS:
fs.SysTypeName = "cifs"
case C.MNT_PMEMFS:
fs.SysTypeName = "pmemfs"
case C.MNT_AHAFS:
fs.SysTypeName = "ahafs"
case C.MNT_STNFS:
fs.SysTypeName = "stnfs"
default:
if ent.vmt_flags&C.MNT_REMOTE != 0 {
fs.SysTypeName = "network"
} else {
fs.SysTypeName = "none"
}
}
fs.DirName = convertBytesToString(vmt2data(buf, ent, C.VMT_STUB, entOff))
fs.Options = convertBytesToString(vmt2data(buf, ent, C.VMT_ARGS, entOff))
devname := convertBytesToString(vmt2data(buf, ent, C.VMT_OBJECT, entOff))
if ent.vmt_flags&C.MNT_REMOTE != 0 {
hostname := convertBytesToString(vmt2data(buf, ent, C.VMT_OBJECT, entOff))
fs.DevName = hostname + ":" + devname
} else {
fs.DevName = devname
}
entOff += int(ent.vmt_length)
}
self.List = fslist
return nil
}
// Get returns the CPU load average
func (self *LoadAverage) Get() error {
cpudata := C.perfstat_cpu_total_t{}
if _, err := C.perfstat_cpu_total(nil, &cpudata, C.sizeof_perfstat_cpu_total_t, 1); err != nil {
return fmt.Errorf("perfstat_cpu_total: %s", err)
}
// from libperfstat.h:
// "To calculate the load average, divide the numbers by (1<<SBITS).
// SBITS is defined in <sys/proc.h>."
fixedToFloat64 := func(x uint64) float64 {
return float64(x) / (1 << C.SBITS)
}
self.One = fixedToFloat64(uint64(cpudata.loadavg[0]))
self.Five = fixedToFloat64(uint64(cpudata.loadavg[1]))
self.Fifteen = fixedToFloat64(uint64(cpudata.loadavg[2]))
return nil
}
// Get returns the system uptime
func (self *Uptime) Get() error {
btime, err := bootTime()
if err != nil {
return err
}
uptime := time.Now().Sub(time.Unix(int64(btime), 0))
self.Length = uptime.Seconds()
return nil
}
// Get returns the current system memory
func (self *Mem) Get() error {
meminfo := C.perfstat_memory_total_t{}
_, err := C.perfstat_memory_total(nil, &meminfo, C.sizeof_perfstat_memory_total_t, 1)
if err != nil {
return fmt.Errorf("perfstat_memory_total: %s", err)
}
self.Total = uint64(meminfo.real_total) * system.pagesize
self.Free = uint64(meminfo.real_free) * system.pagesize
kern := uint64(meminfo.numperm) * system.pagesize // number of pages in file cache
self.Used = self.Total - self.Free
self.ActualFree = self.Free + kern
self.ActualUsed = self.Used - kern
return nil
}
// Get returns the current system swap memory
func (self *Swap) Get() error {
ps := C.perfstat_pagingspace_t{}
id := C.perfstat_id_t{}
id.name[0] = 0
for {
// errno can be set during perfstat_pagingspace's call even
// if it succeeds. Thus, only check it when the result is -1.
if r, err := C.perfstat_pagingspace(&id, &ps, C.sizeof_perfstat_pagingspace_t, 1); r == -1 && err != nil {
return fmt.Errorf("perfstat_memory_total: %s", err)
}
if ps.active != 1 {
continue
}
// convert MB sizes to bytes
self.Total += uint64(ps.mb_size) * 1024 * 1024
self.Used += uint64(ps.mb_used) * 1024 * 1024
if id.name[0] == 0 {
break
}
}
self.Free = self.Total - self.Used
return nil
}
// Get returns information about a CPU
func (self *Cpu) Get() error {
cpudata := C.perfstat_cpu_total_t{}
if _, err := C.perfstat_cpu_total(nil, &cpudata, C.sizeof_perfstat_cpu_total_t, 1); err != nil {
return fmt.Errorf("perfstat_cpu_total: %s", err)
}
self.User = tick2msec(uint64(cpudata.user))
self.Sys = tick2msec(uint64(cpudata.sys))
self.Idle = tick2msec(uint64(cpudata.idle))
self.Wait = tick2msec(uint64(cpudata.wait))
return nil
}
// Get returns the list of CPU used by the system
func (self *CpuList) Get() error {
cpudata := C.perfstat_cpu_t{}
id := C.perfstat_id_t{}
id.name[0] = 0
// Retrieve the number of cpu using perfstat_cpu
capacity, err := C.perfstat_cpu(nil, nil, C.sizeof_perfstat_cpu_t, 0)
if err != nil {
return fmt.Errorf("error while retrieving CPU number: %s", err)
}
list := make([]Cpu, 0, capacity)
for {
if _, err := C.perfstat_cpu(&id, &cpudata, C.sizeof_perfstat_cpu_t, 1); err != nil {
return fmt.Errorf("perfstat_cpu: %s", err)
}
cpu := Cpu{}
cpu.User = tick2msec(uint64(cpudata.user))
cpu.Sys = tick2msec(uint64(cpudata.sys))
cpu.Idle = tick2msec(uint64(cpudata.idle))
cpu.Wait = tick2msec(uint64(cpudata.wait))
list = append(list, cpu)
if id.name[0] == 0 {
break
}
}
self.List = list
return nil
}
// Get returns the list of all active processes
func (self *ProcList) Get() error {
info := C.struct_procsinfo64{}
pid := C.pid_t(0)
var list []int
for {
// getprocs first argument is a void*
num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &pid, 1)
if err != nil {
return err
}
list = append(list, int(info.pi_pid))
if num == 0 {
break
}
}
self.List = list
return nil
}
// Get returns information about a process
func (self *ProcState) Get(pid int) error {
info := C.struct_procsinfo64{}
cpid := C.pid_t(pid)
num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1)
if err != nil {
return err
}
if num != 1 {
return syscall.ESRCH
}
self.Name = C.GoString(&info.pi_comm[0])
self.Ppid = int(info.pi_ppid)
self.Pgid = int(info.pi_pgrp)
self.Nice = int(info.pi_nice)
self.Tty = int(info.pi_ttyd)
self.Priority = int(info.pi_pri)
switch info.pi_state {
case C.SACTIVE:
self.State = RunStateRun
case C.SIDL:
self.State = RunStateIdle
case C.SSTOP:
self.State = RunStateStop
case C.SZOMB:
self.State = RunStateZombie
case C.SSWAP:
self.State = RunStateSleep
default:
self.State = RunStateUnknown
}
// Get process username. Fallback to UID if username is not available.
uid := strconv.Itoa(int(info.pi_uid))
userID, err := user.LookupId(uid)
if err == nil && userID.Username != "" {
self.Username = userID.Username
} else {
self.Username = uid
}
thrinfo := C.struct_thrdsinfo64{}
tid := C.tid_t(0)
if _, err := C.getthrds(cpid, unsafe.Pointer(&thrinfo), C.sizeof_struct_thrdsinfo64, &tid, 1); err != nil {
self.Processor = int(thrinfo.ti_affinity)
}
return nil
}
//Get returns the current memory usage of a process
func (self *ProcMem) Get(pid int) error {
info := C.struct_procsinfo64{}
cpid := C.pid_t(pid)
num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1)
if err != nil {
return err
}
if num != 1 {
return syscall.ESRCH
}
self.Size = uint64(info.pi_size) * system.pagesize
self.Share = uint64(info.pi_sdsize) * system.pagesize
self.Resident = uint64(info.pi_drss+info.pi_trss) * system.pagesize
self.MinorFaults = uint64(info.pi_minflt)
self.MajorFaults = uint64(info.pi_majflt)
self.PageFaults = self.MinorFaults + self.MajorFaults
return nil
}
// Get returns a process uptime
func (self *ProcTime) Get(pid int) error {
info := C.struct_procsinfo64{}
cpid := C.pid_t(pid)
num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1)
if err != nil {
return err
}
if num != 1 {
return syscall.ESRCH
}
self.StartTime = uint64(info.pi_start) * 1000
self.User = uint64(info.pi_utime) * 1000
self.Sys = uint64(info.pi_stime) * 1000
self.Total = self.User + self.Sys
return nil
}
// Get returns arguments of a process
func (self *ProcArgs) Get(pid int) error {
/* If buffer is not large enough, args are truncated */
buf := make([]byte, 8192)
info := C.struct_procsinfo64{}
info.pi_pid = C.pid_t(pid)
if _, err := C.getargs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil {
return err
}
bbuf := bytes.NewBuffer(buf)
var args []string
for {
arg, err := bbuf.ReadBytes(0)
if err == io.EOF || arg[0] == 0 {
break
}
if err != nil {
return err
}
args = append(args, string(chop(arg)))
}
self.List = args
return nil
}
// Get returns the environment of a process
func (self *ProcEnv) Get(pid int) error {
if self.Vars == nil {
self.Vars = map[string]string{}
}
/* If buffer is not large enough, args are truncated */
buf := make([]byte, 8192)
info := C.struct_procsinfo64{}
info.pi_pid = C.pid_t(pid)
if _, err := C.getevars(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil {
return err
}
bbuf := bytes.NewBuffer(buf)
delim := []byte{61} // "="
for {
line, err := bbuf.ReadBytes(0)
if err == io.EOF || line[0] == 0 {
break
}
if err != nil {
return err
}
pair := bytes.SplitN(chop(line), delim, 2)
if len(pair) != 2 {
return fmt.Errorf("Error reading process environment for PID: %d", pid)
}
self.Vars[string(pair[0])] = string(pair[1])
}
return nil
}
// Get returns the path of the process executable
func (self *ProcExe) Get(pid int) error {
/* If buffer is not large enough, args are truncated */
buf := make([]byte, 8192)
info := C.struct_procsinfo64{}
info.pi_pid = C.pid_t(pid)
if _, err := C.getargs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil {
return err
}
bbuf := bytes.NewBuffer(buf)
// retrieve the first argument
cmd, err := bbuf.ReadBytes(0)
if err != nil {
return err
}
self.Name = string(chop(cmd))
cwd, err := os.Readlink("/proc/" + strconv.Itoa(pid) + "/cwd")
if err != nil {
if !os.IsNotExist(err) {
return err
}
}
self.Cwd = cwd
return nil
}
// Get returns process filesystem usage. Not implimented on AIX.
func (*ProcFDUsage) Get(_ int) error {
return ErrNotImplemented{runtime.GOOS}
}
// Get returns filesytem usage. Not implimented on AIX.
func (*FDUsage) Get() error {
return ErrNotImplemented{runtime.GOOS}
}
// Get returns huge pages info. Not implimented on AIX.
func (*HugeTLBPages) Get() error {
return ErrNotImplemented{runtime.GOOS}
}