Implement method to get relevant threads from freeze

GitOrigin-RevId: 5ece9d8606b7677409ea66e55d28b68fa4893492
This commit is contained in:
Maxim.Kolmakov
2024-10-10 00:56:22 +02:00
committed by intellij-monorepo-bot
parent 5077d2c7a2
commit 0e565a1b23
5 changed files with 33 additions and 29 deletions

View File

@@ -0,0 +1,13 @@
f:com.intellij.platform.diagnostic.freezeAnalyzer.FreezeAnalysisResult
- <init>(java.lang.String,java.util.List,java.lang.String):V
- b:<init>(java.lang.String,java.util.List,java.lang.String,I,kotlin.jvm.internal.DefaultConstructorMarker):V
- f:component1():java.lang.String
- f:component2():java.util.List
- f:component3():java.lang.String
- f:copy(java.lang.String,java.util.List,java.lang.String):com.intellij.platform.diagnostic.freezeAnalyzer.FreezeAnalysisResult
- bs:copy$default(com.intellij.platform.diagnostic.freezeAnalyzer.FreezeAnalysisResult,java.lang.String,java.util.List,java.lang.String,I,java.lang.Object):com.intellij.platform.diagnostic.freezeAnalyzer.FreezeAnalysisResult
- equals(java.lang.Object):Z
- f:getAdditionalMessage():java.lang.String
- f:getMessage():java.lang.String
- f:getThreads():java.util.List
- hashCode():I

View File

@@ -1,10 +0,0 @@
f:com.intellij.platform.diagnostic.freezeAnalyzer.FreezeAnalysisResult
- <init>(java.lang.String,java.lang.String):V
- f:component1():java.lang.String
- f:component2():java.lang.String
- f:copy(java.lang.String,java.lang.String):com.intellij.platform.diagnostic.freezeAnalyzer.FreezeAnalysisResult
- bs:copy$default(com.intellij.platform.diagnostic.freezeAnalyzer.FreezeAnalysisResult,java.lang.String,java.lang.String,I,java.lang.Object):com.intellij.platform.diagnostic.freezeAnalyzer.FreezeAnalysisResult
- equals(java.lang.Object):Z
- f:getMessage():java.lang.String
- f:getStackTrace():java.lang.String
- hashCode():I

View File

@@ -14,5 +14,6 @@
<orderEntry type="library" scope="TEST" name="kotlin-test-assertions-core-jvm" level="project" />
<orderEntry type="library" scope="TEST" name="JUnit5" level="project" />
<orderEntry type="library" name="jetbrains-annotations" level="project" />
<orderEntry type="module" module-name="intellij.platform.util.base" />
</component>
</module>

View File

@@ -1,13 +1,17 @@
package com.intellij.platform.diagnostic.freezeAnalyzer
import com.intellij.diagnostic.ThreadDump
import com.intellij.threadDumpParser.ThreadDumpParser
import com.intellij.threadDumpParser.ThreadState
import org.jetbrains.annotations.ApiStatus
import kotlin.text.contains
import kotlin.text.lineSequence
@ApiStatus.Experimental
object FreezeAnalyzer {
fun getRelevantThreads(threadDump: ThreadDump): List<ThreadState> {
return analyzeFreeze(threadDump.rawDump, null)?.threads ?: emptyList()
}
/**
* Analyze freeze based on the IJ Platform knowledge and try to infer the relevant message.
* If analysis fails, it returns `null`.
@@ -20,9 +24,9 @@ object FreezeAnalyzer {
private fun analyzeEDThread(edt: ThreadState, threadDumpParsed: List<ThreadState>, testName: String?): FreezeAnalysisResult? =
when {
!edt.isWaiting && !edt.isSleeping -> findFirstRelevantMethod(edt.stackTrace)?.let { FreezeAnalysisResult("EDT is busy with $it", edt.stackTrace) }
edt.isWaiting && isWriteLockWait(edt) -> findThreadThatTookReadWriteLock(threadDumpParsed)?.let { FreezeAnalysisResult(it.message, it.stackTrace + "\n\n" + edt.stackTrace) }
edt.isWaiting && !isEDTFreezed(edt) -> FreezeAnalysisResult("${testName ?: ""}: EDT is not blocked/busy (freeze can be the result of extensive GC)", edt.stackTrace)
!edt.isWaiting && !edt.isSleeping -> findFirstRelevantMethod(edt.stackTrace)?.let { FreezeAnalysisResult("EDT is busy with $it", listOf(edt)) }
edt.isWaiting && isWriteLockWait(edt) -> findThreadThatTookReadWriteLock(threadDumpParsed)?.let { FreezeAnalysisResult(it.message, it.threads + listOf(edt), it.additionalMessage) }
edt.isWaiting && !isEDTFreezed(edt) -> FreezeAnalysisResult("${testName ?: ""}: EDT is not blocked/busy (freeze can be the result of extensive GC)", listOf(edt))
edt.isWaiting -> analyzeLock(edt, threadDumpParsed)
else -> null
}
@@ -47,7 +51,7 @@ object FreezeAnalyzer {
if (possibleThreadWithLock == null) return null
val methodFromThreadWithLock = findFirstRelevantMethod(possibleThreadWithLock.stackTrace)
if (methodFromThreadWithLock != null) {
return FreezeAnalysisResult("EDT is blocked on $relevantMethodFromEdt", "Possibly locked by $methodFromThreadWithLock in ${possibleThreadWithLock.name} \n\n" + edt.stackTrace + "\n\n" + possibleThreadWithLock.stackTrace)
return FreezeAnalysisResult("EDT is blocked on $relevantMethodFromEdt", listOf(edt, possibleThreadWithLock), additionalMessage = "Possibly locked by $methodFromThreadWithLock in ${possibleThreadWithLock.name}")
}
return null
}
@@ -75,17 +79,13 @@ object FreezeAnalyzer {
threadDumpParsed.firstOrNull { it.isAwaitedBy(threadState) }?.let {
if (isWaitingOnReadWriteLock(it)) {
FreezeAnalysisResult("Possible deadlock. Read lock is taken by ${findFirstRelevantMethod(threadState.stackTrace)}, but the thread is blocked by ${findFirstRelevantMethod(it.stackTrace)} which is waiting on RWLock",
"${threadState.name} took RWLock but it's blocked by ${it.name} which waits on RWLock" +
"\n\n" +
threadState.stackTrace + "\n\n" + it.stackTrace)
listOf(threadState, it), additionalMessage = "${threadState.name} took RWLock but it's blocked by ${it.name} which waits on RWLock")
}
else {
FreezeAnalysisResult("Read lock is taken by ${findFirstRelevantMethod(threadState.stackTrace)}, but this thread is blocked by ${findFirstRelevantMethod(it.stackTrace)}",
"${threadState.name} took RWLock but it's blocked by ${it.name}" +
"\n\n" +
threadState.stackTrace + "\n\n" + it.stackTrace)
listOf(threadState, it), additionalMessage = "${threadState.name} took RWLock but it's blocked by ${it.name}")
}
} ?: FreezeAnalysisResult("Long read action in ${findFirstRelevantMethod(threadState.stackTrace)}", threadState.stackTrace)
} ?: FreezeAnalysisResult("Long read action in ${findFirstRelevantMethod(threadState.stackTrace)}", listOf(threadState))
}
private fun isWaitingOnReadWriteLock(threadState: ThreadState): Boolean =
@@ -165,4 +165,4 @@ object FreezeAnalyzer {
}
}
data class FreezeAnalysisResult(val message: String, val stackTrace: String)
data class FreezeAnalysisResult(val message: String, val threads: List<ThreadState>, val additionalMessage: String? = null)

View File

@@ -14,7 +14,7 @@ class FreezeAnalyzerTest {
fun testAWTFreeze1() {
val threadDump = File(this::class.java.classLoader.getResource("freezes/awtFreeze/IDEA-344485.txt")!!.path).toPath().readText()
FreezeAnalyzer.analyzeFreeze(threadDump)?.message.shouldBe("EDT is busy with com.intellij.vcs.log.data.VcsLogUserResolverBase.resolveCurrentUser")
FreezeAnalyzer.analyzeFreeze(threadDump)?.stackTrace?.lineSequence()?.first().shouldBe("\"AWT-EventQueue-0\" prio=0 tid=0x0 nid=0x0 runnable")
FreezeAnalyzer.analyzeFreeze(threadDump)?.threads?.first()?.stackTrace?.lineSequence()?.first().shouldBe("\"AWT-EventQueue-0\" prio=0 tid=0x0 nid=0x0 runnable")
}
@Test
@@ -51,7 +51,7 @@ class FreezeAnalyzerTest {
fun testLockFreeze1() {
val threadDump = File(this::class.java.classLoader.getResource("freezes/readWriteLock/ML-2562.txt")!!.path).toPath().readText()
FreezeAnalyzer.analyzeFreeze(threadDump)?.message.shouldBe("Long read action in org.jetbrains.completion.full.line.local.generation.SimilarContextRetriever.getSimilarity")
FreezeAnalyzer.analyzeFreeze(threadDump)?.stackTrace?.shouldContain("\"DefaultDispatcher-worker-27\" prio=0 tid=0x0 nid=0x0 runnable")
FreezeAnalyzer.analyzeFreeze(threadDump)?.threads?.joinToString { it -> it.stackTrace }.shouldContain("\"DefaultDispatcher-worker-27\" prio=0 tid=0x0 nid=0x0 runnable")
}
@Test
@@ -82,14 +82,14 @@ class FreezeAnalyzerTest {
fun testGeneralLockFreeze() {
val threadDump = File(this::class.java.classLoader.getResource("freezes/generalLock/generalLock.txt")!!.path).toPath().readText()
FreezeAnalyzer.analyzeFreeze(threadDump)?.message.shouldBe("EDT is blocked on com.intellij.codeInsight.completion.CompletionProgressIndicator.blockingWaitForFinish")
FreezeAnalyzer.analyzeFreeze(threadDump)?.stackTrace?.shouldStartWith("Possibly locked by com.intellij.codeInsight.completion.JavaMethodCallElement.<init> in DefaultDispatcher-worker-55")
FreezeAnalyzer.analyzeFreeze(threadDump)?.threads?.joinToString { it -> it.stackTrace }.shouldStartWith("Possibly locked by com.intellij.codeInsight.completion.JavaMethodCallElement.<init> in DefaultDispatcher-worker-55")
}
@Test
fun testGeneralLock2Freeze() {
val threadDump = File(this::class.java.classLoader.getResource("freezes/generalLock/generalLock2.txt")!!.path).toPath().readText()
FreezeAnalyzer.analyzeFreeze(threadDump)?.message.shouldBe("EDT is blocked on org.jetbrains.kotlin.idea.base.projectStructure.KotlinProjectStructureUtils\$hasKotlinJvmRuntime\$1.invoke")
FreezeAnalyzer.analyzeFreeze(threadDump)?.stackTrace?.shouldStartWith("Possibly locked by com.intellij.lang.javascript.psi.stubs.JSUsedRemoteModulesIndex.getUsedModules in ApplicationImpl pooled thread 10")
FreezeAnalyzer.analyzeFreeze(threadDump)?.additionalMessage.shouldBe("Possibly locked by com.intellij.lang.javascript.psi.stubs.JSUsedRemoteModulesIndex.getUsedModules in ApplicationImpl pooled thread 10")
}
@Test
@@ -110,7 +110,7 @@ class FreezeAnalyzerTest {
withClue("") {
assertSoftly {
FreezeAnalyzer.analyzeFreeze(threadDump)?.message.shouldBe("Long read action in com.intellij.openapi.roots.impl.PackageDirectoryCacheImpl\$PackageInfo.lambda\$new\$0")
FreezeAnalyzer.analyzeFreeze(threadDump)?.stackTrace.shouldStartWith("\"DefaultDispatcher-worker-22\" prio=0 tid=0x0 nid=0x0 waiting on condition")
FreezeAnalyzer.analyzeFreeze(threadDump)?.threads?.joinToString { it -> it.stackTrace }.shouldStartWith("\"DefaultDispatcher-worker-22\" prio=0 tid=0x0 nid=0x0 waiting on condition")
}
}
}