sys_linux.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. //go:build linux
  2. // +build linux
  3. package sys
  4. import (
  5. "bufio"
  6. "bytes"
  7. "fmt"
  8. "io"
  9. "os"
  10. "strconv"
  11. "strings"
  12. "sync"
  13. )
  14. func getLinesNum(filename string) (int, error) {
  15. file, err := os.Open(filename)
  16. if err != nil {
  17. return 0, err
  18. }
  19. defer file.Close()
  20. sum := 0
  21. buf := make([]byte, 8192)
  22. for {
  23. n, err := file.Read(buf)
  24. var buffPosition int
  25. for {
  26. i := bytes.IndexByte(buf[buffPosition:n], '\n')
  27. if i < 0 {
  28. break
  29. }
  30. buffPosition += i + 1
  31. sum++
  32. }
  33. if err == io.EOF {
  34. break
  35. } else if err != nil {
  36. return 0, err
  37. }
  38. }
  39. return sum, nil
  40. }
  41. // GetTCPCount returns the number of active TCP connections by reading
  42. // /proc/net/tcp and /proc/net/tcp6 when available.
  43. func GetTCPCount() (int, error) {
  44. root := HostProc()
  45. tcp4, err := safeGetLinesNum(fmt.Sprintf("%v/net/tcp", root))
  46. if err != nil {
  47. return 0, err
  48. }
  49. tcp6, err := safeGetLinesNum(fmt.Sprintf("%v/net/tcp6", root))
  50. if err != nil {
  51. return 0, err
  52. }
  53. return tcp4 + tcp6, nil
  54. }
  55. func GetUDPCount() (int, error) {
  56. root := HostProc()
  57. udp4, err := safeGetLinesNum(fmt.Sprintf("%v/net/udp", root))
  58. if err != nil {
  59. return 0, err
  60. }
  61. udp6, err := safeGetLinesNum(fmt.Sprintf("%v/net/udp6", root))
  62. if err != nil {
  63. return 0, err
  64. }
  65. return udp4 + udp6, nil
  66. }
  67. // safeGetLinesNum returns 0 if the file does not exist, otherwise forwards
  68. // to getLinesNum to count the number of lines.
  69. func safeGetLinesNum(path string) (int, error) {
  70. if _, err := os.Stat(path); os.IsNotExist(err) {
  71. return 0, nil
  72. } else if err != nil {
  73. return 0, err
  74. }
  75. return getLinesNum(path)
  76. }
  77. // --- CPU Utilization (Linux native) ---
  78. var (
  79. cpuMu sync.Mutex
  80. lastTotal uint64
  81. lastIdleAll uint64
  82. hasLast bool
  83. )
  84. // CPUPercentRaw returns instantaneous total CPU utilization by reading /proc/stat.
  85. // First call initializes and returns 0; subsequent calls return busy/total * 100.
  86. func CPUPercentRaw() (float64, error) {
  87. f, err := os.Open("/proc/stat")
  88. if err != nil {
  89. return 0, err
  90. }
  91. defer f.Close()
  92. rd := bufio.NewReader(f)
  93. line, err := rd.ReadString('\n')
  94. if err != nil && err != io.EOF {
  95. return 0, err
  96. }
  97. // Expect line like: cpu user nice system idle iowait irq softirq steal guest guest_nice
  98. fields := strings.Fields(line)
  99. if len(fields) < 5 || fields[0] != "cpu" {
  100. return 0, fmt.Errorf("unexpected /proc/stat format")
  101. }
  102. var nums []uint64
  103. for i := 1; i < len(fields); i++ {
  104. v, err := strconv.ParseUint(fields[i], 10, 64)
  105. if err != nil {
  106. break
  107. }
  108. nums = append(nums, v)
  109. }
  110. if len(nums) < 4 { // need at least user,nice,system,idle
  111. return 0, fmt.Errorf("insufficient cpu fields")
  112. }
  113. // Conform with standard Linux CPU accounting
  114. var user, nice, system, idle, iowait, irq, softirq, steal uint64
  115. user = nums[0]
  116. if len(nums) > 1 {
  117. nice = nums[1]
  118. }
  119. if len(nums) > 2 {
  120. system = nums[2]
  121. }
  122. if len(nums) > 3 {
  123. idle = nums[3]
  124. }
  125. if len(nums) > 4 {
  126. iowait = nums[4]
  127. }
  128. if len(nums) > 5 {
  129. irq = nums[5]
  130. }
  131. if len(nums) > 6 {
  132. softirq = nums[6]
  133. }
  134. if len(nums) > 7 {
  135. steal = nums[7]
  136. }
  137. idleAll := idle + iowait
  138. nonIdle := user + nice + system + irq + softirq + steal
  139. total := idleAll + nonIdle
  140. cpuMu.Lock()
  141. defer cpuMu.Unlock()
  142. if !hasLast {
  143. lastTotal = total
  144. lastIdleAll = idleAll
  145. hasLast = true
  146. return 0, nil
  147. }
  148. totald := total - lastTotal
  149. idled := idleAll - lastIdleAll
  150. lastTotal = total
  151. lastIdleAll = idleAll
  152. if totald == 0 {
  153. return 0, nil
  154. }
  155. busy := totald - idled
  156. pct := float64(busy) / float64(totald) * 100.0
  157. if pct > 100 {
  158. pct = 100
  159. }
  160. return pct, nil
  161. }