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 }