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 }