Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ class CodeWhispererConfigurable(private val project: Project) :
.resizableColumn()
.align(Align.FILL)
}
row {
checkBox("Enable CPU profiling")
.bindSelected(
{ LspSettings.getInstance().isCpuProfilingEnabled() },
{ LspSettings.getInstance().setCpuProfilingEnabled(it) }
)
.comment("Enable CPU profiling for the LSP server to help diagnose performance issues")
}
}

group(message("aws.settings.codewhisperer.group.general")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.intellij.util.io.DigestUtil
import com.intellij.util.net.HttpConfigurable
import com.intellij.util.net.JdkProxyProvider
import com.intellij.util.net.ssl.CertificateManager
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Job
Expand Down Expand Up @@ -125,6 +126,19 @@ internal class LSPProcessListener : ProcessListener {

override fun processTerminated(event: ProcessEvent) {
LOG.info { "LSP process terminated with exit code ${event.exitCode}" }

// Check for CPU profile file after process termination only if profiling was enabled
if (LspSettings.getInstance().isCpuProfilingEnabled()) {
val processId = ProcessHandle.current().pid()
val profileDir = System.getProperty("java.io.tmpdir").trimEnd('/')
val profilePath = "$profileDir/node-profile-$processId.cpuprofile"
if (Files.exists(Path.of(profilePath))) {
LOG.info { "CPU profile file created: $profilePath" }
} else {
LOG.warn { "CPU profile file not found at: $profilePath" }
}
}

try {
this.outputStreamWriter.close()
this.outputStream.close()
Expand Down Expand Up @@ -465,9 +479,20 @@ private class AmazonQServerInstance(private val project: Project, private val cs

val cmd = NodeExePatcher.patch(nodePath)
.withParameters(
LspSettings.getInstance().getArtifactPath() ?: artifact.resolve("aws-lsp-codewhisperer.js").toString(),
"--stdio",
"--set-credentials-encryption-key",
buildList {
if (LspSettings.getInstance().isCpuProfilingEnabled()) {
val processId = ProcessHandle.current().pid()
val profileDir = System.getProperty("java.io.tmpdir").trimEnd('/')
val profilePath = "node-profile-$processId.cpuprofile"
LOG.info { "Node.js CPU profile will be saved to: $profileDir $profilePath" }
add("--cpu-prof")
add("--cpu-prof-dir=$profileDir")
add("--cpu-prof-name=$profilePath")
}
add(LspSettings.getInstance().getArtifactPath() ?: artifact.resolve("aws-lsp-codewhisperer.js").toString())
add("--stdio")
add("--set-credentials-encryption-key")
}
).withEnvironment(
buildMap {
extraCaCerts?.let {
Expand Down Expand Up @@ -721,18 +746,28 @@ private class AmazonQServerInstance(private val project: Project, private val cs
// otherwise can deadlock waiting for frozen flare process
cs.launch {
languageServer.apply {
withTimeout(2000) {
withTimeout(5000) {
shutdown().await()
exit()
}
}
}.asCompletableFuture()
.get(3000, TimeUnit.MILLISECONDS)
.get(6000, TimeUnit.MILLISECONDS)
} catch (e: CancellationException) {
// Expected during shutdown, proceed with cleanup
launcherHandler.process.destroy()
Thread.sleep(500)
} catch (e: Exception) {
LOG.warn(e) { "LSP shutdown failed" }
launcherHandler.killProcess()
launcherHandler.process.destroy()
Thread.sleep(500)
} finally {
if (!launcherHandler.isProcessTerminated) {
launcherHandler.killProcess()
}
}
} else if (!launcherHandler.isProcessTerminated) {
Thread.sleep(500)
launcherHandler.killProcess()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class LspSettings : PersistentStateComponent<LspConfiguration> {

fun getNodeRuntimePath() = state.nodeRuntimePath

fun isCpuProfilingEnabled() = state.cpuProfilingEnabled

fun setArtifactPath(artifactPath: String?) {
state.artifactPath = artifactPath.nullize(nullizeSpaces = true)
}
Expand All @@ -35,6 +37,10 @@ class LspSettings : PersistentStateComponent<LspConfiguration> {
state.nodeRuntimePath = nodeRuntimePath.nullize(nullizeSpaces = true)
}

fun setCpuProfilingEnabled(enabled: Boolean) {
state.cpuProfilingEnabled = enabled
}

companion object {
fun getInstance(): LspSettings = service()
}
Expand All @@ -43,4 +49,5 @@ class LspSettings : PersistentStateComponent<LspConfiguration> {
class LspConfiguration : BaseState() {
var artifactPath by string()
var nodeRuntimePath by string()
var cpuProfilingEnabled by property(false)
}
Loading