Extract linux specifc code from psutils for reading CPU times

This commit is contained in:
Ingo Oppermann
2024-07-25 09:30:11 +02:00
parent 46950372be
commit 6101585fd2
4 changed files with 122 additions and 21 deletions

2
go.mod
View File

@@ -40,6 +40,7 @@ require (
github.com/stretchr/testify v1.9.0
github.com/swaggo/echo-swagger v1.4.1
github.com/swaggo/swag v1.16.3
github.com/tklauser/go-sysconf v0.3.14
github.com/vektah/gqlparser/v2 v2.5.16
github.com/xeipuuv/gojsonschema v1.2.0
go.etcd.io/bbolt v1.3.10
@@ -104,7 +105,6 @@ require (
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sosodev/duration v1.3.1 // indirect
github.com/swaggo/files/v2 v2.0.1 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/urfave/cli/v2 v2.27.2 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect

View File

@@ -142,26 +142,6 @@ func (p *process) Resume() error {
return p.proc.Resume()
}
func (p *process) cpuTimes() (*cpuTimesStat, error) {
times, err := p.proc.Times()
if err != nil {
return nil, err
}
s := &cpuTimesStat{
total: cpuTotal(times),
system: times.System,
user: times.User,
}
s.other = s.total - s.system - s.user
if s.other < 0.0001 {
s.other = 0
}
return s, nil
}
func (p *process) CPUPercent() (*CPUInfoStat, error) {
var diff float64

97
psutil/process_linux.go Normal file
View File

@@ -0,0 +1,97 @@
//go:build linux
// +build linux
package psutil
import (
"bytes"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/tklauser/go-sysconf"
)
// Extracted from "github.com/shirou/gopsutil/v3/process/process_linux.go"
// We only need the CPU times. p.proc.Times() calls a function that is
// doing more than we actually need.
var clockTicks = 100 // default value
func init() {
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
// ignore errors
if err == nil {
clockTicks = int(clkTck)
}
}
func (p *process) cpuTimes() (*cpuTimesStat, error) {
value := os.Getenv("HOST_PROC")
if value == "" {
value = "/proc"
}
path := filepath.Join(value, strconv.FormatInt(int64(p.pid), 10), "stat")
contents, err := os.ReadFile(path)
if err != nil {
return nil, err
}
// Indexing from one, as described in `man proc` about the file /proc/[pid]/stat
fields := splitProcStat(contents)
utime, err := strconv.ParseFloat(fields[14], 64)
if err != nil {
return nil, err
}
stime, err := strconv.ParseFloat(fields[15], 64)
if err != nil {
return nil, err
}
// There is no such thing as iotime in stat file. As an approximation, we
// will use delayacct_blkio_ticks (aggregated block I/O delays, as per Linux
// docs). Note: I am assuming at least Linux 2.6.18
var iotime float64
if len(fields) > 42 {
iotime, err = strconv.ParseFloat(fields[42], 64)
if err != nil {
iotime = 0 // Ancient linux version, most likely
}
} else {
iotime = 0 // e.g. SmartOS containers
}
userTime := utime / float64(clockTicks)
systemTime := stime / float64(clockTicks)
iowaitTime := iotime / float64(clockTicks)
s := &cpuTimesStat{
total: userTime + systemTime + iowaitTime,
system: systemTime,
user: userTime,
other: iowaitTime,
}
if s.other < 0.0001 {
s.other = 0
}
return s, nil
}
func splitProcStat(content []byte) []string {
nameStart := bytes.IndexByte(content, '(')
nameEnd := bytes.LastIndexByte(content, ')')
restFields := strings.Fields(string(content[nameEnd+2:])) // +2 skip ') '
name := content[nameStart+1 : nameEnd]
pid := strings.TrimSpace(string(content[:nameStart]))
fields := make([]string, 3, len(restFields)+3)
fields[1] = string(pid)
fields[2] = string(name)
fields = append(fields, restFields...)
return fields
}

24
psutil/process_other.go Normal file
View File

@@ -0,0 +1,24 @@
//go:build !linux
// +build !linux
package psutil
func (p *process) cpuTimes() (*cpuTimesStat, error) {
times, err := p.proc.Times()
if err != nil {
return nil, err
}
s := &cpuTimesStat{
total: cpuTotal(times),
system: times.System,
user: times.User,
}
s.other = s.total - s.system - s.user
if s.other < 0.0001 {
s.other = 0
}
return s, nil
}