Skip to content

Instantly share code, notes, and snippets.

@gekigek99
Last active March 3, 2023 18:07
Show Gist options
  • Save gekigek99/46f4683e3f9ed5411736dcded485c1f4 to your computer and use it in GitHub Desktop.
Save gekigek99/46f4683e3f9ed5411736dcded485c1f4 to your computer and use it in GitHub Desktop.
calculate the average cpu usage percent of the last second of a given pid
// This script calculates the average cpu usage percent of the last second (of a given pid, in this case the script itself).
// (*process.Process).CPUPercent() from "github.com/shirou/gopsutil/process" returns the average cpu usage percent since the process was started.
// If you call cpuPercent(p) every 10 seconds then the average cpu usage percent of the last 10 seconds is returned.
//
// cpuPercent first call returns the average cpu percent usage since the start of the process.
//
// Remember that if the process you are analyzing changes pid you have to update `pTracker` with the new pid (+new info) and remove the old pid.
// (not the case for this example though)
package main
import (
"fmt"
"os"
"time"
"github.com/shirou/gopsutil/process"
)
// pStats keeps track of a single process stats
type pStats struct {
cpuTotalLast float64
lifeTimeLast time.Duration
}
// pStatsByPid keeps track of multiple processes stats
//
// (pid is used as map key to access each single process stats)
type pStatsByPid map[int32]*pStats
// pTracker is the variable that stores all process stats in a pid map
var pTracker pStatsByPid = pStatsByPid{}
func main() {
// get current process
p, err := process.NewProcess(int32(os.Getpid()))
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
n := 0
go func() {
timer := time.NewTimer(3000 * time.Millisecond)
for {
n++
if n > 1000000 {
n = 0
}
select {
case <-timer.C:
time.Sleep(3000 * time.Millisecond)
timer.Reset(3000 * time.Millisecond)
default:
}
}
}()
t := time.NewTicker(time.Second)
for {
<-tick.C
percent1, err := cpuPercent(p)
if err != nil {
fmt.Println(err.Error())
continue
}
percent2, err := p.CPUPercent()
if err != nil {
fmt.Println(err.Error())
continue
}
fmt.Printf("cpu usage (%d): script = %f\t-\tgopsutil = %f\n", p.Pid, percent1, percent2)
}
}
// cpuPercent returns the average cpu percent usage since last call
//
// cpuPercent first call returns the average cpu percent usage since the start of the process.
func cpuPercent(p *process.Process) (float64, error) {
crt_time, err := p.CreateTime()
if err != nil {
return -1, err
}
lifeTimeNow := time.Since(time.Unix(0, crt_time*int64(time.Millisecond)))
cput, err := p.Times()
if err != nil {
return -1, err
}
cpuTotalNow := cput.Total()
// update tracked pid
cpuTotalLast, lifeTimeLast := pTracker.upd(p.Pid, cpuTotalNow, lifeTimeNow)
cpuPercent := 100 * (cpuTotalNow - cpuTotalLast) / (lifeTimeNow - lifeTimeLast).Seconds()
return cpuPercent, nil
}
// upd specified pid and returns last cpu total and last life time.
//
// pTracker.upd(p.Pid, ..., ...) should be called before accessing pTracker[p.Pid] (if not it will be nil).
func (pTracker *pStatsByPid) upd(pid int32, cpuTotalNow float64, lifeTimeNow time.Duration) (float64, time.Duration) {
// if pid is not tracked, return 0, 0
cpuTotalLast := 0.0
lifeTimeLast := time.Duration(0)
// if pid is tracked, return last values
_, ok := (*pTracker)[pid]
if ok {
cpuTotalLast = (*pTracker)[pid].cpuTotalLast
lifeTimeLast = (*pTracker)[pid].lifeTimeLast
}
// update process stats in tracker
(*pTracker)[pid] = &pStats{
cpuTotalLast: cpuTotalNow,
lifeTimeLast: lifeTimeNow,
}
return cpuTotalLast, lifeTimeLast
}
// rem removes pid from pTracker.
//
// it's safe to use even if pTracker does not contain pid.
func (pTracker *pStatsByPid) rem(pid int32) {
delete(*pTracker, pid)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment