github.com/sercand/please@v13.4.0+incompatible/src/follow/resources.go (about)

     1  // +build !bootstrap
     2  
     3  package follow
     4  
     5  import (
     6  	"time"
     7  
     8  	"github.com/shirou/gopsutil/cpu"
     9  	"github.com/shirou/gopsutil/mem"
    10  
    11  	"github.com/thought-machine/please/src/core"
    12  )
    13  
    14  // resourceUpdateFrequency is the frequency that we re-check CPU usage etc at.
    15  // We don't want to sample too often; it will actually make CPU usage less accurate, and
    16  // we don't want to spend all our time looking at it anyway.
    17  var resourceUpdateFrequency = 500 * time.Millisecond
    18  
    19  // UpdateResources continuously updates the resources that we store on the BuildState object.
    20  // It should probably be run in a goroutine since it never returns.
    21  func UpdateResources(state *core.BuildState) {
    22  	lastTime := time.Now()
    23  	// Assume this doesn't change through the process lifetime.
    24  	count, _ := cpu.Counts(true)
    25  	state.Stats.CPU.Count = count
    26  	// Top out max CPU; sometimes we get our maths slightly wrong, probably because of
    27  	// mild uncertainty in times.
    28  	maxCPU := float64(100 * count)
    29  	clamp := func(f float64) float64 {
    30  		if f >= maxCPU {
    31  			return maxCPU
    32  		} else if f <= 0.0 {
    33  			return 0.0
    34  		}
    35  		return f
    36  	}
    37  	// CPU is a bit of a fiddle since the kernel only gives us totals,
    38  	// so we have to sample how busy we think it's been.
    39  	lastTotal, lastIO := getCPUTimes()
    40  	for timeNow := range time.NewTicker(resourceUpdateFrequency).C {
    41  		if thisTotal, thisIO := getCPUTimes(); thisTotal > 0.0 {
    42  			elapsed := timeNow.Sub(lastTime).Seconds()
    43  			state.Stats.CPU.Used = clamp(100.0 * (thisTotal - lastTotal) / elapsed)
    44  			state.Stats.CPU.IOWait = clamp(100.0 * (thisIO - lastIO) / elapsed)
    45  			lastTotal, lastIO = thisTotal, thisIO
    46  		}
    47  		// Thank goodness memory is a simpler beast.
    48  		if vm, err := mem.VirtualMemory(); err != nil {
    49  			log.Error("Error getting memory usage: %s", err)
    50  		} else {
    51  			state.Stats.Memory.Total = vm.Total
    52  			state.Stats.Memory.Used = vm.Used
    53  			state.Stats.Memory.UsedPercent = vm.UsedPercent
    54  		}
    55  		lastTime = timeNow
    56  	}
    57  }
    58  
    59  func getCPUTimes() (float64, float64) {
    60  	ts, err := cpu.Times(false) // not per CPU
    61  	if err != nil || len(ts) == 0 {
    62  		log.Error("Error getting CPU info: %s", err)
    63  		return 0.0, 0.0
    64  	}
    65  	t := ts[0]
    66  	return t.Total() - t.Idle - t.Iowait, t.Iowait
    67  }