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  }