Files
go-zero/core/stat/internal/cpu_linux.go

152 lines
2.4 KiB
Go
Raw Normal View History

2020-07-26 17:09:05 +08:00
package internal
import (
"errors"
"fmt"
"strings"
"sync"
2020-07-26 17:09:05 +08:00
"time"
"github.com/zeromicro/go-zero/core/iox"
"github.com/zeromicro/go-zero/core/logx"
2020-07-26 17:09:05 +08:00
)
const (
cpuTicks = 100
cpuFields = 8
cpuMax = 1000
2024-01-17 23:35:42 +08:00
statFile = "/proc/stat"
2020-07-26 17:09:05 +08:00
)
var (
preSystem uint64
preTotal uint64
2024-01-17 23:35:42 +08:00
limit float64
2020-07-26 17:09:05 +08:00
cores uint64
noCgroup bool
2022-08-25 23:37:32 +08:00
initOnce sync.Once
2020-07-26 17:09:05 +08:00
)
2021-05-10 00:09:00 +08:00
// if /proc not present, ignore the cpu calculation, like wsl linux
func initialize() {
2024-01-17 23:35:42 +08:00
cpus, err := effectiveCpus()
2020-08-27 13:22:44 +08:00
if err != nil {
noCgroup = true
2020-08-27 13:22:44 +08:00
logx.Error(err)
return
}
2020-07-26 17:09:05 +08:00
2024-01-17 23:35:42 +08:00
cores = uint64(cpus)
limit = float64(cpus)
quota, err := cpuQuota()
if err == nil && quota > 0 {
if quota < limit {
limit = quota
2020-07-26 17:09:05 +08:00
}
}
preSystem, err = systemCpuUsage()
2020-08-27 13:22:44 +08:00
if err != nil {
noCgroup = true
2020-08-27 13:22:44 +08:00
logx.Error(err)
return
}
2020-07-26 17:09:05 +08:00
2024-01-17 23:35:42 +08:00
preTotal, err = cpuUsage()
2020-08-27 13:22:44 +08:00
if err != nil {
noCgroup = true
2020-08-27 13:22:44 +08:00
logx.Error(err)
return
}
2020-07-26 17:09:05 +08:00
}
2021-03-02 00:04:12 +08:00
// RefreshCpu refreshes cpu usage and returns.
2020-07-26 17:09:05 +08:00
func RefreshCpu() uint64 {
2022-08-25 23:37:32 +08:00
initOnce.Do(initialize)
if noCgroup {
return 0
}
2024-01-17 23:35:42 +08:00
total, err := cpuUsage()
2020-07-26 17:09:05 +08:00
if err != nil {
return 0
}
2022-08-25 23:37:32 +08:00
2020-07-26 17:09:05 +08:00
system, err := systemCpuUsage()
if err != nil {
return 0
}
var usage uint64
cpuDelta := total - preTotal
systemDelta := system - preSystem
if cpuDelta > 0 && systemDelta > 0 {
2024-01-17 23:35:42 +08:00
usage = uint64(float64(cpuDelta*cores*cpuMax) / (float64(systemDelta) * limit))
if usage > cpuMax {
usage = cpuMax
}
2020-07-26 17:09:05 +08:00
}
preSystem = system
preTotal = total
return usage
}
2024-01-17 23:35:42 +08:00
func cpuQuota() (float64, error) {
2020-07-26 17:09:05 +08:00
cg, err := currentCgroup()
if err != nil {
return 0, err
}
2024-01-17 23:35:42 +08:00
return cg.cpuQuota()
2020-07-26 17:09:05 +08:00
}
2024-01-17 23:35:42 +08:00
func cpuUsage() (uint64, error) {
2020-07-26 17:09:05 +08:00
cg, err := currentCgroup()
if err != nil {
return 0, err
}
2024-01-17 23:35:42 +08:00
return cg.cpuUsage()
2020-07-26 17:09:05 +08:00
}
2024-01-17 23:35:42 +08:00
func effectiveCpus() (int, error) {
2020-07-26 17:09:05 +08:00
cg, err := currentCgroup()
if err != nil {
2024-01-17 23:35:42 +08:00
return 0, err
2020-07-26 17:09:05 +08:00
}
2024-01-17 23:35:42 +08:00
return cg.effectiveCpus()
2020-07-26 17:09:05 +08:00
}
func systemCpuUsage() (uint64, error) {
2024-01-17 23:35:42 +08:00
lines, err := iox.ReadTextLines(statFile, iox.WithoutBlank())
2020-07-26 17:09:05 +08:00
if err != nil {
return 0, err
}
for _, line := range lines {
fields := strings.Fields(line)
if fields[0] == "cpu" {
if len(fields) < cpuFields {
return 0, fmt.Errorf("bad format of cpu stats")
}
var totalClockTicks uint64
for _, i := range fields[1:cpuFields] {
v, err := parseUint(i)
if err != nil {
return 0, err
}
totalClockTicks += v
}
return (totalClockTicks * uint64(time.Second)) / cpuTicks, nil
}
}
return 0, errors.New("bad stats format")
}