package process
import (
"context"
"encoding/json"
"errors"
"runtime"
"sort"
"time"
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
"github.com/gofiber/fiber/v2/internal/gopsutil/cpu"
"github.com/gofiber/fiber/v2/internal/gopsutil/mem"
)
var (
invoke common .Invoker = common .Invoke {}
ErrorNoChildren = errors .New ("process does not have children" )
ErrorProcessNotRunning = errors .New ("process does not exist" )
)
type Process struct {
Pid int32 `json:"pid"`
name string
status string
parent int32
numCtxSwitches *NumCtxSwitchesStat
uids []int32
gids []int32
groups []int32
numThreads int32
memInfo *MemoryInfoStat
sigInfo *SignalInfoStat
createTime int64
lastCPUTimes *cpu .TimesStat
lastCPUTime time .Time
tgid int32
}
type OpenFilesStat struct {
Path string `json:"path"`
Fd uint64 `json:"fd"`
}
type MemoryInfoStat struct {
RSS uint64 `json:"rss"`
VMS uint64 `json:"vms"`
HWM uint64 `json:"hwm"`
Data uint64 `json:"data"`
Stack uint64 `json:"stack"`
Locked uint64 `json:"locked"`
Swap uint64 `json:"swap"`
}
type SignalInfoStat struct {
PendingProcess uint64 `json:"pending_process"`
PendingThread uint64 `json:"pending_thread"`
Blocked uint64 `json:"blocked"`
Ignored uint64 `json:"ignored"`
Caught uint64 `json:"caught"`
}
type RlimitStat struct {
Resource int32 `json:"resource"`
Soft int32 `json:"soft"`
Hard int32 `json:"hard"`
Used uint64 `json:"used"`
}
type IOCountersStat struct {
ReadCount uint64 `json:"readCount"`
WriteCount uint64 `json:"writeCount"`
ReadBytes uint64 `json:"readBytes"`
WriteBytes uint64 `json:"writeBytes"`
}
type NumCtxSwitchesStat struct {
Voluntary int64 `json:"voluntary"`
Involuntary int64 `json:"involuntary"`
}
type PageFaultsStat struct {
MinorFaults uint64 `json:"minorFaults"`
MajorFaults uint64 `json:"majorFaults"`
ChildMinorFaults uint64 `json:"childMinorFaults"`
ChildMajorFaults uint64 `json:"childMajorFaults"`
}
const (
RLIMIT_CPU int32 = 0
RLIMIT_FSIZE int32 = 1
RLIMIT_DATA int32 = 2
RLIMIT_STACK int32 = 3
RLIMIT_CORE int32 = 4
RLIMIT_RSS int32 = 5
RLIMIT_NPROC int32 = 6
RLIMIT_NOFILE int32 = 7
RLIMIT_MEMLOCK int32 = 8
RLIMIT_AS int32 = 9
RLIMIT_LOCKS int32 = 10
RLIMIT_SIGPENDING int32 = 11
RLIMIT_MSGQUEUE int32 = 12
RLIMIT_NICE int32 = 13
RLIMIT_RTPRIO int32 = 14
RLIMIT_RTTIME int32 = 15
)
func (p Process ) String () string {
s , _ := json .Marshal (p )
return string (s )
}
func (o OpenFilesStat ) String () string {
s , _ := json .Marshal (o )
return string (s )
}
func (m MemoryInfoStat ) String () string {
s , _ := json .Marshal (m )
return string (s )
}
func (r RlimitStat ) String () string {
s , _ := json .Marshal (r )
return string (s )
}
func (i IOCountersStat ) String () string {
s , _ := json .Marshal (i )
return string (s )
}
func (p NumCtxSwitchesStat ) String () string {
s , _ := json .Marshal (p )
return string (s )
}
func Pids () ([]int32 , error ) {
return PidsWithContext (context .Background ())
}
func PidsWithContext (ctx context .Context ) ([]int32 , error ) {
pids , err := pidsWithContext (ctx )
sort .Slice (pids , func (i , j int ) bool { return pids [i ] < pids [j ] })
return pids , err
}
func NewProcess (pid int32 ) (*Process , error ) {
p := &Process {Pid : pid }
exists , err := PidExists (pid )
if err != nil {
return p , err
}
if !exists {
return p , ErrorProcessNotRunning
}
_, err = p .CreateTime ()
return p , err
}
func PidExists (pid int32 ) (bool , error ) {
return PidExistsWithContext (context .Background (), pid )
}
func (p *Process ) Background () (bool , error ) {
return p .BackgroundWithContext (context .Background ())
}
func (p *Process ) BackgroundWithContext (ctx context .Context ) (bool , error ) {
fg , err := p .ForegroundWithContext (ctx )
if err != nil {
return false , err
}
return !fg , err
}
func (p *Process ) Percent (interval time .Duration ) (float64 , error ) {
return p .PercentWithContext (context .Background (), interval )
}
func (p *Process ) PercentWithContext (ctx context .Context , interval time .Duration ) (float64 , error ) {
cpuTimes , err := p .Times ()
if err != nil {
return 0 , err
}
now := time .Now ()
if interval > 0 {
p .lastCPUTimes = cpuTimes
p .lastCPUTime = now
if err := common .Sleep (ctx , interval ); err != nil {
return 0 , err
}
cpuTimes , err = p .Times ()
now = time .Now ()
if err != nil {
return 0 , err
}
} else {
if p .lastCPUTimes == nil {
p .lastCPUTimes = cpuTimes
p .lastCPUTime = now
return 0 , nil
}
}
numcpu := runtime .NumCPU ()
delta := (now .Sub (p .lastCPUTime ).Seconds ()) * float64 (numcpu )
ret := calculatePercent (p .lastCPUTimes , cpuTimes , delta , numcpu )
p .lastCPUTimes = cpuTimes
p .lastCPUTime = now
return ret , nil
}
func (p *Process ) IsRunning () (bool , error ) {
return p .IsRunningWithContext (context .Background ())
}
func (p *Process ) IsRunningWithContext (ctx context .Context ) (bool , error ) {
createTime , err := p .CreateTimeWithContext (ctx )
if err != nil {
return false , err
}
p2 , err := NewProcess (p .Pid )
if err == ErrorProcessNotRunning {
return false , nil
}
createTime2 , err := p2 .CreateTimeWithContext (ctx )
if err != nil {
return false , err
}
return createTime == createTime2 , nil
}
func (p *Process ) CreateTime () (int64 , error ) {
return p .CreateTimeWithContext (context .Background ())
}
func (p *Process ) CreateTimeWithContext (ctx context .Context ) (int64 , error ) {
if p .createTime != 0 {
return p .createTime , nil
}
createTime , err := p .createTimeWithContext (ctx )
p .createTime = createTime
return p .createTime , err
}
func calculatePercent(t1 , t2 *cpu .TimesStat , delta float64 , numcpu int ) float64 {
if delta == 0 {
return 0
}
delta_proc := t2 .Total () - t1 .Total ()
overall_percent := ((delta_proc / delta ) * 100 ) * float64 (numcpu )
return overall_percent
}
func (p *Process ) MemoryPercent () (float32 , error ) {
return p .MemoryPercentWithContext (context .Background ())
}
func (p *Process ) MemoryPercentWithContext (ctx context .Context ) (float32 , error ) {
machineMemory , err := mem .VirtualMemory ()
if err != nil {
return 0 , err
}
total := machineMemory .Total
processMemory , err := p .MemoryInfo ()
if err != nil {
return 0 , err
}
used := processMemory .RSS
return (100 * float32 (used ) / float32 (total )), nil
}
func (p *Process ) CPUPercent () (float64 , error ) {
return p .CPUPercentWithContext (context .Background ())
}
func (p *Process ) CPUPercentWithContext (ctx context .Context ) (float64 , error ) {
crt_time , err := p .CreateTime ()
if err != nil {
return 0 , err
}
cput , err := p .Times ()
if err != nil {
return 0 , err
}
created := time .Unix (0 , crt_time *int64 (time .Millisecond ))
totalTime := time .Since (created ).Seconds ()
if totalTime <= 0 {
return 0 , nil
}
return 100 * cput .Total () / totalTime , nil
}
func (p *Process ) Groups () ([]int32 , error ) {
return p .GroupsWithContext (context .Background ())
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .