github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/cpu/cpu.go (about)

     1  package cpu
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"github.com/isyscore/isc-gobase/system/common"
     8  	"math"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  )
    14  
    15  // TimesStat contains the amounts of time the CPU has spent performing different
    16  // kinds of work. Time units are in seconds. It is based on linux /proc/stat file.
    17  type TimesStat struct {
    18  	CPU       string  `json:"cpu"`
    19  	User      float64 `json:"user"`
    20  	System    float64 `json:"system"`
    21  	Idle      float64 `json:"idle"`
    22  	Nice      float64 `json:"nice"`
    23  	Iowait    float64 `json:"iowait"`
    24  	Irq       float64 `json:"irq"`
    25  	Softirq   float64 `json:"softirq"`
    26  	Steal     float64 `json:"steal"`
    27  	Guest     float64 `json:"guest"`
    28  	GuestNice float64 `json:"guestNice"`
    29  }
    30  
    31  type InfoStat struct {
    32  	CPU        int32    `json:"cpu"`
    33  	VendorID   string   `json:"vendorId"`
    34  	Family     string   `json:"family"`
    35  	Model      string   `json:"model"`
    36  	Stepping   int32    `json:"stepping"`
    37  	PhysicalID string   `json:"physicalId"`
    38  	CoreID     string   `json:"coreId"`
    39  	Cores      int32    `json:"cores"`
    40  	ModelName  string   `json:"modelName"`
    41  	Mhz        float64  `json:"mhz"`
    42  	CacheSize  int32    `json:"cacheSize"`
    43  	Flags      []string `json:"flags"`
    44  	Microcode  string   `json:"microcode"`
    45  }
    46  
    47  type lastPercent struct {
    48  	sync.Mutex
    49  	lastCPUTimes    []TimesStat
    50  	lastPerCPUTimes []TimesStat
    51  }
    52  
    53  var lastCPUPercent lastPercent
    54  var invoke common.Invoker = common.Invoke{}
    55  
    56  func init() {
    57  	lastCPUPercent.Lock()
    58  	lastCPUPercent.lastCPUTimes, _ = Times(false)
    59  	lastCPUPercent.lastPerCPUTimes, _ = Times(true)
    60  	lastCPUPercent.Unlock()
    61  }
    62  
    63  // Counts returns the number of physical or logical cores in the system
    64  func Counts(logical bool) (int, error) {
    65  	return CountsWithContext(context.Background(), logical)
    66  }
    67  
    68  func (c TimesStat) String() string {
    69  	v := []string{
    70  		`"cpu":"` + c.CPU + `"`,
    71  		`"user":` + strconv.FormatFloat(c.User, 'f', 1, 64),
    72  		`"system":` + strconv.FormatFloat(c.System, 'f', 1, 64),
    73  		`"idle":` + strconv.FormatFloat(c.Idle, 'f', 1, 64),
    74  		`"nice":` + strconv.FormatFloat(c.Nice, 'f', 1, 64),
    75  		`"iowait":` + strconv.FormatFloat(c.Iowait, 'f', 1, 64),
    76  		`"irq":` + strconv.FormatFloat(c.Irq, 'f', 1, 64),
    77  		`"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64),
    78  		`"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64),
    79  		`"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64),
    80  		`"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64),
    81  	}
    82  
    83  	return `{` + strings.Join(v, ",") + `}`
    84  }
    85  
    86  // Total returns the total number of seconds in a CPUTimesStat
    87  func (c TimesStat) Total() float64 {
    88  	total := c.User + c.System + c.Nice + c.Iowait + c.Irq + c.Softirq +
    89  		c.Steal + c.Idle
    90  	return total
    91  }
    92  
    93  func (c InfoStat) String() string {
    94  	s, _ := json.Marshal(c)
    95  	return string(s)
    96  }
    97  
    98  func getAllBusy(t TimesStat) (float64, float64) {
    99  	busy := t.User + t.System + t.Nice + t.Iowait + t.Irq +
   100  		t.Softirq + t.Steal
   101  	return busy + t.Idle, busy
   102  }
   103  
   104  func calculateBusy(t1, t2 TimesStat) float64 {
   105  	t1All, t1Busy := getAllBusy(t1)
   106  	t2All, t2Busy := getAllBusy(t2)
   107  
   108  	if t2Busy <= t1Busy {
   109  		return 0
   110  	}
   111  	if t2All <= t1All {
   112  		return 100
   113  	}
   114  	return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100))
   115  }
   116  
   117  func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) {
   118  	// Make sure the CPU measurements have the same length.
   119  	if len(t1) != len(t2) {
   120  		return nil, fmt.Errorf(
   121  			"received two CPU counts: %d != %d",
   122  			len(t1), len(t2),
   123  		)
   124  	}
   125  
   126  	ret := make([]float64, len(t1))
   127  	for i, t := range t2 {
   128  		ret[i] = calculateBusy(t1[i], t)
   129  	}
   130  	return ret, nil
   131  }
   132  
   133  // Percent calculates the percentage of cpu used either per CPU or combined.
   134  // If an interval of 0 is given it will compare the current cpu times against the last call.
   135  // Returns one value per cpu, or a single value if percpu is set to false.
   136  func Percent(interval time.Duration, percpu bool) ([]float64, error) {
   137  	return PercentWithContext(context.Background(), interval, percpu)
   138  }
   139  
   140  func PercentWithContext(ctx context.Context, interval time.Duration, percpu bool) ([]float64, error) {
   141  	if interval <= 0 {
   142  		return percentUsedFromLastCall(percpu)
   143  	}
   144  
   145  	// Get CPU usage at the start of the interval.
   146  	cpuTimes1, err := Times(percpu)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	if err := common.Sleep(ctx, interval); err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	// And at the end of the interval.
   156  	cpuTimes2, err := Times(percpu)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	return calculateAllBusy(cpuTimes1, cpuTimes2)
   162  }
   163  
   164  func percentUsedFromLastCall(percpu bool) ([]float64, error) {
   165  	cpuTimes, err := Times(percpu)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  	lastCPUPercent.Lock()
   170  	defer lastCPUPercent.Unlock()
   171  	var lastTimes []TimesStat
   172  	if percpu {
   173  		lastTimes = lastCPUPercent.lastPerCPUTimes
   174  		lastCPUPercent.lastPerCPUTimes = cpuTimes
   175  	} else {
   176  		lastTimes = lastCPUPercent.lastCPUTimes
   177  		lastCPUPercent.lastCPUTimes = cpuTimes
   178  	}
   179  
   180  	if lastTimes == nil {
   181  		return nil, fmt.Errorf("error getting times for cpu percent. lastTimes was nil")
   182  	}
   183  	return calculateAllBusy(lastTimes, cpuTimes)
   184  }