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

     1  // Copyright (c) 2012 VMware, Inc.
     2  
     3  package gosigar
     4  
     5  /*
     6  #include <stdlib.h>
     7  #include <sys/sysctl.h>
     8  #include <sys/mount.h>
     9  #include <mach/mach_init.h>
    10  #include <mach/mach_host.h>
    11  #include <mach/host_info.h>
    12  #include <libproc.h>
    13  #include <mach/processor_info.h>
    14  #include <mach/vm_map.h>
    15  */
    16  import "C"
    17  
    18  import (
    19  	"bytes"
    20  	"encoding/binary"
    21  	"fmt"
    22  	"io"
    23  	"os/user"
    24  	"runtime"
    25  	"strconv"
    26  	"syscall"
    27  	"time"
    28  	"unsafe"
    29  
    30  	"golang.org/x/sys/unix"
    31  )
    32  
    33  // Get fetches LoadAverage data
    34  func (self *LoadAverage) Get() error {
    35  	avg := []C.double{0, 0, 0}
    36  
    37  	C.getloadavg(&avg[0], C.int(len(avg)))
    38  
    39  	self.One = float64(avg[0])
    40  	self.Five = float64(avg[1])
    41  	self.Fifteen = float64(avg[2])
    42  
    43  	return nil
    44  }
    45  
    46  // Get fetches memory data
    47  func (self *Mem) Get() error {
    48  	var vmstat C.vm_statistics_data_t
    49  
    50  	if err := sysctlbyname("hw.memsize", &self.Total); err != nil {
    51  		return err
    52  	}
    53  
    54  	if err := vmInfo(&vmstat); err != nil {
    55  		return err
    56  	}
    57  
    58  	kern := uint64(vmstat.inactive_count) << 12
    59  	self.Free = uint64(vmstat.free_count) << 12
    60  
    61  	self.Used = self.Total - self.Free
    62  	self.ActualFree = self.Free + kern
    63  	self.ActualUsed = self.Used - kern
    64  
    65  	return nil
    66  }
    67  
    68  type xswUsage struct {
    69  	Total, Avail, Used uint64
    70  }
    71  
    72  // Get fetches swap data
    73  func (self *Swap) Get() error {
    74  	swUsage := xswUsage{}
    75  
    76  	if err := sysctlbyname("vm.swapusage", &swUsage); err != nil {
    77  		return err
    78  	}
    79  
    80  	self.Total = swUsage.Total
    81  	self.Used = swUsage.Used
    82  	self.Free = swUsage.Avail
    83  
    84  	return nil
    85  }
    86  
    87  // Get fetches hugepages data
    88  func (self *HugeTLBPages) Get() error {
    89  	return ErrNotImplemented{runtime.GOOS}
    90  }
    91  
    92  // Get fetches CPU data
    93  func (self *Cpu) Get() error {
    94  	var count C.mach_msg_type_number_t = C.HOST_CPU_LOAD_INFO_COUNT
    95  	var cpuload C.host_cpu_load_info_data_t
    96  
    97  	status := C.host_statistics(C.host_t(C.mach_host_self()),
    98  		C.HOST_CPU_LOAD_INFO,
    99  		C.host_info_t(unsafe.Pointer(&cpuload)),
   100  		&count)
   101  
   102  	if status != C.KERN_SUCCESS {
   103  		return fmt.Errorf("host_statistics error=%d", status)
   104  	}
   105  
   106  	self.User = uint64(cpuload.cpu_ticks[C.CPU_STATE_USER])
   107  	self.Sys = uint64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM])
   108  	self.Idle = uint64(cpuload.cpu_ticks[C.CPU_STATE_IDLE])
   109  	self.Nice = uint64(cpuload.cpu_ticks[C.CPU_STATE_NICE])
   110  
   111  	return nil
   112  }
   113  
   114  // Get fetches CPU list data
   115  func (self *CpuList) Get() error {
   116  	var count C.mach_msg_type_number_t
   117  	var cpuload *C.processor_cpu_load_info_data_t
   118  	var ncpu C.natural_t
   119  
   120  	status := C.host_processor_info(C.host_t(C.mach_host_self()),
   121  		C.PROCESSOR_CPU_LOAD_INFO,
   122  		&ncpu,
   123  		(*C.processor_info_array_t)(unsafe.Pointer(&cpuload)),
   124  		&count)
   125  
   126  	if status != C.KERN_SUCCESS {
   127  		return fmt.Errorf("host_processor_info error=%d", status)
   128  	}
   129  
   130  	// jump through some cgo casting hoops and ensure we properly free
   131  	// the memory that cpuload points to
   132  	target := C.vm_map_t(C.mach_task_self_)
   133  	address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload)))
   134  	defer C.vm_deallocate(target, address, C.vm_size_t(ncpu))
   135  
   136  	// the body of struct processor_cpu_load_info
   137  	// aka processor_cpu_load_info_data_t
   138  	var cpuTicks [C.CPU_STATE_MAX]uint32
   139  
   140  	// copy the cpuload array to a []byte buffer
   141  	// where we can binary.Read the data
   142  	size := int(ncpu) * binary.Size(cpuTicks)
   143  	buf := C.GoBytes(unsafe.Pointer(cpuload), C.int(size))
   144  
   145  	bbuf := bytes.NewBuffer(buf)
   146  
   147  	self.List = make([]Cpu, 0, ncpu)
   148  
   149  	for i := 0; i < int(ncpu); i++ {
   150  		cpu := Cpu{}
   151  
   152  		err := binary.Read(bbuf, binary.LittleEndian, &cpuTicks)
   153  		if err != nil {
   154  			return err
   155  		}
   156  
   157  		cpu.User = uint64(cpuTicks[C.CPU_STATE_USER])
   158  		cpu.Sys = uint64(cpuTicks[C.CPU_STATE_SYSTEM])
   159  		cpu.Idle = uint64(cpuTicks[C.CPU_STATE_IDLE])
   160  		cpu.Nice = uint64(cpuTicks[C.CPU_STATE_NICE])
   161  
   162  		self.List = append(self.List, cpu)
   163  	}
   164  
   165  	return nil
   166  }
   167  
   168  // Get returns FD usage data
   169  func (self *FDUsage) Get() error {
   170  	return ErrNotImplemented{runtime.GOOS}
   171  }
   172  
   173  // Get returns filesystem data
   174  func (self *FileSystemList) Get() error {
   175  	num, err := syscall.Getfsstat(nil, C.MNT_NOWAIT)
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	buf := make([]syscall.Statfs_t, num)
   181  
   182  	_, err = syscall.Getfsstat(buf, C.MNT_NOWAIT)
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	fslist := make([]FileSystem, 0, num)
   188  
   189  	for i := 0; i < num; i++ {
   190  		fs := FileSystem{}
   191  		fs.DirName = byteListToString(buf[i].Mntonname[:])
   192  		fs.DevName = byteListToString(buf[i].Mntfromname[:])
   193  		fs.SysTypeName = byteListToString(buf[i].Fstypename[:])
   194  
   195  		fslist = append(fslist, fs)
   196  	}
   197  
   198  	self.List = fslist
   199  
   200  	return err
   201  }
   202  
   203  // Get returns a process list
   204  func (self *ProcList) Get() error {
   205  	n := C.proc_listpids(C.PROC_ALL_PIDS, 0, nil, 0)
   206  	if n <= 0 {
   207  		return syscall.EINVAL
   208  	}
   209  	buf := make([]byte, n)
   210  	n = C.proc_listpids(C.PROC_ALL_PIDS, 0, unsafe.Pointer(&buf[0]), n)
   211  	if n <= 0 {
   212  		return syscall.ENOMEM
   213  	}
   214  
   215  	var pid int32
   216  	num := int(n) / binary.Size(pid)
   217  	list := make([]int, 0, num)
   218  	bbuf := bytes.NewBuffer(buf)
   219  
   220  	for i := 0; i < num; i++ {
   221  		if err := binary.Read(bbuf, binary.LittleEndian, &pid); err != nil {
   222  			return err
   223  		}
   224  		if pid == 0 {
   225  			continue
   226  		}
   227  
   228  		list = append(list, int(pid))
   229  	}
   230  
   231  	self.List = list
   232  
   233  	return nil
   234  }
   235  
   236  // Get returns process state data
   237  func (self *ProcState) Get(pid int) error {
   238  	info := C.struct_proc_taskallinfo{}
   239  
   240  	if err := taskInfo(pid, &info); err != nil {
   241  		return err
   242  	}
   243  
   244  	self.Name = C.GoString(&info.pbsd.pbi_comm[0])
   245  
   246  	switch info.pbsd.pbi_status {
   247  	case C.SIDL:
   248  		self.State = RunStateIdle
   249  	case C.SRUN:
   250  		self.State = RunStateRun
   251  	case C.SSLEEP:
   252  		self.State = RunStateSleep
   253  	case C.SSTOP:
   254  		self.State = RunStateStop
   255  	case C.SZOMB:
   256  		self.State = RunStateZombie
   257  	default:
   258  		self.State = RunStateUnknown
   259  	}
   260  
   261  	self.Ppid = int(info.pbsd.pbi_ppid)
   262  
   263  	self.Pgid = int(info.pbsd.pbi_pgid)
   264  
   265  	self.Tty = int(info.pbsd.e_tdev)
   266  
   267  	self.Priority = int(info.ptinfo.pti_priority)
   268  
   269  	self.Nice = int(info.pbsd.pbi_nice)
   270  
   271  	// Get process username. Fallback to UID if username is not available.
   272  	uid := strconv.Itoa(int(info.pbsd.pbi_uid))
   273  	user, err := user.LookupId(uid)
   274  	if err == nil && user.Username != "" {
   275  		self.Username = user.Username
   276  	} else {
   277  		self.Username = uid
   278  	}
   279  
   280  	return nil
   281  }
   282  
   283  // Get returns process memory data
   284  func (self *ProcMem) Get(pid int) error {
   285  	info := C.struct_proc_taskallinfo{}
   286  
   287  	if err := taskInfo(pid, &info); err != nil {
   288  		return err
   289  	}
   290  
   291  	self.Size = uint64(info.ptinfo.pti_virtual_size)
   292  	self.Resident = uint64(info.ptinfo.pti_resident_size)
   293  	self.PageFaults = uint64(info.ptinfo.pti_faults)
   294  
   295  	return nil
   296  }
   297  
   298  // Get returns process time data
   299  func (self *ProcTime) Get(pid int) error {
   300  	info := C.struct_proc_taskallinfo{}
   301  
   302  	if err := taskInfo(pid, &info); err != nil {
   303  		return err
   304  	}
   305  
   306  	self.User =
   307  		uint64(info.ptinfo.pti_total_user) / uint64(time.Millisecond)
   308  
   309  	self.Sys =
   310  		uint64(info.ptinfo.pti_total_system) / uint64(time.Millisecond)
   311  
   312  	self.Total = self.User + self.Sys
   313  
   314  	self.StartTime = (uint64(info.pbsd.pbi_start_tvsec) * 1000) +
   315  		(uint64(info.pbsd.pbi_start_tvusec) / 1000)
   316  
   317  	return nil
   318  }
   319  
   320  // Get returns process arg data
   321  func (self *ProcArgs) Get(pid int) error {
   322  	var args []string
   323  
   324  	argv := func(arg string) {
   325  		args = append(args, arg)
   326  	}
   327  
   328  	err := kernProcargs(pid, nil, argv, nil)
   329  
   330  	self.List = args
   331  
   332  	return err
   333  }
   334  
   335  // Get returns process environment data
   336  func (self *ProcEnv) Get(pid int) error {
   337  	if self.Vars == nil {
   338  		self.Vars = map[string]string{}
   339  	}
   340  
   341  	env := func(k, v string) {
   342  		self.Vars[k] = v
   343  	}
   344  
   345  	return kernProcargs(pid, nil, nil, env)
   346  }
   347  
   348  // Get returns process exec data
   349  func (self *ProcExe) Get(pid int) error {
   350  	exe := func(arg string) {
   351  		self.Name = arg
   352  	}
   353  
   354  	return kernProcargs(pid, exe, nil, nil)
   355  }
   356  
   357  // Get returns process file usage
   358  func (self *ProcFDUsage) Get(pid int) error {
   359  	return ErrNotImplemented{runtime.GOOS}
   360  }
   361  
   362  // kernProcargs is a wrapper around sysctl KERN_PROCARGS2
   363  // callbacks params are optional,
   364  // up to the caller as to which pieces of data they want
   365  func kernProcargs(pid int,
   366  	exe func(string),
   367  	argv func(string),
   368  	env func(string, string)) error {
   369  
   370  	mib := []C.int{C.CTL_KERN, C.KERN_PROCARGS2, C.int(pid)}
   371  	argmax := uintptr(C.ARG_MAX)
   372  	buf := make([]byte, argmax)
   373  	err := sysctl(mib, &buf[0], &argmax, nil, 0)
   374  	if err != nil {
   375  		return nil
   376  	}
   377  
   378  	bbuf := bytes.NewBuffer(buf)
   379  	bbuf.Truncate(int(argmax))
   380  
   381  	var argc int32
   382  	binary.Read(bbuf, binary.LittleEndian, &argc)
   383  
   384  	path, err := bbuf.ReadBytes(0)
   385  	if err != nil {
   386  		return fmt.Errorf("Error reading the argv[0]: %v", err)
   387  	}
   388  	if exe != nil {
   389  		exe(string(chop(path)))
   390  	}
   391  
   392  	// skip trailing \0's
   393  	for {
   394  		c, err := bbuf.ReadByte()
   395  		if err != nil {
   396  			return fmt.Errorf("Error skipping nils: %v", err)
   397  		}
   398  		if c != 0 {
   399  			bbuf.UnreadByte()
   400  			break // start of argv[0]
   401  		}
   402  	}
   403  
   404  	for i := 0; i < int(argc); i++ {
   405  		arg, err := bbuf.ReadBytes(0)
   406  		if err == io.EOF {
   407  			break
   408  		}
   409  		if err != nil {
   410  			return fmt.Errorf("Error reading args: %v", err)
   411  		}
   412  		if argv != nil {
   413  			argv(string(chop(arg)))
   414  		}
   415  	}
   416  
   417  	if env == nil {
   418  		return nil
   419  	}
   420  
   421  	delim := []byte{61} // "="
   422  
   423  	for {
   424  		line, err := bbuf.ReadBytes(0)
   425  		if err == io.EOF || line[0] == 0 {
   426  			break
   427  		}
   428  		if err != nil {
   429  			return fmt.Errorf("Error reading args: %v", err)
   430  		}
   431  		pair := bytes.SplitN(chop(line), delim, 2)
   432  
   433  		if len(pair) != 2 {
   434  			return fmt.Errorf("Error reading process information for PID: %d", pid)
   435  		}
   436  
   437  		env(string(pair[0]), string(pair[1]))
   438  	}
   439  
   440  	return nil
   441  }
   442  
   443  // XXX copied from zsyscall_darwin_amd64.go
   444  func sysctl(mib []C.int, old *byte, oldlen *uintptr,
   445  	new *byte, newlen uintptr) (err error) {
   446  	var p0 unsafe.Pointer
   447  	p0 = unsafe.Pointer(&mib[0])
   448  	_, _, e1 := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(p0),
   449  		uintptr(len(mib)),
   450  		uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)),
   451  		uintptr(unsafe.Pointer(new)), uintptr(newlen))
   452  	if e1 != 0 {
   453  		err = e1
   454  	}
   455  	return
   456  }
   457  
   458  func vmInfo(vmstat *C.vm_statistics_data_t) error {
   459  	var count C.mach_msg_type_number_t = C.HOST_VM_INFO_COUNT
   460  
   461  	status := C.host_statistics(
   462  		C.host_t(C.mach_host_self()),
   463  		C.HOST_VM_INFO,
   464  		C.host_info_t(unsafe.Pointer(vmstat)),
   465  		&count)
   466  
   467  	if status != C.KERN_SUCCESS {
   468  		return fmt.Errorf("host_statistics=%d", status)
   469  	}
   470  
   471  	return nil
   472  }
   473  
   474  // generic Sysctl buffer unmarshalling
   475  func sysctlbyname(name string, data interface{}) (err error) {
   476  	switch v := data.(type) {
   477  	case *uint64:
   478  		res, err := unix.SysctlUint64(name)
   479  		if err != nil {
   480  			return err
   481  		}
   482  		*v = res
   483  		return nil
   484  	default:
   485  		val, err := syscall.Sysctl(name)
   486  		if err != nil {
   487  			return err
   488  		}
   489  
   490  		bbuf := bytes.NewBuffer([]byte(val))
   491  		return binary.Read(bbuf, binary.LittleEndian, data)
   492  	}
   493  }
   494  
   495  func taskInfo(pid int, info *C.struct_proc_taskallinfo) error {
   496  	size := C.int(unsafe.Sizeof(*info))
   497  	ptr := unsafe.Pointer(info)
   498  
   499  	n := C.proc_pidinfo(C.int(pid), C.PROC_PIDTASKALLINFO, 0, ptr, size)
   500  	if n != size {
   501  		return fmt.Errorf("Could not read process info for pid %d", pid)
   502  	}
   503  
   504  	return nil
   505  }