diff --git a/util/sys/sys_darwin.go b/util/sys/sys_darwin.go index b62b5f7b..21822821 100644 --- a/util/sys/sys_darwin.go +++ b/util/sys/sys_darwin.go @@ -32,8 +32,9 @@ func GetUDPCount() (int, error) { // --- CPU Utilization (macOS native) --- -// sysctl kern.cp_time returns an array of 5 longs: user, nice, sys, idle, intr. -// We compute utilization deltas without cgo. +// sysctl kern.cp_time returns 5 longs in the BSD CPUSTATES order: +// user, nice, sys, intr, idle (CP_INTR=3, CP_IDLE=4). gopsutil reads the +// same layout in cpu_darwin_nocgo.go. var ( cpuMu sync.Mutex lastTotals [5]uint64 @@ -60,13 +61,6 @@ func CPUPercentRaw() (float64, error) { return 0, fmt.Errorf("unexpected kern.cp_time size: %d", len(raw)) } - // user, nice, sys, idle, intr - user := out[0] - nice := out[1] - sysv := out[2] - idle := out[3] - intr := out[4] - cpuMu.Lock() defer cpuMu.Unlock() @@ -76,19 +70,19 @@ func CPUPercentRaw() (float64, error) { return 0, nil } - dUser := user - lastTotals[0] - dNice := nice - lastTotals[1] - dSys := sysv - lastTotals[2] - dIdle := idle - lastTotals[3] - dIntr := intr - lastTotals[4] - + var deltas [5]uint64 + var totald uint64 + for i := range 5 { + deltas[i] = out[i] - lastTotals[i] + totald += deltas[i] + } lastTotals = out - totald := dUser + dNice + dSys + dIdle + dIntr if totald == 0 { return 0, nil } - busy := totald - dIdle + idleDelta := deltas[4] + busy := totald - idleDelta pct := float64(busy) / float64(totald) * 100.0 if pct > 100 { pct = 100 diff --git a/util/sys/sys_linux.go b/util/sys/sys_linux.go index 00d02a59..554f9c98 100644 --- a/util/sys/sys_linux.go +++ b/util/sys/sys_linux.go @@ -4,7 +4,6 @@ package sys import ( "bufio" - "bytes" "fmt" "io" "os" @@ -16,80 +15,63 @@ import ( var SIGUSR1 = syscall.SIGUSR1 -func getLinesNum(filename string) (int, error) { - file, err := os.Open(filename) +// countConnections returns the number of entries in a /proc/net/{tcp,udp}[6] +// file. Returns 0 if the file is absent (e.g. /proc/net/tcp6 when IPv6 is +// disabled) and excludes the column header line. +func countConnections(path string) (int, error) { + f, err := os.Open(path) + if os.IsNotExist(err) { + return 0, nil + } if err != nil { return 0, err } - defer file.Close() + defer f.Close() - sum := 0 - buf := make([]byte, 8192) - for { - n, err := file.Read(buf) - - var buffPosition int - for { - i := bytes.IndexByte(buf[buffPosition:n], '\n') - if i < 0 { - break - } - buffPosition += i + 1 - sum++ - } - - if err == io.EOF { - break - } else if err != nil { - return 0, err - } + sc := bufio.NewScanner(f) + n := 0 + for sc.Scan() { + n++ } - return sum, nil + if err := sc.Err(); err != nil { + return 0, err + } + if n > 0 { + n-- // first line is the column header + } + return n, nil } // GetTCPCount returns the number of active TCP connections by reading // /proc/net/tcp and /proc/net/tcp6 when available. func GetTCPCount() (int, error) { root := HostProc() - - tcp4, err := safeGetLinesNum(fmt.Sprintf("%v/net/tcp", root)) + tcp4, err := countConnections(root + "/net/tcp") if err != nil { return 0, err } - tcp6, err := safeGetLinesNum(fmt.Sprintf("%v/net/tcp6", root)) + tcp6, err := countConnections(root + "/net/tcp6") if err != nil { return 0, err } - return tcp4 + tcp6, nil } +// GetUDPCount returns the number of active UDP connections by reading +// /proc/net/udp and /proc/net/udp6 when available. func GetUDPCount() (int, error) { root := HostProc() - - udp4, err := safeGetLinesNum(fmt.Sprintf("%v/net/udp", root)) + udp4, err := countConnections(root + "/net/udp") if err != nil { return 0, err } - udp6, err := safeGetLinesNum(fmt.Sprintf("%v/net/udp6", root)) + udp6, err := countConnections(root + "/net/udp6") if err != nil { return 0, err } - return udp4 + udp6, nil } -// safeGetLinesNum returns 0 if the file does not exist, otherwise forwards -// to getLinesNum to count the number of lines. -func safeGetLinesNum(path string) (int, error) { - if _, err := os.Stat(path); os.IsNotExist(err) { - return 0, nil - } else if err != nil { - return 0, err - } - return getLinesNum(path) -} - // --- CPU Utilization (Linux native) --- var ( @@ -99,10 +81,11 @@ var ( hasLast bool ) -// CPUPercentRaw returns instantaneous total CPU utilization by reading /proc/stat. -// First call initializes and returns 0; subsequent calls return busy/total * 100. +// CPUPercentRaw returns instantaneous total CPU utilization by reading +// /proc/stat. First call initializes and returns 0; subsequent calls return +// busy/total * 100. Uses HostProc so HOST_PROC overrides (containers) apply. func CPUPercentRaw() (float64, error) { - f, err := os.Open("/proc/stat") + f, err := os.Open(HostProc("stat")) if err != nil { return 0, err } @@ -113,13 +96,13 @@ func CPUPercentRaw() (float64, error) { if err != nil && err != io.EOF { return 0, err } - // Expect line like: cpu user nice system idle iowait irq softirq steal guest guest_nice + // Expect: cpu user nice system idle iowait irq softirq steal guest guest_nice fields := strings.Fields(line) if len(fields) < 5 || fields[0] != "cpu" { return 0, fmt.Errorf("unexpected /proc/stat format") } - var nums []uint64 + nums := make([]uint64, 0, len(fields)-1) for i := 1; i < len(fields); i++ { v, err := strconv.ParseUint(fields[i], 10, 64) if err != nil { @@ -127,34 +110,15 @@ func CPUPercentRaw() (float64, error) { } nums = append(nums, v) } - if len(nums) < 4 { // need at least user,nice,system,idle + if len(nums) < 4 { return 0, fmt.Errorf("insufficient cpu fields") } + for len(nums) < 8 { + nums = append(nums, 0) + } - // Conform with standard Linux CPU accounting - var user, nice, system, idle, iowait, irq, softirq, steal uint64 - user = nums[0] - if len(nums) > 1 { - nice = nums[1] - } - if len(nums) > 2 { - system = nums[2] - } - if len(nums) > 3 { - idle = nums[3] - } - if len(nums) > 4 { - iowait = nums[4] - } - if len(nums) > 5 { - irq = nums[5] - } - if len(nums) > 6 { - softirq = nums[6] - } - if len(nums) > 7 { - steal = nums[7] - } + user, nice, system, idle := nums[0], nums[1], nums[2], nums[3] + iowait, irq, softirq, steal := nums[4], nums[5], nums[6], nums[7] idleAll := idle + iowait nonIdle := user + nice + system + irq + softirq + steal