-
Notifications
You must be signed in to change notification settings - Fork 0
/
process_state.go
137 lines (124 loc) · 2.95 KB
/
process_state.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package base
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"strings"
"syscall"
"github.com/codefly-dev/core/wool"
"github.com/shirou/gopsutil/v3/process"
)
func (ps ProcessState) String() string {
return [...]string{
"Unknown",
"NotFound",
"Running",
"Interruptible Sleep",
"Uninterruptible Sleep",
"Stopped",
"Zombie",
"Dead",
"Tracing Stop",
"Idle",
"Parked",
"Waking",
}[ps]
}
type TrackedProcess struct {
PID int
Killed bool
}
func (p *TrackedProcess) GetState(ctx context.Context) (ProcessState, error) {
w := wool.Get(ctx).In("TrackedProcess.Status", wool.Field("pid", p.PID))
// Check for PID
proc, err := os.FindProcess(p.PID)
if err != nil {
return NotFound, nil
}
// Sending signal 0 to a pid will return an error if the pid is not running
// and do nothing if it is.
err = proc.Signal(syscall.Signal(0))
if err == nil {
state, err := findState(ctx, p.PID)
if err != nil {
return Unknown, w.Wrapf(err, "cannot check if proc is defunct")
}
return state, nil
}
return Dead, nil
}
func (p *TrackedProcess) GetCPU(ctx context.Context) (*CPU, error) {
w := wool.Get(ctx).In("TrackedProcess.Usage", wool.Field("pid", p.PID))
proc, err := process.NewProcess(int32(p.PID))
if err != nil {
return nil, w.Wrapf(err, "cannot createAndWait process")
}
// Get CPU percent
cpuPercent, err := proc.CPUPercent()
if err != nil {
return nil, w.Wrapf(err, "cannot get cpu percent")
}
return &CPU{usage: cpuPercent}, nil
}
func (p *TrackedProcess) GetMemory(ctx context.Context) (*Memory, error) {
w := wool.Get(ctx).In("TrackedProcess.Usage", wool.Field("pid", p.PID))
proc, err := process.NewProcess(int32(p.PID))
if err != nil {
return nil, w.Wrapf(err, "cannot createAndWait process")
}
// Get memory info
memInfo, err := proc.MemoryInfo()
if err != nil {
return nil, w.Wrapf(err, "cannot get memory info")
}
return &Memory{used: (memInfo.RSS) / 1024.0}, nil
}
func parseState(out string) (string, bool) {
state := strings.TrimSpace(out)
// Can we the S+ version
if regular, ok := strings.CutSuffix(state, "+"); ok {
return regular, false
}
return state, true
}
func findState(ctx context.Context, pid int) (ProcessState, error) {
w := wool.Get(ctx).In("findState", wool.Field("pid", pid))
// #nosec G204
cmd := exec.Command("ps", "-p", fmt.Sprintf("%d", pid), "-o", "state=")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return Unknown, err
}
state, tts := parseState(out.String())
if tts {
w.Debug("process %d is in TTS")
}
switch state {
case "R":
return Running, nil
case "S":
return InterruptibleSleep, nil
case "D":
return UninterruptibleSleep, nil
case "T":
return Stopped, nil
case "Z":
return Zombie, nil
case "X":
return Dead, nil
case "t":
return TracingStop, nil
case "I":
return Idle, nil
case "P":
return Parked, nil
case "W":
return Waking, nil
default:
return Unknown, w.NewError("unknown state: %s", out.String())
}
}