Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/ws-daemon/pkg/cgroup/plugin_process_priority_v2.go
2499 views
1
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package cgroup
6
7
import (
8
"context"
9
"errors"
10
"fmt"
11
"io/fs"
12
"io/ioutil"
13
"os"
14
"path/filepath"
15
"regexp"
16
"strconv"
17
"strings"
18
"syscall"
19
"time"
20
21
"github.com/shirou/gopsutil/process"
22
23
"github.com/gitpod-io/gitpod/common-go/log"
24
)
25
26
// ProcessType referes to the kinds of prioritisable processes in the cgroup
27
type ProcessType string
28
29
const (
30
ProcessWorkspaceKit ProcessType = "workspacekit"
31
// ProcessSupervisor referes to a supervisor process
32
ProcessSupervisor ProcessType = "supervisor"
33
// ProcessIDE refers to node.js IDE process
34
ProcessIDE ProcessType = "ide"
35
// ProcessWebIDEHelper refers to VS Code Browser process
36
ProcessWebIDEHelper ProcessType = "ide-helper"
37
// ProcessCodeServer refers to VS Code Desktop IDE process
38
ProcessCodeServer ProcessType = "vscode-server"
39
// ProcessCodeServerHelper refers to VS Code Desktop child process
40
ProcessCodeServerHelper ProcessType = "vscode-server-helper"
41
// ProcessJetBrainsIDE refers to JetBrains IDE process
42
ProcessJetBrainsIDE ProcessType = "jetbrains-ide"
43
// ProcessDefault referes to any process that is not one of the above
44
ProcessDefault ProcessType = "default"
45
46
// Repeat applying until this number of processes is reached
47
NumberOfProcessesToStopApplying = 5
48
)
49
50
type OOMScoreAdjConfig struct {
51
Enabled bool `json:"enabled"`
52
Tier1 int `json:"tier1"`
53
Tier2 int `json:"tier2"`
54
}
55
56
type ProcessPriorityV2 struct {
57
ProcessPriorities map[ProcessType]int
58
OOMScoreAdj map[ProcessType]int
59
EnableOOMScoreAdj bool
60
}
61
62
func (c *ProcessPriorityV2) Name() string { return "process-priority-v2" }
63
func (c *ProcessPriorityV2) Type() Version { return Version2 }
64
65
func (c *ProcessPriorityV2) Apply(ctx context.Context, opts *PluginOptions) error {
66
fullCgroupPath := filepath.Join(opts.BasePath, opts.CgroupPath)
67
68
t := time.NewTicker(10 * time.Second)
69
defer t.Stop()
70
71
for {
72
select {
73
case <-ctx.Done():
74
return nil
75
case <-t.C:
76
}
77
78
data, err := ioutil.ReadFile(filepath.Join(fullCgroupPath, "workspace", "user", "cgroup.procs"))
79
if errors.Is(err, fs.ErrNotExist) {
80
// the target cgroup/workspace has gone
81
return nil
82
} else if err != nil {
83
log.WithFields(log.OWI("", "", opts.InstanceId)).WithField("path", fullCgroupPath).WithError(err).Errorf("cannot read cgroup.procs file")
84
return err
85
}
86
87
var countRunningProcess int
88
lines := strings.Split(string(data), "\n")
89
for _, line := range lines {
90
line = strings.TrimSpace(line)
91
if len(line) == 0 {
92
continue
93
}
94
95
pid, err := strconv.ParseInt(line, 10, 64)
96
if err != nil {
97
log.WithError(err).WithFields(log.OWI("", "", opts.InstanceId)).WithField("line", line).Warn("cannot parse pid")
98
continue
99
}
100
101
proc, err := process.NewProcess(int32(pid))
102
if err != nil {
103
if errors.Is(err, process.ErrorProcessNotRunning) {
104
continue
105
}
106
107
log.WithError(err).WithFields(log.OWI("", "", opts.InstanceId)).WithField("pid", pid).Warn("cannot get process")
108
continue
109
}
110
111
procType := determineProcessType(proc)
112
if procType == ProcessDefault {
113
continue
114
}
115
116
c.adaptProcessPriorites(procType, pid)
117
if c.EnableOOMScoreAdj {
118
c.adaptOOMScore(procType, pid)
119
}
120
}
121
122
if countRunningProcess >= NumberOfProcessesToStopApplying {
123
return nil
124
}
125
}
126
}
127
128
var (
129
vsCodeNodeRegex = regexp.MustCompile("/home/gitpod/.vscode-server/bin/.*/node")
130
jetBrainsRegex = regexp.MustCompile("/ide-desktop/.*/backend/plugins/remote-dev-server/selfcontained/lib")
131
)
132
133
func determineProcessType(p *process.Process) ProcessType {
134
cmd := extractCommand(p)
135
if len(cmd) == 0 {
136
return ProcessDefault
137
}
138
139
if strings.HasSuffix(cmd[0], "workspacekit") || (len(cmd) >= 2 && cmd[1] == "ring1") {
140
return ProcessWorkspaceKit
141
}
142
143
if strings.HasSuffix(cmd[0], "supervisor") {
144
return ProcessSupervisor
145
}
146
147
if strings.HasSuffix(cmd[0], "/bin/code-server") {
148
return ProcessCodeServer
149
}
150
151
if vsCodeNodeRegex.MatchString(cmd[0]) {
152
return ProcessCodeServerHelper
153
}
154
155
if strings.HasSuffix(cmd[0], "/ide/bin/gitpod-code") {
156
return ProcessIDE
157
}
158
159
if strings.HasSuffix(cmd[0], "/ide/node") {
160
return ProcessWebIDEHelper
161
}
162
163
if jetBrainsRegex.MatchString(strings.Join(cmd, " ")) {
164
return ProcessJetBrainsIDE
165
}
166
167
return ProcessDefault
168
}
169
170
func extractCommand(p *process.Process) []string {
171
if p == nil {
172
return []string{}
173
}
174
175
cmdLine, err := p.CmdlineSlice()
176
if err != nil {
177
return []string{}
178
}
179
180
if len(cmdLine) == 0 {
181
return []string{}
182
}
183
184
cmd := cmdLine[0]
185
if cmd == "/bin/bash" || cmd == "sh" {
186
return cmdLine[1:]
187
}
188
189
return cmdLine
190
}
191
192
func (c *ProcessPriorityV2) adaptProcessPriorites(procType ProcessType, pid int64) {
193
priority, ok := c.ProcessPriorities[procType]
194
if !ok {
195
return
196
}
197
198
err := syscall.Setpriority(syscall.PRIO_PROCESS, int(pid), priority)
199
if err != nil {
200
log.WithError(err).WithField("pid", pid).WithField("priority", priority).Warn("cannot set process priority")
201
}
202
}
203
204
func (c *ProcessPriorityV2) adaptOOMScore(procType ProcessType, pid int64) {
205
oomScoreAdj, ok := c.OOMScoreAdj[procType]
206
if !ok {
207
return
208
}
209
210
err := os.WriteFile(fmt.Sprintf("/proc/%v/oom_score_adj", pid), []byte(strconv.Itoa(oomScoreAdj)), 0644)
211
if err != nil {
212
log.WithError(err).WithField("pid", pid).WithField("oomScoreAdj", oomScoreAdj).Warn("cannot set oomScoreAdj")
213
}
214
}
215
216