mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[debugger] Improve statistics about Kotlin code fragment evaluation
IDEA-362361 (cherry picked from commit be355d22d5edae92a009c687dc7c083eb0dff335) IJ-CR-151042 GitOrigin-RevId: 089dd4e20439c541ee8da7f9105d55399c77d1fa
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a9a6b63c37
commit
9afb971dd9
@@ -118,7 +118,7 @@ class K1KotlinCodeFragmentCompiler : KotlinCodeFragmentCompiler {
|
||||
EvaluationCompilerResult.COMPILATION_FAILURE,
|
||||
stats
|
||||
)
|
||||
evaluationException(DefaultErrorMessages.render(it))
|
||||
throw IncorrectCodeFragmentException(DefaultErrorMessages.render(it))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ package org.jetbrains.kotlin.idea.debugger.evaluate
|
||||
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiRecursiveElementVisitor
|
||||
import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.compile.CodeFragmentCapturedValue
|
||||
@@ -22,12 +25,12 @@ import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.GENERATED_CLASS_NAME
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.GENERATED_FUNCTION_NAME
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.compilation.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.NameUtils
|
||||
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||
import org.jetbrains.kotlin.psi.KtOperationReferenceExpression
|
||||
import java.util.concurrent.ExecutionException
|
||||
|
||||
private class IncorrectCodeFragmentException(message: String) : EvaluateException(message)
|
||||
|
||||
interface KotlinCodeFragmentCompiler {
|
||||
fun compileCodeFragment(context: ExecutionContext, codeFragment: KtCodeFragment): CompiledCodeFragmentData
|
||||
|
||||
@@ -61,7 +64,8 @@ class K2KotlinCodeFragmentCompiler : KotlinCodeFragmentCompiler {
|
||||
|
||||
stats.compilerFailExceptionClass = extractExceptionCauseClass(e)
|
||||
|
||||
onFinish(if (cause is IncorrectCodeFragmentException) EvaluationCompilerResult.COMPILATION_FAILURE
|
||||
val isJustInvalidUserCode = cause is IncorrectCodeFragmentException
|
||||
onFinish(if (isJustInvalidUserCode) EvaluationCompilerResult.COMPILATION_FAILURE
|
||||
else EvaluationCompilerResult.COMPILER_INTERNAL_ERROR)
|
||||
throw e
|
||||
}
|
||||
@@ -172,3 +176,17 @@ fun isCodeFragmentClassPath(path: String): Boolean {
|
||||
@KaExperimentalApi
|
||||
val KaCompiledFile.isCodeFragmentClassFile: Boolean
|
||||
get() = isCodeFragmentClassPath(path)
|
||||
|
||||
fun hasCastOperator(codeFragment: KtCodeFragment): Boolean {
|
||||
var result = false
|
||||
runReadAction {
|
||||
codeFragment.accept(object : PsiRecursiveElementVisitor() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
if (result) return
|
||||
result = element is KtOperationReferenceExpression && element.operationSignTokenType == KtTokens.AS_KEYWORD
|
||||
super.visitElement(element)
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ object KotlinDebuggerEvaluatorStatisticsCollector : CounterUsagesCollector() {
|
||||
|
||||
override fun getGroup(): EventLogGroup = GROUP
|
||||
|
||||
private val GROUP = EventLogGroup("kotlin.debugger.evaluator", 8)
|
||||
private val GROUP = EventLogGroup("kotlin.debugger.evaluator", 9)
|
||||
|
||||
// fields
|
||||
private val compilerField = EventFields.Enum<CompilerType>("compiler")
|
||||
@@ -39,7 +39,7 @@ object KotlinDebuggerEvaluatorStatisticsCollector : CounterUsagesCollector() {
|
||||
wrapTimeMsField, analysisTimeMsField, compilationTimeMsField, wholeTimeField, interruptionsField, compilerExceptionField
|
||||
)
|
||||
// no need to record evaluation time, as it reflects what user evaluates, not how effective our evaluation is
|
||||
private val evaluationEvent = GROUP.registerEvent("evaluation.result", resultField, compilerField)
|
||||
private val evaluationEvent = GROUP.registerEvent("evaluation.result", resultField, compilerField, originField)
|
||||
|
||||
@JvmStatic
|
||||
fun logAnalysisAndCompilationResult(
|
||||
@@ -60,8 +60,8 @@ object KotlinDebuggerEvaluatorStatisticsCollector : CounterUsagesCollector() {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
internal fun logEvaluationResult(project: Project?, evaluationResult: StatisticsEvaluationResult, compilerType: CompilerType) {
|
||||
evaluationEvent.log(project, evaluationResult, compilerType)
|
||||
internal fun logEvaluationResult(project: Project?, evaluationResult: StatisticsEvaluationResult, compilerType: CompilerType, origin: XEvaluationOrigin) {
|
||||
evaluationEvent.log(project, evaluationResult, compilerType, origin)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,18 @@ enum class EvaluationCompilerResult {
|
||||
}
|
||||
|
||||
enum class StatisticsEvaluationResult {
|
||||
SUCCESS, FAILURE
|
||||
SUCCESS,
|
||||
USER_EXCEPTION,
|
||||
|
||||
COMPILATION_FAILURE,
|
||||
COMPILER_INTERNAL_ERROR,
|
||||
UNCLASSIFIED_COMPILATION_PROBLEM,
|
||||
|
||||
UNCLASSIFIED_EVALUATION_PROBLEM,
|
||||
MISCOMPILED,
|
||||
ERROR_DURING_PARSING_EXCEPTION,
|
||||
WRONG_JVM_STATE,
|
||||
UNRELATED_EXCEPTION,
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.intellij.debugger.engine.evaluation.expression.*
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.diagnostic.Attachment
|
||||
import com.intellij.openapi.diagnostic.ControlFlowException
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.project.IndexNotReadyException
|
||||
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.idea.debugger.base.util.safeLocation
|
||||
import org.jetbrains.kotlin.idea.debugger.base.util.safeMethod
|
||||
import org.jetbrains.kotlin.idea.debugger.base.util.safeVisibleVariableByName
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.CoroutineStackFrameProxyImpl
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.GENERATED_CLASS_NAME
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.isEvaluationEntryPoint
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.compilation.*
|
||||
@@ -125,21 +127,58 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, private val sourcePositi
|
||||
}
|
||||
|
||||
private fun evaluateSafe(context: ExecutionContext, codeFragment: KtCodeFragment): Any? {
|
||||
val compiledData = getCompiledCodeFragment(context)
|
||||
val hasCast = hasCastOperator(codeFragment)
|
||||
val compiledData = try {
|
||||
getCompiledCodeFragment(context)
|
||||
} catch (e: Throwable) {
|
||||
if (e !is ProcessCanceledException) {
|
||||
val evaluationResultValue = when (e) {
|
||||
is IncorrectCodeFragmentException -> StatisticsEvaluationResult.COMPILATION_FAILURE
|
||||
is EvaluateException -> StatisticsEvaluationResult.COMPILER_INTERNAL_ERROR
|
||||
else -> StatisticsEvaluationResult.UNCLASSIFIED_COMPILATION_PROBLEM
|
||||
}
|
||||
KotlinDebuggerEvaluatorStatisticsCollector.logEvaluationResult(codeFragment.project, evaluationResultValue, CompilerType.K2, context.evaluationContext.origin)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
return try {
|
||||
runEvaluation(context, compiledData).also {
|
||||
KotlinDebuggerEvaluatorStatisticsCollector.logEvaluationResult(codeFragment.project, StatisticsEvaluationResult.SUCCESS, compiledData.compilerType)
|
||||
if (!compiledData.statisticReported) {
|
||||
compiledData.statisticReported = true
|
||||
KotlinDebuggerEvaluatorStatisticsCollector.logEvaluationResult(
|
||||
codeFragment.project,
|
||||
StatisticsEvaluationResult.SUCCESS,
|
||||
compiledData.compilerType,
|
||||
context.evaluationContext.origin
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
if (!isUnitTestMode()) {
|
||||
if (e !is EvaluateException && e !is Eval4JInterpretingException) {
|
||||
val cause = e.cause
|
||||
val errorType = when {
|
||||
e is ControlFlowException || e is IndexNotReadyException -> StatisticsEvaluationResult.UNRELATED_EXCEPTION
|
||||
e is Eval4JInterpretingException ->
|
||||
if (!hasCast && e.cause is ClassCastException) StatisticsEvaluationResult.MISCOMPILED
|
||||
else StatisticsEvaluationResult.USER_EXCEPTION
|
||||
e is EvaluateException && cause != null -> checkCauseOfEvaluateException(cause, hasCast)
|
||||
isSpecialException(e) -> StatisticsEvaluationResult.WRONG_JVM_STATE
|
||||
else -> StatisticsEvaluationResult.UNCLASSIFIED_EVALUATION_PROBLEM
|
||||
}
|
||||
|
||||
if (!compiledData.statisticReported) {
|
||||
if (errorType != StatisticsEvaluationResult.UNRELATED_EXCEPTION && errorType != StatisticsEvaluationResult.WRONG_JVM_STATE) {
|
||||
compiledData.statisticReported = true
|
||||
}
|
||||
KotlinDebuggerEvaluatorStatisticsCollector.logEvaluationResult(
|
||||
codeFragment.project,
|
||||
StatisticsEvaluationResult.FAILURE,
|
||||
compiledData.compilerType
|
||||
errorType,
|
||||
compiledData.compilerType,
|
||||
context.evaluationContext.origin
|
||||
)
|
||||
}
|
||||
|
||||
if (isApplicationInternalMode()) {
|
||||
reportErrorWithAttachments(context, codeFragment, e,
|
||||
prepareBytecodes(compiledData),
|
||||
@@ -150,6 +189,32 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, private val sourcePositi
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCauseOfEvaluateException(cause: Throwable, hasCast: Boolean): StatisticsEvaluationResult {
|
||||
if (cause is InvocationException) {
|
||||
try {
|
||||
val exceptionFromCodeFragment = cause.exception()
|
||||
val type = exceptionFromCodeFragment.type()
|
||||
if (type.signature().startsWith("Ljava/lang/invoke/") || type.isSubTypeOrSame("java.lang.ReflectiveOperationException")) {
|
||||
return StatisticsEvaluationResult.MISCOMPILED
|
||||
}
|
||||
if (type.isSubTypeOrSame("java.lang.ClassCastException")) {
|
||||
return if (hasCast) StatisticsEvaluationResult.USER_EXCEPTION else StatisticsEvaluationResult.MISCOMPILED
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
LOG.error("Can't extract error type from InvocationException", e)
|
||||
return StatisticsEvaluationResult.ERROR_DURING_PARSING_EXCEPTION
|
||||
}
|
||||
return StatisticsEvaluationResult.USER_EXCEPTION
|
||||
}
|
||||
|
||||
if (isSpecialException(cause)) {
|
||||
return StatisticsEvaluationResult.WRONG_JVM_STATE
|
||||
}
|
||||
|
||||
return StatisticsEvaluationResult.MISCOMPILED
|
||||
}
|
||||
|
||||
private fun prepareBytecodes(compiledData: CompiledCodeFragmentData): List<Pair<String, String>> {
|
||||
// TODO run javap, if found valid java home?
|
||||
val result = buildString {
|
||||
@@ -498,6 +563,10 @@ fun createCompiledDataDescriptor(result: CompilationResult): CompiledCodeFragmen
|
||||
fun evaluationException(msg: String): Nothing = throw EvaluateExceptionUtil.createEvaluateException(msg)
|
||||
fun evaluationException(e: Throwable): Nothing = throw EvaluateExceptionUtil.createEvaluateException(e)
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
class IncorrectCodeFragmentException(message: String) : EvaluateException(message)
|
||||
|
||||
enum class CompilerType {
|
||||
OLD, IR, K2
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@ internal sealed interface CompilationCodeFragmentResult
|
||||
|
||||
internal class FailedCompilationCodeFragment(val evaluateException: EvaluateException): CompilationCodeFragmentResult
|
||||
|
||||
data class CompiledCodeFragmentData(
|
||||
class CompiledCodeFragmentData(
|
||||
val classes: List<ClassToLoad>,
|
||||
val parameters: List<CodeFragmentParameter.Dumb>,
|
||||
val crossingBounds: Set<CodeFragmentParameter.Dumb>,
|
||||
val mainMethodSignature: MethodSignature,
|
||||
val compilerType: CompilerType
|
||||
val compilerType: CompilerType,
|
||||
var statisticReported: Boolean = false,
|
||||
): CompilationCodeFragmentResult {
|
||||
data class MethodSignature(val parameterTypes: List<Type>, val returnType: Type)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user