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