cpu_linux.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package internal
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "sync"
  7. "time"
  8. "github.com/wuntsong-org/go-zero-plus/core/iox"
  9. "github.com/wuntsong-org/go-zero-plus/core/logx"
  10. )
  11. const (
  12. cpuTicks = 100
  13. cpuFields = 8
  14. )
  15. var (
  16. preSystem uint64
  17. preTotal uint64
  18. quota float64
  19. cores uint64
  20. initOnce sync.Once
  21. )
  22. // if /proc not present, ignore the cpu calculation, like wsl linux
  23. func initialize() {
  24. cpus, err := cpuSets()
  25. if err != nil {
  26. logx.Error(err)
  27. return
  28. }
  29. cores = uint64(len(cpus))
  30. quota = float64(len(cpus))
  31. cq, err := cpuQuota()
  32. if err == nil {
  33. if cq != -1 {
  34. period, err := cpuPeriod()
  35. if err != nil {
  36. logx.Error(err)
  37. return
  38. }
  39. limit := float64(cq) / float64(period)
  40. if limit < quota {
  41. quota = limit
  42. }
  43. }
  44. }
  45. preSystem, err = systemCpuUsage()
  46. if err != nil {
  47. logx.Error(err)
  48. return
  49. }
  50. preTotal, err = totalCpuUsage()
  51. if err != nil {
  52. logx.Error(err)
  53. return
  54. }
  55. }
  56. // RefreshCpu refreshes cpu usage and returns.
  57. func RefreshCpu() uint64 {
  58. initOnce.Do(initialize)
  59. total, err := totalCpuUsage()
  60. if err != nil {
  61. return 0
  62. }
  63. system, err := systemCpuUsage()
  64. if err != nil {
  65. return 0
  66. }
  67. var usage uint64
  68. cpuDelta := total - preTotal
  69. systemDelta := system - preSystem
  70. if cpuDelta > 0 && systemDelta > 0 {
  71. usage = uint64(float64(cpuDelta*cores*1e3) / (float64(systemDelta) * quota))
  72. }
  73. preSystem = system
  74. preTotal = total
  75. return usage
  76. }
  77. func cpuQuota() (int64, error) {
  78. cg, err := currentCgroup()
  79. if err != nil {
  80. return 0, err
  81. }
  82. return cg.cpuQuotaUs()
  83. }
  84. func cpuPeriod() (uint64, error) {
  85. cg, err := currentCgroup()
  86. if err != nil {
  87. return 0, err
  88. }
  89. return cg.cpuPeriodUs()
  90. }
  91. func cpuSets() ([]uint64, error) {
  92. cg, err := currentCgroup()
  93. if err != nil {
  94. return nil, err
  95. }
  96. return cg.cpus()
  97. }
  98. func systemCpuUsage() (uint64, error) {
  99. lines, err := iox.ReadTextLines("/proc/stat", iox.WithoutBlank())
  100. if err != nil {
  101. return 0, err
  102. }
  103. for _, line := range lines {
  104. fields := strings.Fields(line)
  105. if fields[0] == "cpu" {
  106. if len(fields) < cpuFields {
  107. return 0, fmt.Errorf("bad format of cpu stats")
  108. }
  109. var totalClockTicks uint64
  110. for _, i := range fields[1:cpuFields] {
  111. v, err := parseUint(i)
  112. if err != nil {
  113. return 0, err
  114. }
  115. totalClockTicks += v
  116. }
  117. return (totalClockTicks * uint64(time.Second)) / cpuTicks, nil
  118. }
  119. }
  120. return 0, errors.New("bad stats format")
  121. }
  122. func totalCpuUsage() (usage uint64, err error) {
  123. var cg cgroup
  124. if cg, err = currentCgroup(); err != nil {
  125. return
  126. }
  127. return cg.usageAllCpus()
  128. }