(CoroutineDebugger) kotlinx.coroutines.core:1.3.5 support / dynamicProxy(interface) classes

Original commit: be2185ec01c9fd6c250bd5a2452aae9873b5eeca

GitOrigin-RevId: 502e69fbe01dd8db411c3a772a1b479fc1d9b49a
This commit is contained in:
Vladimir Ilmov
2020-04-19 19:06:29 +02:00
committed by intellij-monorepo-bot
parent 8d7be35656
commit 874c72fba0
6 changed files with 161 additions and 20 deletions

View File

@@ -17,12 +17,12 @@ import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class CoroutineLibraryAgent2Proxy(private val executionContext: DefaultExecutionContext) :
CoroutineInfoProvider {
val log by logger
private val debugProbesImpl = DebugProbesImpl(executionContext)
private val debugProbesImpl = DebugProbesImpl.instance(executionContext)
private val locationCache = LocationCache(executionContext)
private val debugMetadata: DebugMetadata? = DebugMetadata.instance(executionContext)
override fun dumpCoroutinesInfo(): List<CoroutineInfoData> {
val result = debugProbesImpl.dumpCoroutinesInfo(executionContext)
val result = debugProbesImpl?.dumpCoroutinesInfo(executionContext) ?: emptyList()
return result.mapNotNull { mapToCoroutineInfoData(it) }
}
@@ -45,7 +45,7 @@ class CoroutineLibraryAgent2Proxy(private val executionContext: DefaultExecution
fun isInstalled(): Boolean {
try {
return debugProbesImpl.isInstalledValue ?: false
return debugProbesImpl?.isInstalledValue ?: false
} catch (e: Exception) {
log.error("Exception happened while checking agent status.", e)
return false

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror
import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
abstract class BaseDynamicMirror<T>(val value: ObjectReference, val name: String, val context: DefaultExecutionContext) {
val log by logger
val cls: ReferenceType? = value.referenceType()
fun makeField(fieldName: String): Field? =
cls?.let { it.fieldByName(fieldName) }
fun makeMethod(methodName: String): Method? =
cls?.let { it.methodsByName(methodName).single() }
fun makeMethod(methodName: String, signature: String): Method? =
cls?.let { it.methodsByName(methodName, signature).single() }
fun isCompatible(value: ObjectReference?) =
value?.let { it.referenceType().isSubTypeOrSame(name) } ?: false
fun mirror(): T? {
if (!isCompatible(value)) {
log.trace("Value ${value.referenceType()} is not compatible with $name.")
return null
} else
return fetchMirror(value, context)
}
fun staticObjectValue(fieldName: String): ObjectReference? {
val keyFieldRef = makeField(fieldName)
return cls?.let { it.getValue(keyFieldRef) as? ObjectReference }
}
fun staticMethodValue(instance: ObjectReference?, method: Method?, context: DefaultExecutionContext, vararg values: Value?) =
instance?.let {
method?.let { m ->
context.invokeMethod(it, m, values.asList()) as? ObjectReference
}
}
fun staticMethodValue(method: Method?, context: DefaultExecutionContext, vararg values: Value?) =
cls?.let {
method?.let {
if (cls is ClassType)
context.invokeMethodSafe(cls, method, values.asList()) as? ObjectReference
else
null
}
}
fun stringValue(value: ObjectReference, field: Field?) =
field?.let {
(value.getValue(it) as? StringReference)?.value()
}
fun byteValue(value: ObjectReference, field: Field?) =
field?.let {
(value.getValue(it) as? ByteValue)?.value()
}
fun threadValue(value: ObjectReference, field: Field?) =
field?.let {
value.getValue(it) as? ThreadReference
}
fun stringValue(value: ObjectReference, method: Method?, context: DefaultExecutionContext) =
method?.let {
(context.invokeMethod(value, it, emptyList()) as? StringReference)?.value()
}
fun objectValue(value: ObjectReference?, method: Method?, context: DefaultExecutionContext, vararg values: Value) =
value?.let {
method?.let {
context.invokeMethodAsObject(value, method, *values)
}
}
fun longValue(value: ObjectReference, method: Method?, context: DefaultExecutionContext, vararg values: Value) =
method?.let { (context.invokeMethod(value, it, values.asList()) as? LongValue)?.longValue() }
fun intValue(value: ObjectReference, method: Method?, context: DefaultExecutionContext, vararg values: Value) =
method?.let { (context.invokeMethod(value, it, values.asList()) as? IntegerValue)?.intValue() }
fun booleanValue(value: ObjectReference?, method: Method?, context: DefaultExecutionContext, vararg values: Value): Boolean? {
value ?: return null
method ?: return null
return (context.invokeMethod(value, method, values.asList()) as? BooleanValue)?.booleanValue()
}
fun objectValue(value: ObjectReference, field: Field?) =
field?.let { value.getValue(it) as ObjectReference? }
fun intValue(value: ObjectReference, field: Field?) =
field?.let { (value.getValue(it) as? IntegerValue)?.intValue() }
fun longValue(value: ObjectReference, field: Field?) =
field?.let { (value.getValue(it) as? LongValue)?.longValue() }
fun booleanValue(value: ObjectReference?, field: Field?) =
field?.let { (value?.getValue(field) as? BooleanValue)?.booleanValue() }
protected abstract fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): T?
}

View File

@@ -67,8 +67,8 @@ class CoroutineId(context: DefaultExecutionContext) : ContextKey<Long>("kotlinx.
override fun key() = key
}
class Job(context: DefaultExecutionContext) : ContextKey<ObjectReference>("kotlinx.coroutines.Job", context) {
val key = staticObjectValue("Key")
class Job(context: DefaultExecutionContext) : ContextKey<ObjectReference>("kotlinx.coroutines.Job\$Key", context) {
val key = staticObjectValue("\$\$INSTANCE")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): ObjectReference? {
return value

View File

@@ -7,16 +7,18 @@ package org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror
import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class DebugProbesImpl internal constructor(context: DefaultExecutionContext) :
class DebugProbesImpl private constructor(context: DefaultExecutionContext) :
BaseMirror<MirrorOfDebugProbesImpl>("kotlinx.coroutines.debug.internal.DebugProbesImpl", context) {
val javaLangListMirror = JavaUtilAbstractCollection(context)
val stackTraceElement = StackTraceElement(context)
val coroutineInfo = CoroutineInfo(this, context)
val coroutineInfo = CoroutineInfo.instance(this, context) ?: throw IllegalStateException("CoroutineInfo implementation not found.")
val debugProbesCoroutineOwner = DebugProbesImpl_CoroutineOwner(coroutineInfo, context)
val instance = staticObjectValue("INSTANCE")
val isInstalledMethod = makeMethod("isInstalled\$kotlinx_coroutines_debug", "()Z")
?: makeMethod("isInstalled\$kotlinx_coroutines_core", "()Z") ?: throw IllegalStateException("isInstalledMethod not found")
val isInstalledValue = booleanValue(instance, isInstalledMethod, context)
val enhanceStackTraceWithThreadDumpMethod = makeMethod("enhanceStackTraceWithThreadDump")
val dumpMethod = makeMethod("dumpCoroutinesInfo", "()Ljava/util/List;")
@@ -49,10 +51,13 @@ class DebugProbesImpl internal constructor(context: DefaultExecutionContext) :
}
companion object {
val log by logger
fun instance(context: DefaultExecutionContext) =
try {
DebugProbesImpl(context)
} catch (e: IllegalStateException) {
log.warn("Attempt to access DebugProbesImpl", e)
null
}
}
@@ -79,12 +84,15 @@ data class MirrorOfCoroutineOwner(val that: ObjectReference, val coroutineInfo:
data class MirrorOfDebugProbesImpl(val that: ObjectReference, val instance: ObjectReference?, val isInstalled: Boolean?)
class CoroutineInfo(val debugProbesImplMirror: DebugProbesImpl, context: DefaultExecutionContext) :
BaseMirror<MirrorOfCoroutineInfo>("kotlinx.coroutines.debug.CoroutineInfo", context) {
class CoroutineInfo private constructor(
val debugProbesImplMirror: DebugProbesImpl,
context: DefaultExecutionContext,
val className: String = AGENT_134_CLASS_NAME
) :
BaseMirror<MirrorOfCoroutineInfo>(className, context) {
val javaLangMirror = JavaLangMirror(context)
val javaLangListMirror = JavaUtilAbstractCollection(context)
private val coroutineContextMirror = CoroutineContext(context)
private val coroutineStackFrameMirror = CoroutineStackFrame(context)
private val stackTraceElement = StackTraceElement(context)
private val contextFieldRef = makeField("context")
private val creationStackBottom = makeField("creationStackBottom")
@@ -96,13 +104,30 @@ class CoroutineInfo(val debugProbesImplMirror: DebugProbesImpl, context: Default
private val lastObservedFrameField = makeField("lastObservedFrame")
private val lastObservedThreadField = makeField("lastObservedThread")
companion object {
val log by logger
const val AGENT_134_CLASS_NAME = "kotlinx.coroutines.debug.CoroutineInfo"
const val AGENT_135_AND_UP_CLASS_NAME = "kotlinx.coroutines.debug.internal.DebugCoroutineInfo"
fun instance(debugProbesImplMirror: DebugProbesImpl, context: DefaultExecutionContext): CoroutineInfo? {
val classType = context.findClassSafe(AGENT_134_CLASS_NAME) ?: context.findClassSafe(AGENT_135_AND_UP_CLASS_NAME) ?: return null
try {
return CoroutineInfo(debugProbesImplMirror, context, classType.name())
} catch (e: IllegalStateException) {
log.warn("coroutine-debugger: $classType not found", e)
return null
}
}
}
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCoroutineInfo {
val state = objectValue(value, stateMethod, context)?.let {
stringValue(it, javaLangMirror.toString, context)
}
val coroutineContext = coroutineContextMirror.mirror(objectValue(value, contextFieldRef), context)
val creationStackBottomObjectReference = objectValue(value, creationStackBottom)
val creationStackBottom = coroutineStackFrameMirror.mirror(creationStackBottomObjectReference, context)
val creationStackBottom =
creationStackBottomObjectReference?.let { CoroutineStackFrame(creationStackBottomObjectReference, context).mirror() }
val sequenceNumber = longValue(value, sequenceNumberField)
val creationStackTraceList = objectValue(value, creationStackTraceMethod, context)
val creationStackTraceMirror = javaLangListMirror.mirror(creationStackTraceList, context)
@@ -127,9 +152,9 @@ class CoroutineInfo(val debugProbesImplMirror: DebugProbesImpl, context: Default
lastObservedFrame
)
}
}
data class MirrorOfCoroutineInfo(
val that: ObjectReference,
val context: MirrorOfCoroutineContext?,
@@ -142,8 +167,8 @@ data class MirrorOfCoroutineInfo(
val lastObservedFrame: ObjectReference?
)
class CoroutineStackFrame(context: DefaultExecutionContext) :
BaseMirror<MirrorOfCoroutineStackFrame>("kotlin.coroutines.jvm.internal.CoroutineStackFrame", context) {
class CoroutineStackFrame(value: ObjectReference, context: DefaultExecutionContext) :
BaseDynamicMirror<MirrorOfCoroutineStackFrame>(value, "kotlin.coroutines.jvm.internal.CoroutineStackFrame", context) {
private val stackTraceElementMirror = StackTraceElement(context)
private val callerFrameMethod = makeMethod("getCallerFrame")
private val getStackTraceElementMethod = makeMethod("getStackTraceElement")
@@ -151,7 +176,7 @@ class CoroutineStackFrame(context: DefaultExecutionContext) :
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCoroutineStackFrame? {
val objectReference = objectValue(value, callerFrameMethod, context)
val callerFrame = if (objectReference is ObjectReference)
this.mirror(objectReference, context) else null
CoroutineStackFrame(objectReference, context).mirror() else null
val stackTraceElementReference = objectValue(value, getStackTraceElementMethod, context)
val stackTraceElement =
if (stackTraceElementReference is ObjectReference) stackTraceElementMirror.mirror(stackTraceElementReference, context) else null

View File

@@ -9,20 +9,20 @@ import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
import java.lang.IllegalStateException
abstract class BaseMirror<T>(val name: String, context: DefaultExecutionContext) {
val log by logger
protected val cls = context.findClassSafe(name)
protected val cls = context.findClassSafe(name) ?: throw IllegalStateException("coroutine-debugger: class $name not found.")
fun makeField(fieldName: String): Field? =
cls?.let { it.fieldByName(fieldName) }
fun makeMethod(methodName: String): Method? =
cls?.let { it.methodsByName(methodName).single() }
cls?.let { it.methodsByName(methodName).singleOrNull() }
fun makeMethod(methodName: String, signature: String): Method? =
cls?.let { it.methodsByName(methodName, signature).single() }
cls?.let { it.methodsByName(methodName, signature).singleOrNull() }
fun isCompatible(value: ObjectReference?) =
value?.let { it.referenceType().isSubTypeOrSame(name) } ?: false

View File

@@ -10,9 +10,10 @@ import com.sun.jdi.ClassType
import com.sun.jdi.ObjectReference
import com.sun.jdi.StringReference
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isBaseContinuationImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class DebugMetadata internal constructor(context: DefaultExecutionContext) :
class DebugMetadata private constructor(context: DefaultExecutionContext) :
BaseMirror<MirrorOfDebugProbesImpl>("kotlin.coroutines.jvm.internal.DebugMetadataKt", context) {
private val getStackTraceElementMethod = makeMethod("getStackTraceElement")
private val getSpilledVariableFieldMappingMethod =
@@ -42,10 +43,13 @@ class DebugMetadata internal constructor(context: DefaultExecutionContext) :
staticMethodValue(getSpilledVariableFieldMappingMethod, context, value) as? ArrayReference
companion object {
val log by logger
fun instance(context: DefaultExecutionContext) =
try {
DebugMetadata(context)
} catch (e: IllegalStateException) {
log.warn("Attempt to access DebugMetadata", e)
null
}
}