github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/toolkit/cpu/cpu.go (about)

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