Skip to content

Commit dd95277

Browse files
committed
[jb] add JB_MEM_PROFILE env var to capture heap usage
1 parent 5361bab commit dd95277

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import git4idea.config.GitVcsApplicationSettings
1919
import io.gitpod.gitpodprotocol.api.GitpodClient
2020
import io.gitpod.gitpodprotocol.api.GitpodServerLauncher
2121
import io.gitpod.jetbrains.remote.services.HeartbeatService
22+
import io.gitpod.jetbrains.remote.utils.LatencyRecord
2223
import io.gitpod.jetbrains.remote.utils.Retrier.retry
2324
import io.gitpod.supervisor.api.*
2425
import io.gitpod.supervisor.api.Info.WorkspaceInfoResponse
@@ -38,11 +39,14 @@ import java.net.URI
3839
import java.net.http.HttpClient
3940
import java.net.http.HttpRequest
4041
import java.net.http.HttpResponse
42+
import java.nio.file.Files
43+
import java.nio.file.Paths
4144
import java.time.Duration
4245
import java.util.concurrent.CancellationException
4346
import java.util.concurrent.CompletableFuture
4447
import javax.websocket.DeploymentException
4548

49+
4650
@Service
4751
class GitpodManager : Disposable {
4852

@@ -59,6 +63,35 @@ class GitpodManager : Disposable {
5963
lifetime.terminate()
6064
}
6165

66+
private val memProfileMode = System.getenv("JB_MEM_PROFILE").toBoolean()
67+
68+
init {
69+
val memProfileJob = GlobalScope.launch {
70+
if (application.isHeadlessEnvironment || !memProfileMode) {
71+
return@launch
72+
}
73+
Files.newBufferedWriter(Paths.get("/tmp/jb_mem_profile.log")).use { writer ->
74+
writer.write("")
75+
writer.flush()
76+
val allocatedRecord = LatencyRecord()
77+
val usedRecord = LatencyRecord()
78+
while (isActive) {
79+
val totalMemory = Runtime.getRuntime().totalMemory() / (1024 * 1024)
80+
val usedMemory = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024)
81+
allocatedRecord.update(totalMemory)
82+
usedRecord.update(usedMemory)
83+
writer.write("allocated - current ${totalMemory}M, avg ${allocatedRecord.averageLatency}M, max ${allocatedRecord.maxLatency}M, 90% percentile ${allocatedRecord.percentile(
84+
90)}M\n")
85+
writer.write("used - current ${usedMemory}M,avg ${usedRecord.averageLatency}M, max ${usedRecord.maxLatency}M, 90% percentile ${usedRecord.percentile(
86+
90)}M\n")
87+
writer.flush()
88+
delay(1000)
89+
}
90+
}
91+
}
92+
lifetime.onTerminationOrNow { memProfileJob.cancel() }
93+
}
94+
6295
init {
6396
GlobalScope.launch {
6497
if (application.isHeadlessEnvironment) {
@@ -269,4 +302,4 @@ class GitpodManager : Disposable {
269302
serverJob.cancel()
270303
}
271304
}
272-
}
305+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 io.gitpod.jetbrains.remote.utils
6+
7+
import it.unimi.dsi.fastutil.longs.LongArrayList
8+
9+
class LatencyRecord {
10+
var totalLatency: Long = 0L
11+
var maxLatency: Long = 0
12+
val samples = LongArrayList()
13+
var samplesSorted = false
14+
15+
fun update(latencyInMS: Long) {
16+
samplesSorted = false
17+
samples.add(latencyInMS)
18+
totalLatency += latencyInMS
19+
if (latencyInMS > maxLatency) {
20+
maxLatency = latencyInMS
21+
}
22+
}
23+
24+
val averageLatency: Long
25+
get() = totalLatency / samples.size
26+
27+
fun percentile(n: Int): Long {
28+
if (!samplesSorted) {
29+
samples.sort()
30+
samplesSorted = true
31+
}
32+
val index = (samples.size * n / 100).coerceAtMost(samples.size - 1)
33+
return samples.getLong(index)
34+
}
35+
}

0 commit comments

Comments
 (0)