Skip to content

Commit 49224d1

Browse files
yaohui-wyhroboquat
authored andcommitted
[jb] configure vmoptions for intellij backend server performance optimization
1 parent 5ecf3ba commit 49224d1

File tree

3 files changed

+100
-12
lines changed

3 files changed

+100
-12
lines changed

components/ide/jetbrains/image/leeway.Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ RUN apk add --no-cache --upgrade curl gzip tar unzip
99
RUN curl -sSLo backend.tar.gz "$JETBRAINS_BACKEND_URL" && tar -xf backend.tar.gz --strip-components=1 && rm backend.tar.gz
1010
COPY --chown=33333:33333 components-ide-jetbrains-backend-plugin--plugin/build/distributions/gitpod-remote-0.0.1.zip /workdir
1111
RUN unzip gitpod-remote-0.0.1.zip -d plugins/ && rm gitpod-remote-0.0.1.zip
12-
RUN printf '\n-Dgtw.disable.exit.dialog=true\n' >> $(find /workdir/bin/ -name "*64.vmoptions")
1312
# enable shared indexes by default
1413
RUN printf '\nshared.indexes.download.auto.consent=true' >> "/workdir/bin/idea.properties"
1514

components/ide/jetbrains/image/status/main.go

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ func main() {
8787
}
8888
}
8989

90-
err = configureXmx(alias)
90+
err = configureVMOptions(alias)
9191
if err != nil {
92-
log.WithError(err).Error("failed to configure backend Xmx")
92+
log.WithError(err).Error("failed to configure vmoptions")
9393
}
9494
go run(wsInfo, alias)
9595

@@ -307,19 +307,65 @@ func handleSignal(projectPath string) {
307307
log.Info("asked IDE to terminate")
308308
}
309309

310-
func configureXmx(alias string) error {
311-
xmx := os.Getenv(strings.ToUpper(alias) + "_XMX")
312-
if xmx == "" {
313-
return nil
310+
func configureVMOptions(alias string) error {
311+
idePrefix := alias
312+
if alias == "intellij" {
313+
idePrefix = "idea"
314314
}
315-
launcherPath := "/ide-desktop/backend/plugins/remote-dev-server/bin/launcher.sh"
316-
content, err := ioutil.ReadFile(launcherPath)
315+
// [idea64|goland64|pycharm64|phpstorm64].vmoptions
316+
path := fmt.Sprintf("/ide-desktop/backend/bin/%s64.vmoptions", idePrefix)
317+
content, err := ioutil.ReadFile(path)
317318
if err != nil {
318319
return err
319320
}
320-
// by default remote dev already set -Xmx2048m, see /ide-desktop/backend/plugins/remote-dev-server/bin/launcher.sh
321-
newContent := strings.Replace(string(content), "-Xmx2048m", "-Xmx"+xmx, 1)
322-
return ioutil.WriteFile(launcherPath, []byte(newContent), 0)
321+
newContent := updateVMOptions(alias, string(content))
322+
return ioutil.WriteFile(path, []byte(newContent), 0)
323+
}
324+
325+
// deduplicateVMOption append new VMOptions onto old VMOptions and remove any duplicated leftmost options
326+
func deduplicateVMOption(oldLines []string, newLines []string, predicate func(l, r string) bool) []string {
327+
var result []string
328+
var merged = append(oldLines, newLines...)
329+
for i, left := range merged {
330+
for _, right := range merged[i+1:] {
331+
if predicate(left, right) {
332+
left = ""
333+
break
334+
}
335+
}
336+
if left != "" {
337+
result = append(result, left)
338+
}
339+
}
340+
return result
341+
}
342+
343+
func updateVMOptions(alias string, content string) string {
344+
// inspired by how intellij platform merge the VMOptions
345+
// https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java#L1115
346+
filterFunc := func(l, r string) bool {
347+
isEqual := l == r
348+
isXmx := strings.HasPrefix(l, "-Xmx") && strings.HasPrefix(r, "-Xmx")
349+
isXms := strings.HasPrefix(l, "-Xms") && strings.HasPrefix(r, "-Xms")
350+
isXss := strings.HasPrefix(l, "-Xss") && strings.HasPrefix(r, "-Xss")
351+
isXXOptions := strings.HasPrefix(l, "-XX:") && strings.HasPrefix(r, "-XX:") &&
352+
strings.Split(l, "=")[0] == strings.Split(r, "=")[0]
353+
return isEqual || isXmx || isXms || isXss || isXXOptions
354+
}
355+
// original vmoptions (inherited from $JETBRAINS_IDE_HOME/bin/idea64.vmoptions)
356+
ideaVMOptionsLines := strings.Fields(content)
357+
// Gitpod's default customization
358+
gitpodVMOptions := []string{"-Dgtw.disable.exit.dialog=true"}
359+
vmoptions := deduplicateVMOption(ideaVMOptionsLines, gitpodVMOptions, filterFunc)
360+
361+
// user-defined vmoptions
362+
userVMOptionsVar := os.Getenv(strings.ToUpper(alias) + "_VMOPTIONS")
363+
userVMOptions := strings.Fields(userVMOptionsVar)
364+
if len(userVMOptions) > 0 {
365+
vmoptions = deduplicateVMOption(vmoptions, userVMOptions, filterFunc)
366+
}
367+
// vmoptions file should end with a newline
368+
return strings.Join(vmoptions, "\n") + "\n"
323369
}
324370

325371
/**

components/ide/jetbrains/image/status/main_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
package main
66

77
import (
8+
"strings"
89
"testing"
910

1011
protocol "github.com/gitpod-io/gitpod/gitpod-protocol"
1112
"github.com/google/go-cmp/cmp"
13+
"github.com/google/go-cmp/cmp/cmpopts"
1214
)
1315

1416
func TestGetProductConfig(t *testing.T) {
@@ -23,3 +25,44 @@ func TestGetProductConfig(t *testing.T) {
2325
t.Errorf("unexpected output (-want +got):\n%s", diff)
2426
}
2527
}
28+
29+
func TestUpdateVMOptions(t *testing.T) {
30+
tests := []struct {
31+
Desc string
32+
Alias string
33+
EnvVar map[string]string
34+
Src string
35+
Expectation string
36+
}{
37+
{"goland64.vmoptions", "goland", nil, "-Xms128m\n-Xmx750m\n-Dsun.tools.attach.tmp.only=true", "-Xms128m\n-Xmx750m\n-Dsun.tools.attach.tmp.only=true\n-Dgtw.disable.exit.dialog=true"},
38+
{"idea64.vmoptions", "intellij", nil, "-Xms128m\n-Xmx750m\n-Dsun.tools.attach.tmp.only=true", "-Xms128m\n-Xmx750m\n-Dsun.tools.attach.tmp.only=true\n-Dgtw.disable.exit.dialog=true"},
39+
{"idea64.vmoptions (INTELLIJ_VMOPTIONS env set)", "intellij", map[string]string{"INTELLIJ_VMOPTIONS": "-Xmx2048m"}, "-Xms128m\n-Xmx750m\n-Dsun.tools.attach.tmp.only=true", "-Xms128m\n-Xmx2048m\n-Dsun.tools.attach.tmp.only=true\n-Dgtw.disable.exit.dialog=true"},
40+
{"idea64.vmoptions (INTELLIJ_VMOPTIONS env set)", "intellij", map[string]string{"INTELLIJ_VMOPTIONS": "-Xmx4096m"}, "-Xms128m\n-Xmx2g\n-Dsun.tools.attach.tmp.only=true", "-Xms128m\n-Xmx4096m\n-Dsun.tools.attach.tmp.only=true\n-Dgtw.disable.exit.dialog=true"},
41+
{"idea64.vmoptions (INTELLIJ_VMOPTIONS env set)", "intellij", map[string]string{"INTELLIJ_VMOPTIONS": "-Xmx4096m -XX:MaxRAMPercentage=75"}, "-Xms128m\n-Xmx2g\n-Dsun.tools.attach.tmp.only=true", "-Xms128m\n-Xmx4096m\n-XX:MaxRAMPercentage=75\n-Dsun.tools.attach.tmp.only=true\n-Dgtw.disable.exit.dialog=true"},
42+
{"goland64.vmoptions (GOLAND_VMOPTIONS env set with conflicting options)", "goland", map[string]string{"GOLAND_VMOPTIONS": "-ea -XX:+IgnoreUnrecognizedVMOptions -XX:MaxRAMPercentage=75 -XX:MaxRAMPercentage=50"}, "-Xms128m\n-Xmx2g\n-Dsun.tools.attach.tmp.only=true", "-Xms128m\n-Xmx2g\n-Dsun.tools.attach.tmp.only=true\n-Dgtw.disable.exit.dialog=true\n-ea\n-XX:+IgnoreUnrecognizedVMOptions\n-XX:MaxRAMPercentage=50"},
43+
}
44+
for _, test := range tests {
45+
for v := range test.EnvVar {
46+
t.Setenv(v, test.EnvVar[v])
47+
}
48+
// compare vmoptions string content equality (i.e. split into slices and compare ignore order)
49+
lessFunc := func(a, b string) bool { return a < b }
50+
51+
t.Run(test.Desc, func(t *testing.T) {
52+
actual := updateVMOptions(test.Alias, test.Src)
53+
if diff := cmp.Diff(strings.Fields(test.Expectation), strings.Fields(actual), cmpopts.SortSlices(lessFunc)); diff != "" {
54+
t.Errorf("unexpected output (-want +got):\n%s", diff)
55+
}
56+
})
57+
58+
t.Run("updateVMOptions multiple time should be stable", func(t *testing.T) {
59+
actual := test.Src
60+
for i := 0; i < 5; i++ {
61+
actual = updateVMOptions(test.Alias, actual)
62+
if diff := cmp.Diff(strings.Fields(test.Expectation), strings.Fields(actual), cmpopts.SortSlices(lessFunc)); diff != "" {
63+
t.Errorf("unexpected output (-want +got):\n%s", diff)
64+
}
65+
}
66+
})
67+
}
68+
}

0 commit comments

Comments
 (0)