github.com/elastic/gosigar@v0.14.3/sigar_aix.go (about)

     1  // +build aix
     2  
     3  package gosigar
     4  
     5  /*
     6  #cgo LDFLAGS: -L/usr/lib -lperfstat
     7  
     8  #include <libperfstat.h>
     9  #include <procinfo.h>
    10  #include <unistd.h>
    11  #include <utmp.h>
    12  #include <sys/mntctl.h>
    13  #include <sys/proc.h>
    14  #include <sys/types.h>
    15  #include <sys/vmount.h>
    16  
    17  */
    18  import "C"
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/binary"
    23  	"fmt"
    24  	"io"
    25  	"os"
    26  	"os/user"
    27  	"runtime"
    28  	"strconv"
    29  	"syscall"
    30  	"time"
    31  	"unsafe"
    32  )
    33  
    34  var system struct {
    35  	ticks    uint64
    36  	btime    uint64
    37  	pagesize uint64
    38  }
    39  
    40  func init() {
    41  	// sysconf(_SC_CLK_TCK) returns the number of ticks by second.
    42  	system.ticks = uint64(C.sysconf(C._SC_CLK_TCK))
    43  	system.pagesize = uint64(os.Getpagesize())
    44  }
    45  
    46  // utmp can't be used by "encoding/binary" if generated by cgo,
    47  // some pads will be explicitly missing.
    48  type utmp struct {
    49  	User        [256]uint8
    50  	ID          [14]uint8
    51  	Line        [64]uint8
    52  	XPad1       int16
    53  	Pid         int32
    54  	Type        int16
    55  	XPad2       int16
    56  	Time        int64
    57  	Termination int16
    58  	Exit        int16
    59  	Host        [256]uint8
    60  	XdblWordPad int32
    61  	XreservedA  [2]int32
    62  	XreservedV  [6]int32
    63  }
    64  
    65  func bootTime() (uint64, error) {
    66  	if system.btime != 0 {
    67  		return system.btime, nil
    68  	}
    69  
    70  	// Get boot time from /etc/utmp
    71  	file, err := os.Open("/etc/utmp")
    72  	if err != nil {
    73  		return 0, fmt.Errorf("error while opening /etc/utmp: %s", err)
    74  	}
    75  
    76  	defer file.Close()
    77  
    78  	for {
    79  		var utmp utmp
    80  		if err := binary.Read(file, binary.BigEndian, &utmp); err != nil {
    81  			break
    82  		}
    83  
    84  		if utmp.Type == C.BOOT_TIME {
    85  			system.btime = uint64(utmp.Time)
    86  			break
    87  		}
    88  	}
    89  	return system.btime, nil
    90  }
    91  
    92  func tick2msec(val uint64) uint64 {
    93  	return val * 1000 / system.ticks
    94  }
    95  
    96  // Get returns the list of file systems
    97  func (self *FileSystemList) Get() error {
    98  	var size C.int
    99  	_, err := C.mntctl(C.MCTL_QUERY, C.sizeof_int, (*C.char)(unsafe.Pointer(&size)))
   100  	if err != nil {
   101  		return fmt.Errorf("error while retrieving file system number: %s", err)
   102  	}
   103  
   104  	buf := make([]byte, size)
   105  	num, err := C.mntctl(C.MCTL_QUERY, C.ulong(size), (*C.char)(&buf[0]))
   106  	if err != nil {
   107  		return fmt.Errorf("error while retrieving file system list: %s", err)
   108  	}
   109  
   110  	// Vmount structure has a fixed size area for common data (type,
   111  	// offsets, etc) and another area with variable length data (devname,
   112  	// options, etc). These data can be accessed based on the offsets
   113  	// stored in an array inside the fixed part. They can be retrieve
   114  	// using index given by C define.
   115  	vmt2data := func(buf []byte, ent *C.struct_vmount, idx int, baseOff int) []byte {
   116  		off := int(ent.vmt_data[idx].vmt_off)
   117  		size := int(ent.vmt_data[idx].vmt_size)
   118  		return buf[baseOff+off : baseOff+off+size]
   119  	}
   120  
   121  	entOff := 0
   122  
   123  	fslist := make([]FileSystem, num)
   124  	for i := 0; i < int(num); i++ {
   125  		ent := (*C.struct_vmount)(unsafe.Pointer(&buf[entOff]))
   126  		fs := &fslist[i]
   127  
   128  		// Correspondances taken for /etc/vfs
   129  		switch ent.vmt_gfstype {
   130  		case C.MNT_AIX:
   131  			fs.SysTypeName = "jfs2"
   132  		case C.MNT_NAMEFS:
   133  			fs.SysTypeName = "namefs"
   134  		case C.MNT_NFS:
   135  			fs.SysTypeName = "nfs"
   136  		case C.MNT_JFS:
   137  			fs.SysTypeName = "jfs"
   138  		case C.MNT_CDROM:
   139  			fs.SysTypeName = "cdrom"
   140  		case C.MNT_PROCFS:
   141  			fs.SysTypeName = "proc"
   142  		case C.MNT_SFS:
   143  			fs.SysTypeName = "sfs"
   144  		case C.MNT_CACHEFS:
   145  			fs.SysTypeName = "cachefs"
   146  		case C.MNT_NFS3:
   147  			fs.SysTypeName = "nfs3"
   148  		case C.MNT_AUTOFS:
   149  			fs.SysTypeName = "autofs"
   150  		case C.MNT_POOLFS:
   151  			fs.SysTypeName = "poolfs"
   152  		case C.MNT_UDF:
   153  			fs.SysTypeName = "udfs"
   154  		case C.MNT_NFS4:
   155  			fs.SysTypeName = "nfs4"
   156  		case C.MNT_CIFS:
   157  			fs.SysTypeName = "cifs"
   158  		case C.MNT_PMEMFS:
   159  			fs.SysTypeName = "pmemfs"
   160  		case C.MNT_AHAFS:
   161  			fs.SysTypeName = "ahafs"
   162  		case C.MNT_STNFS:
   163  			fs.SysTypeName = "stnfs"
   164  		default:
   165  			if ent.vmt_flags&C.MNT_REMOTE != 0 {
   166  				fs.SysTypeName = "network"
   167  			} else {
   168  				fs.SysTypeName = "none"
   169  			}
   170  		}
   171  
   172  		fs.DirName = convertBytesToString(vmt2data(buf, ent, C.VMT_STUB, entOff))
   173  		fs.Options = convertBytesToString(vmt2data(buf, ent, C.VMT_ARGS, entOff))
   174  		devname := convertBytesToString(vmt2data(buf, ent, C.VMT_OBJECT, entOff))
   175  		if ent.vmt_flags&C.MNT_REMOTE != 0 {
   176  			hostname := convertBytesToString(vmt2data(buf, ent, C.VMT_OBJECT, entOff))
   177  			fs.DevName = hostname + ":" + devname
   178  		} else {
   179  			fs.DevName = devname
   180  		}
   181  
   182  		entOff += int(ent.vmt_length)
   183  	}
   184  
   185  	self.List = fslist
   186  
   187  	return nil
   188  }
   189  
   190  // Get returns the CPU load average
   191  func (self *LoadAverage) Get() error {
   192  	cpudata := C.perfstat_cpu_total_t{}
   193  
   194  	if _, err := C.perfstat_cpu_total(nil, &cpudata, C.sizeof_perfstat_cpu_total_t, 1); err != nil {
   195  		return fmt.Errorf("perfstat_cpu_total: %s", err)
   196  	}
   197  
   198  	// from libperfstat.h:
   199  	// "To calculate the load average, divide the numbers by (1<<SBITS).
   200  	//  SBITS is defined in <sys/proc.h>."
   201  	fixedToFloat64 := func(x uint64) float64 {
   202  		return float64(x) / (1 << C.SBITS)
   203  	}
   204  	self.One = fixedToFloat64(uint64(cpudata.loadavg[0]))
   205  	self.Five = fixedToFloat64(uint64(cpudata.loadavg[1]))
   206  	self.Fifteen = fixedToFloat64(uint64(cpudata.loadavg[2]))
   207  
   208  	return nil
   209  }
   210  
   211  // Get returns the system uptime
   212  func (self *Uptime) Get() error {
   213  	btime, err := bootTime()
   214  	if err != nil {
   215  		return err
   216  	}
   217  	uptime := time.Now().Sub(time.Unix(int64(btime), 0))
   218  	self.Length = uptime.Seconds()
   219  	return nil
   220  }
   221  
   222  // Get returns the current system memory
   223  func (self *Mem) Get() error {
   224  	meminfo := C.perfstat_memory_total_t{}
   225  	_, err := C.perfstat_memory_total(nil, &meminfo, C.sizeof_perfstat_memory_total_t, 1)
   226  	if err != nil {
   227  		return fmt.Errorf("perfstat_memory_total: %s", err)
   228  	}
   229  
   230  	self.Total = uint64(meminfo.real_total) * system.pagesize
   231  	self.Free = uint64(meminfo.real_free) * system.pagesize
   232  
   233  	kern := uint64(meminfo.numperm) * system.pagesize // number of pages in file cache
   234  
   235  	self.Used = self.Total - self.Free
   236  	self.ActualFree = self.Free + kern
   237  	self.ActualUsed = self.Used - kern
   238  
   239  	return nil
   240  }
   241  
   242  // Get returns the current system swap memory
   243  func (self *Swap) Get() error {
   244  	ps := C.perfstat_pagingspace_t{}
   245  	id := C.perfstat_id_t{}
   246  
   247  	id.name[0] = 0
   248  
   249  	for {
   250  		// errno can be set during perfstat_pagingspace's call even
   251  		// if it succeeds. Thus, only check it when the result is -1.
   252  		if r, err := C.perfstat_pagingspace(&id, &ps, C.sizeof_perfstat_pagingspace_t, 1); r == -1 && err != nil {
   253  			return fmt.Errorf("perfstat_memory_total: %s", err)
   254  		}
   255  
   256  		if ps.active != 1 {
   257  			continue
   258  		}
   259  
   260  		// convert MB sizes to bytes
   261  		self.Total += uint64(ps.mb_size) * 1024 * 1024
   262  		self.Used += uint64(ps.mb_used) * 1024 * 1024
   263  
   264  		if id.name[0] == 0 {
   265  			break
   266  		}
   267  	}
   268  
   269  	self.Free = self.Total - self.Used
   270  
   271  	return nil
   272  }
   273  
   274  // Get returns information about a CPU
   275  func (self *Cpu) Get() error {
   276  	cpudata := C.perfstat_cpu_total_t{}
   277  
   278  	if _, err := C.perfstat_cpu_total(nil, &cpudata, C.sizeof_perfstat_cpu_total_t, 1); err != nil {
   279  		return fmt.Errorf("perfstat_cpu_total: %s", err)
   280  	}
   281  
   282  	self.User = tick2msec(uint64(cpudata.user))
   283  	self.Sys = tick2msec(uint64(cpudata.sys))
   284  	self.Idle = tick2msec(uint64(cpudata.idle))
   285  	self.Wait = tick2msec(uint64(cpudata.wait))
   286  
   287  	return nil
   288  }
   289  
   290  // Get returns the list of CPU used by the system
   291  func (self *CpuList) Get() error {
   292  	cpudata := C.perfstat_cpu_t{}
   293  	id := C.perfstat_id_t{}
   294  	id.name[0] = 0
   295  
   296  	// Retrieve the number of cpu using perfstat_cpu
   297  	capacity, err := C.perfstat_cpu(nil, nil, C.sizeof_perfstat_cpu_t, 0)
   298  	if err != nil {
   299  		return fmt.Errorf("error while retrieving CPU number: %s", err)
   300  	}
   301  	list := make([]Cpu, 0, capacity)
   302  
   303  	for {
   304  		if _, err := C.perfstat_cpu(&id, &cpudata, C.sizeof_perfstat_cpu_t, 1); err != nil {
   305  			return fmt.Errorf("perfstat_cpu: %s", err)
   306  		}
   307  
   308  		cpu := Cpu{}
   309  		cpu.User = tick2msec(uint64(cpudata.user))
   310  		cpu.Sys = tick2msec(uint64(cpudata.sys))
   311  		cpu.Idle = tick2msec(uint64(cpudata.idle))
   312  		cpu.Wait = tick2msec(uint64(cpudata.wait))
   313  
   314  		list = append(list, cpu)
   315  
   316  		if id.name[0] == 0 {
   317  			break
   318  		}
   319  	}
   320  
   321  	self.List = list
   322  
   323  	return nil
   324  }
   325  
   326  // Get returns the list of all active processes
   327  func (self *ProcList) Get() error {
   328  	info := C.struct_procsinfo64{}
   329  	pid := C.pid_t(0)
   330  
   331  	var list []int
   332  
   333  	for {
   334  		// getprocs first argument is a void*
   335  		num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &pid, 1)
   336  		if err != nil {
   337  			return err
   338  		}
   339  
   340  		list = append(list, int(info.pi_pid))
   341  
   342  		if num == 0 {
   343  			break
   344  		}
   345  	}
   346  
   347  	self.List = list
   348  
   349  	return nil
   350  }
   351  
   352  // Get returns information about a process
   353  func (self *ProcState) Get(pid int) error {
   354  	info := C.struct_procsinfo64{}
   355  	cpid := C.pid_t(pid)
   356  
   357  	num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1)
   358  	if err != nil {
   359  		return err
   360  	}
   361  	if num != 1 {
   362  		return syscall.ESRCH
   363  	}
   364  
   365  	self.Name = C.GoString(&info.pi_comm[0])
   366  	self.Ppid = int(info.pi_ppid)
   367  	self.Pgid = int(info.pi_pgrp)
   368  	self.Nice = int(info.pi_nice)
   369  	self.Tty = int(info.pi_ttyd)
   370  	self.Priority = int(info.pi_pri)
   371  
   372  	switch info.pi_state {
   373  	case C.SACTIVE:
   374  		self.State = RunStateRun
   375  	case C.SIDL:
   376  		self.State = RunStateIdle
   377  	case C.SSTOP:
   378  		self.State = RunStateStop
   379  	case C.SZOMB:
   380  		self.State = RunStateZombie
   381  	case C.SSWAP:
   382  		self.State = RunStateSleep
   383  	default:
   384  		self.State = RunStateUnknown
   385  	}
   386  
   387  	// Get process username. Fallback to UID if username is not available.
   388  	uid := strconv.Itoa(int(info.pi_uid))
   389  	userID, err := user.LookupId(uid)
   390  	if err == nil && userID.Username != "" {
   391  		self.Username = userID.Username
   392  	} else {
   393  		self.Username = uid
   394  	}
   395  
   396  	thrinfo := C.struct_thrdsinfo64{}
   397  	tid := C.tid_t(0)
   398  
   399  	if _, err := C.getthrds(cpid, unsafe.Pointer(&thrinfo), C.sizeof_struct_thrdsinfo64, &tid, 1); err != nil {
   400  		self.Processor = int(thrinfo.ti_affinity)
   401  	}
   402  
   403  	return nil
   404  }
   405  
   406  //Get returns the current memory usage of a process
   407  func (self *ProcMem) Get(pid int) error {
   408  	info := C.struct_procsinfo64{}
   409  	cpid := C.pid_t(pid)
   410  
   411  	num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1)
   412  	if err != nil {
   413  		return err
   414  	}
   415  	if num != 1 {
   416  		return syscall.ESRCH
   417  	}
   418  
   419  	self.Size = uint64(info.pi_size) * system.pagesize
   420  	self.Share = uint64(info.pi_sdsize) * system.pagesize
   421  	self.Resident = uint64(info.pi_drss+info.pi_trss) * system.pagesize
   422  
   423  	self.MinorFaults = uint64(info.pi_minflt)
   424  	self.MajorFaults = uint64(info.pi_majflt)
   425  	self.PageFaults = self.MinorFaults + self.MajorFaults
   426  
   427  	return nil
   428  }
   429  
   430  // Get returns a process uptime
   431  func (self *ProcTime) Get(pid int) error {
   432  	info := C.struct_procsinfo64{}
   433  	cpid := C.pid_t(pid)
   434  
   435  	num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1)
   436  	if err != nil {
   437  		return err
   438  	}
   439  	if num != 1 {
   440  		return syscall.ESRCH
   441  	}
   442  
   443  	self.StartTime = uint64(info.pi_start) * 1000
   444  	self.User = uint64(info.pi_utime) * 1000
   445  	self.Sys = uint64(info.pi_stime) * 1000
   446  	self.Total = self.User + self.Sys
   447  
   448  	return nil
   449  }
   450  
   451  // Get returns arguments of a process
   452  func (self *ProcArgs) Get(pid int) error {
   453  	/* If buffer is not large enough, args are truncated */
   454  	buf := make([]byte, 8192)
   455  	info := C.struct_procsinfo64{}
   456  	info.pi_pid = C.pid_t(pid)
   457  
   458  	if _, err := C.getargs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil {
   459  		return err
   460  	}
   461  
   462  	bbuf := bytes.NewBuffer(buf)
   463  
   464  	var args []string
   465  
   466  	for {
   467  		arg, err := bbuf.ReadBytes(0)
   468  		if err == io.EOF || arg[0] == 0 {
   469  			break
   470  		}
   471  		if err != nil {
   472  			return err
   473  		}
   474  
   475  		args = append(args, string(chop(arg)))
   476  	}
   477  
   478  	self.List = args
   479  	return nil
   480  }
   481  
   482  // Get returns the environment of a process
   483  func (self *ProcEnv) Get(pid int) error {
   484  	if self.Vars == nil {
   485  		self.Vars = map[string]string{}
   486  	}
   487  
   488  	/* If buffer is not large enough, args are truncated */
   489  	buf := make([]byte, 8192)
   490  	info := C.struct_procsinfo64{}
   491  	info.pi_pid = C.pid_t(pid)
   492  
   493  	if _, err := C.getevars(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil {
   494  		return err
   495  	}
   496  
   497  	bbuf := bytes.NewBuffer(buf)
   498  
   499  	delim := []byte{61} // "="
   500  
   501  	for {
   502  		line, err := bbuf.ReadBytes(0)
   503  		if err == io.EOF || line[0] == 0 {
   504  			break
   505  		}
   506  		if err != nil {
   507  			return err
   508  		}
   509  
   510  		pair := bytes.SplitN(chop(line), delim, 2)
   511  		if len(pair) != 2 {
   512  			return fmt.Errorf("Error reading process environment for PID: %d", pid)
   513  		}
   514  		self.Vars[string(pair[0])] = string(pair[1])
   515  	}
   516  
   517  	return nil
   518  }
   519  
   520  // Get returns the path of the process executable
   521  func (self *ProcExe) Get(pid int) error {
   522  	/* If buffer is not large enough, args are truncated */
   523  	buf := make([]byte, 8192)
   524  	info := C.struct_procsinfo64{}
   525  	info.pi_pid = C.pid_t(pid)
   526  
   527  	if _, err := C.getargs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil {
   528  		return err
   529  	}
   530  
   531  	bbuf := bytes.NewBuffer(buf)
   532  
   533  	// retrieve the first argument
   534  	cmd, err := bbuf.ReadBytes(0)
   535  	if err != nil {
   536  		return err
   537  	}
   538  	self.Name = string(chop(cmd))
   539  
   540  	cwd, err := os.Readlink("/proc/" + strconv.Itoa(pid) + "/cwd")
   541  	if err != nil {
   542  		if !os.IsNotExist(err) {
   543  			return err
   544  		}
   545  	}
   546  	self.Cwd = cwd
   547  
   548  	return nil
   549  }
   550  
   551  // Get returns process filesystem usage. Not implimented on AIX.
   552  func (*ProcFDUsage) Get(_ int) error {
   553  	return ErrNotImplemented{runtime.GOOS}
   554  }
   555  
   556  // Get returns filesytem usage. Not implimented on AIX.
   557  func (*FDUsage) Get() error {
   558  	return ErrNotImplemented{runtime.GOOS}
   559  }
   560  
   561  // Get returns huge pages info. Not implimented on AIX.
   562  func (*HugeTLBPages) Get() error {
   563  	return ErrNotImplemented{runtime.GOOS}
   564  }