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 }