(CoroutineDebugger) Minor issued fixed, log level adjusted for missing classes in JVM.

This commit is contained in:
Vladimir Ilmov
2020-04-22 13:56:36 +02:00
parent eec3263518
commit 9b0e37f627
31 changed files with 191 additions and 447 deletions
@@ -4,7 +4,6 @@
*/
package org.jetbrains.kotlin.idea.debugger.coroutine;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.RunConfigurationExtension;
import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.configurations.RunConfigurationBase;
@@ -19,7 +18,7 @@ public class CoroutineDebugConfigurationExtension extends RunConfigurationExtens
@Override
public <T extends RunConfigurationBase> void updateJavaParameters(
@NotNull T configuration, @NotNull JavaParameters params, RunnerSettings runnerSettings
T configuration, @NotNull JavaParameters params, RunnerSettings runnerSettings
) {
if (configuration != null) {
Project project = configuration.getProject();
@@ -5,11 +5,9 @@
package org.jetbrains.kotlin.idea.debugger.coroutine
import org.jetbrains.annotations.NonNls
class CoroutineDebuggerContentInfo {
companion object {
val XCOROUTINE_THREADS_CONTENT = "XCoroutineThreadsContent"
val XCOROUTINE_POPUP_ACTION_GROUP = "Kotlin.XDebugger.Actions"
const val XCOROUTINE_THREADS_CONTENT = "XCoroutineThreadsContent"
const val XCOROUTINE_POPUP_ACTION_GROUP = "Kotlin.XDebugger.Actions"
}
}
@@ -7,16 +7,12 @@ package org.jetbrains.kotlin.idea.debugger.coroutine
import com.intellij.debugger.actions.AsyncStacksToggleAction
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.openapi.application.Application
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.impl.XDebugSessionImpl
import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.StackFrameInterceptor
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ContinuationHolder
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.SkipCoroutineStackFrameProxyImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CoroutineFrameBuilder
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isInUnitTest
@@ -45,12 +45,15 @@ class DebuggerConnection(
val kotlinxCoroutinesCore = params.classPath?.pathList?.firstOrNull { it.contains("kotlinx-coroutines-core") }
val kotlinxCoroutinesDebug = params.classPath?.pathList?.firstOrNull { it.contains("kotlinx-coroutines-debug") }
val mode = if (kotlinxCoroutinesDebug != null) {
CoroutineDebuggerMode.VERSION_UP_TO_1_3_5
} else if (kotlinxCoroutinesCore != null) {
determineCoreVersionMode(kotlinxCoroutinesCore)
} else
CoroutineDebuggerMode.DISABLED
val mode = when {
kotlinxCoroutinesDebug != null -> {
CoroutineDebuggerMode.VERSION_UP_TO_1_3_5
}
kotlinxCoroutinesCore != null -> {
determineCoreVersionMode(kotlinxCoroutinesCore)
}
else -> CoroutineDebuggerMode.DISABLED
}
when (mode) {
CoroutineDebuggerMode.VERSION_1_3_6_AND_UP -> initializeCoroutineAgent(params, kotlinxCoroutinesCore)
@@ -62,12 +65,12 @@ class DebuggerConnection(
}
private fun determineCoreVersionMode(kotlinxCoroutinesCore: String): CoroutineDebuggerMode {
val regex = Regex(""".+\Wkotlinx\-coroutines\-core\-(.+)?\.jar""")
val regex = Regex(""".+\Wkotlinx-coroutines-core-(.+)?\.jar""")
val matchResult = regex.matchEntire(kotlinxCoroutinesCore) ?: return CoroutineDebuggerMode.DISABLED
val coroutinesCoreVersion = DefaultArtifactVersion(matchResult.groupValues.get(1))
val coroutinesCoreVersion = DefaultArtifactVersion(matchResult.groupValues[1])
val versionToCompareTo = DefaultArtifactVersion("1.3.5")
return if (versionToCompareTo.compareTo(coroutinesCoreVersion) < 0)
return if (versionToCompareTo < coroutinesCoreVersion)
CoroutineDebuggerMode.VERSION_1_3_6_AND_UP
else
CoroutineDebuggerMode.DISABLED
@@ -24,9 +24,9 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.text.DateFormatUtil
import com.intellij.xdebugger.impl.XDebuggerManagerImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import org.jetbrains.kotlin.idea.debugger.coroutine.view.CoroutineDumpPanel
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.CoroutineDebugProbesProxy
import org.jetbrains.kotlin.idea.debugger.coroutine.view.CoroutineDumpPanel
@Suppress("ComponentNotRegistered")
class CoroutineDumpAction : AnAction(), AnAction.TransparentUpdate {
@@ -49,7 +49,7 @@ class CoroutineDumpAction : AnAction(), AnAction.TransparentUpdate {
ApplicationManager.getApplication().invokeLater(f, ModalityState.NON_MODAL)
} else {
val message = KotlinDebuggerCoroutinesBundle.message("coroutine.dump.failed")
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(message,MessageType.ERROR).notify(project)
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(message, MessageType.ERROR).notify(project)
}
}
})
@@ -12,13 +12,12 @@ import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl
import com.intellij.openapi.project.Project
import com.sun.jdi.ObjectReference
import com.sun.jdi.Value
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ContinuationHolder
class ContinuationValueDescriptorImpl(
project: Project,
val continuation: ObjectReference,
val fieldName: String,
val variableName: String
private val variableName: String
) : ValueDescriptorImpl(project) {
override fun calcValueName() = variableName
@@ -9,7 +9,7 @@ import com.sun.jdi.ObjectReference
import com.sun.jdi.ThreadReference
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData.Companion.DEFAULT_COROUTINE_NAME
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData.Companion.DEFAULT_COROUTINE_STATE
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.*
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.MirrorOfCoroutineInfo
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
/**
@@ -31,9 +31,9 @@ data class CoroutineInfoData(
fun isRunning() = key.state == State.RUNNING
fun topRestoredFrame() = stackTrace.firstOrNull()
private fun topRestoredFrame() = stackTrace.firstOrNull()
fun topFrameVariables() = stackTrace.firstOrNull()?.spilledVariables ?: emptyList()
fun topFrameVariables() = topRestoredFrame()?.spilledVariables ?: emptyList()
fun restoredStackTrace(mode: SuspendExitMode): List<CoroutineStackFrameItem> =
if (stackTrace.isNotEmpty() && stackTrace.first().isInvokeSuspend())
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBund
import javax.swing.Icon
@Deprecated("moved to XCoroutineView")
class CoroutineDescriptorImpl(val infoData: CoroutineInfoData) : NodeDescriptorImpl() {
class CoroutineDescriptorImpl(private val infoData: CoroutineInfoData) : NodeDescriptorImpl() {
lateinit var icon: Icon
override fun getName() = infoData.key.name
@@ -74,10 +74,9 @@ class SuspendStackFrameDescriptor(
}
}
override fun getName() : String? = frame.methodName
override fun getName(): String? = frame.methodName
}
/**
* For the case when no data inside frame is available
*/
@@ -10,16 +10,11 @@ import com.intellij.debugger.jdi.LocalVariableProxyImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
import com.intellij.ui.ColoredTextContainer
import com.intellij.ui.SimpleTextAttributes
import com.intellij.xdebugger.frame.XCompositeNode
import com.intellij.xdebugger.frame.XNamedValue
import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.frame.XValueChildrenList
import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.SkipCoroutineStackFrameProxyImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.util.coroutineDebuggerTraceEnabled
import org.jetbrains.kotlin.idea.debugger.invokeInManagerThread
import org.jetbrains.kotlin.idea.debugger.stackFrame.KotlinStackFrame
@@ -32,10 +27,10 @@ import org.jetbrains.kotlin.idea.debugger.stackFrame.KotlinStackFrame
class CoroutinePreflightStackFrame(
val coroutineInfoData: CoroutineInfoData,
val stackFrameDescriptorImpl: StackFrameDescriptorImpl,
private val stackFrameDescriptorImpl: StackFrameDescriptorImpl,
val threadPreCoroutineFrames: List<StackFrameProxyImpl>,
val mode: SuspendExitMode,
val firstFrameVariables: List<XNamedValue> = coroutineInfoData.topFrameVariables()
private val firstFrameVariables: List<XNamedValue> = coroutineInfoData.topFrameVariables()
) : KotlinStackFrame(stackFrameDescriptorImpl), JVMStackFrameInfoProvider {
override fun computeChildren(node: XCompositeNode) {
@@ -49,7 +44,7 @@ class CoroutinePreflightStackFrame(
override fun getVisibleVariables(): List<LocalVariableProxyImpl> {
// skip restored variables
return super.getVisibleVariables().filter { v -> ! firstFrameVariables.any { it.name == v.name() } }
return super.getVisibleVariables().filter { v -> !firstFrameVariables.any { it.name == v.name() } }
}
override fun isInLibraryContent() = false
@@ -77,7 +72,7 @@ class CreationCoroutineStackFrame(debugProcess: DebugProcessImpl, item: StackFra
true
}
open class CoroutineStackFrame(debugProcess: DebugProcessImpl, val item: StackFrameItem, val realStackFrame: XStackFrame? = null) :
open class CoroutineStackFrame(debugProcess: DebugProcessImpl, val item: StackFrameItem, private val realStackFrame: XStackFrame? = null) :
StackFrameItem.CapturedStackFrame(debugProcess, item) {
override fun computeChildren(node: XCompositeNode) {
@@ -6,10 +6,11 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.debugger.engine.JavaValue
import com.sun.jdi.*
import com.sun.jdi.ObjectReference
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isAbstractCoroutine
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class ContinuationHolder private constructor(val context: DefaultExecutionContext) {
@@ -46,8 +47,8 @@ class ContinuationHolder private constructor(val context: DefaultExecutionContex
val ci = debugProbesImpl?.getCoroutineInfo(input, context)
if (ci != null) {
if (ci.creationStackTrace != null)
for (index in 0 until ci.creationStackTrace.size) {
val frame = ci.creationStackTrace.get(index)
for (index in ci.creationStackTrace.indices) {
val frame = ci.creationStackTrace[index]
val ste = frame.stackTraceElement()
val location = locationCache.createLocation(ste)
creationStackTrace.add(CreationCoroutineStackFrameItem(ste, location, index == 0))
@@ -64,14 +65,14 @@ class ContinuationHolder private constructor(val context: DefaultExecutionContex
fun state(value: ObjectReference?): CoroutineNameIdState? {
value ?: return null
val reference = JavaLangMirror(context)
val standaloneCoroutine = StandaloneCoroutine(context)
val standaloneCoroutine = StandaloneCoroutine.instance(context) ?: return null
val standAloneCoroutineMirror = standaloneCoroutine.mirror(value, context)
if (standAloneCoroutineMirror?.context is MirrorOfCoroutineContext) {
val id = standAloneCoroutineMirror.context.id
val name = standAloneCoroutineMirror.context.name ?: CoroutineInfoData.DEFAULT_COROUTINE_NAME
val toString = reference.string(value, context)
// trying to get coroutine information by calling JobSupport.toString(), ${nameString()}{${stateString(state)}}@$hexAddress
val r = """\w+\{(\w+)\}\@([\w\d]+)""".toRegex()
val r = """\w+\{(\w+)}@([\w\d]+)""".toRegex()
val matcher = r.toPattern().matcher(toString)
if (matcher.matches()) {
val state = stateOf(matcher.group(1))
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.debugger.engine.DebuggerManagerThreadImpl
import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.openapi.util.registry.Registry
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CoroutineFrameBuilder
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoCache
import org.jetbrains.kotlin.idea.debugger.coroutine.util.executionContext
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
@@ -6,7 +6,10 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.xdebugger.frame.XNamedValue
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineNameIdState
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CreationCoroutineStackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.data.SuspendCoroutineStackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.DebugMetadata
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.DebugProbesImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.MirrorOfCoroutineInfo
@@ -26,16 +29,16 @@ class CoroutineLibraryAgent2Proxy(private val executionContext: DefaultExecution
return result.mapNotNull { mapToCoroutineInfoData(it) }
}
fun mapToCoroutineInfoData(mirror: MirrorOfCoroutineInfo): CoroutineInfoData? {
val cnis = CoroutineNameIdState.instance(mirror)
val stackTrace = mirror.enchancedStackTrace?.mapNotNull { it.stackTraceElement() } ?: emptyList()
private fun mapToCoroutineInfoData(mirror: MirrorOfCoroutineInfo): CoroutineInfoData? {
val coroutineNameIdState = CoroutineNameIdState.instance(mirror)
val stackTrace = mirror.enhancedStackTrace?.mapNotNull { it.stackTraceElement() } ?: emptyList()
val variables: List<XNamedValue> = mirror.lastObservedFrame?.let {
val spilledVariables = debugMetadata?.baseContinuationImpl?.mirror(it, executionContext)
spilledVariables?.spilledValues(executionContext)
} ?: emptyList()
var stackFrames = findStackFrames(stackTrace, variables)
val stackFrames = findStackFrames(stackTrace, variables)
return CoroutineInfoData(
cnis,
coroutineNameIdState,
stackFrames.restoredStackFrames,
stackFrames.creationStackFrames,
mirror.lastObservedThread,
@@ -44,11 +47,11 @@ class CoroutineLibraryAgent2Proxy(private val executionContext: DefaultExecution
}
fun isInstalled(): Boolean {
try {
return debugProbesImpl?.isInstalledValue ?: false
return try {
debugProbesImpl?.isInstalledValue ?: false
} catch (e: Exception) {
log.error("Exception happened while checking agent status.", e)
return false
false
}
}
@@ -74,10 +77,10 @@ class CoroutineLibraryAgent2Proxy(private val executionContext: DefaultExecution
companion object {
fun instance(executionContext: DefaultExecutionContext): CoroutineLibraryAgent2Proxy? {
val agentProxy = CoroutineLibraryAgent2Proxy(executionContext)
if (agentProxy.isInstalled())
return agentProxy
return if (agentProxy.isInstalled())
agentProxy
else
return null
null
}
}
@@ -1,190 +0,0 @@
/*
* 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
import com.intellij.debugger.engine.evaluation.EvaluateException
import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.CoroutineContext
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.JavaLangMirror
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isCreationSeparatorFrame
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class CoroutineLibraryAgentProxy(private val debugProbesClsRef: ClassType, private val executionContext: DefaultExecutionContext) :
CoroutineInfoProvider {
private val coroutineContextReference: JavaLangMirror = JavaLangMirror(executionContext)
private val debugProbesImplClsRef = executionContext.findClass("$DEBUG_PACKAGE.internal.DebugProbesImpl") as ClassType
private val debugProbesImplInstance = with(debugProbesImplClsRef) { getValue(fieldByName("INSTANCE")) as ObjectReference }
private val enhanceStackTraceWithThreadDumpRef: Method = debugProbesImplClsRef
.methodsByName("enhanceStackTraceWithThreadDump").single()
private val dumpMethod: Method = debugProbesClsRef.concreteMethodByName("dumpCoroutinesInfo", "()Ljava/util/List;")
val instance = with(debugProbesClsRef) { getValue(fieldByName("INSTANCE")) as ObjectReference }
// CoroutineInfo
private val coroutineInfoClsRef = executionContext.findClass("$DEBUG_PACKAGE.CoroutineInfo") as ClassType
private val getStateRef: Method = coroutineInfoClsRef.concreteMethodByName("getState", "()Lkotlinx/coroutines/debug/State;")
private val getContextRef: Method = coroutineInfoClsRef.concreteMethodByName("getContext", "()Lkotlin/coroutines/CoroutineContext;")
private val lastObservedStackTraceRef: Method = coroutineInfoClsRef.methodsByName("lastObservedStackTrace").single()
private val sequenceNumberFieldRef: Field = coroutineInfoClsRef.fieldByName("sequenceNumber")
private val lastObservedThreadFieldRef: Field = coroutineInfoClsRef.fieldByName("lastObservedThread")
private val lastObservedFrameFieldRef: Field = coroutineInfoClsRef.fieldByName("lastObservedFrame") // continuation
// value
private val vm = executionContext.vm
private val locationCache = LocationCache(executionContext)
private val coroutineContext: CoroutineContext = CoroutineContext(executionContext)
@Synchronized
@Suppress("unused")
fun install() =
executionContext.invokeMethodAsVoid(instance, "install")
@Synchronized
@Suppress("unused")
fun uninstall() =
executionContext.invokeMethodAsVoid(instance, "uninstall")
override fun dumpCoroutinesInfo(): List<CoroutineInfoData> {
val coroutinesInfo = executionContext.invokeMethodAsObject(instance, dumpMethod) ?: return emptyList()
executionContext.keepReference(coroutinesInfo)
val size = coroutineContextReference.sizeOf(coroutinesInfo, executionContext)
return MutableList(size) {
val elem = coroutineContextReference.elementFromList(coroutinesInfo, it, executionContext)
fetchCoroutineState(elem)
}
}
private fun fetchCoroutineState(instance: ObjectReference): CoroutineInfoData {
val name = getName(instance)
val state = getState(instance)
val thread = getLastObservedThread(instance, lastObservedThreadFieldRef)
val lastObservedFrameFieldRef = instance.getValue(lastObservedFrameFieldRef) as? ObjectReference
val stackTrace = getStackTrace(instance)
val creationFrameSeparatorIndex = findCreationFrameIndex(stackTrace)
val coroutineStackTrace = stackTrace.take(creationFrameSeparatorIndex)
val coroutineStackTraceFrameItems = coroutineStackTrace.map {
SuspendCoroutineStackFrameItem(it, locationCache.createLocation(it))
}
val creationStackTrace = stackTrace.subList(creationFrameSeparatorIndex + 1, stackTrace.size)
val creationStackTraceFrameItems = creationStackTrace.mapIndexed { index, stackTraceElement ->
CreationCoroutineStackFrameItem(stackTraceElement, locationCache.createLocation(stackTraceElement), index == 0)
}
val key = CoroutineNameIdState(name, "", State.valueOf(state), "")
return CoroutineInfoData(
key,
coroutineStackTraceFrameItems.toMutableList(),
creationStackTraceFrameItems,
thread,
lastObservedFrameFieldRef
)
}
/**
* Tries to find creation frame separator if any, returns last index if none found
*/
private fun findCreationFrameIndex(frames: List<StackTraceElement>): Int {
val index = frames.indexOfFirst { it.isCreationSeparatorFrame() }
return if (index < 0)
frames.lastIndex
else
index
}
private fun getName(
info: ObjectReference // CoroutineInfo instance
): String {
// equals to `coroutineInfo.context.get(CoroutineName).name`
val coroutineContextInst = executionContext.invokeMethod(
info,
getContextRef,
emptyList()
) as? ObjectReference ?: throw IllegalArgumentException("Coroutine context must not be null")
val context = coroutineContext.mirror(coroutineContextInst, executionContext)
val name = context?.name ?: "coroutine"
val id = (info.getValue(sequenceNumberFieldRef) as LongValue).value()
return "$name#$id"
}
private fun getState(
info: ObjectReference // CoroutineInfo instance
): String {
// equals to `stringState = coroutineInfo.state.toString()`
val state = executionContext.invokeMethod(info, getStateRef, emptyList()) as ObjectReference
return coroutineContextReference.string(state, executionContext)
}
private fun getLastObservedThread(
info: ObjectReference, // CoroutineInfo instance
threadRef: Field // reference to lastObservedThread
): ThreadReference? = info.getValue(threadRef) as? ThreadReference
/**
* Returns list of stackTraceElements for the given CoroutineInfo's [ObjectReference]
*/
private fun getStackTrace(
info: ObjectReference
): List<StackTraceElement> {
val frameList = lastObservedStackTrace(info)
// val tmpList = mutableListOf<StackTraceElement>()
// val sizeOfFrameList = coroutineContextReference.sizeOf(frameList, executionContext)
// for (it in 0 until sizeOfFrameList) {
// val frame = coroutineContextReference.elementFromList(frameList, it, executionContext)
// val ste = coroutineContextReference.stackTraceElement(frame)
// tmpList.add(ste)
// }
val mergedFrameList = enhanceStackTraceWithThreadDump(listOf(info, frameList))
val sizeOfMergedFrameList = coroutineContextReference.sizeOf(mergedFrameList, executionContext)
val list = mutableListOf<StackTraceElement>()
for (it in 0 until sizeOfMergedFrameList) {
val frame = coroutineContextReference.elementFromList(mergedFrameList, it, executionContext)
val ste = coroutineContextReference.stackTraceElement(frame)
list.add(// 0, // add in the beginning // @TODO what's the point?
ste
)
}
return list
}
private fun lastObservedStackTrace(instance: ObjectReference) =
executionContext.invokeMethod(instance, lastObservedStackTraceRef, emptyList()) as ObjectReference
private fun enhanceStackTraceWithThreadDump(args: List<ObjectReference>) =
executionContext.invokeMethod(
debugProbesImplInstance,
enhanceStackTraceWithThreadDumpRef, args
) as ObjectReference
companion object {
private const val DEBUG_PACKAGE = "kotlinx.coroutines.debug"
fun instance(executionContext: DefaultExecutionContext): CoroutineLibraryAgentProxy? {
try {
val debugProbesClsRef = executionContext.findClass("$DEBUG_PACKAGE.DebugProbes") ?: return null
if (debugProbesClsRef is ClassType) {
val instanceField = debugProbesClsRef.fieldByName("INSTANCE")
val debugProbes = debugProbesClsRef.getValue(instanceField) as ObjectReference
val f = debugProbes.referenceType().fieldByName("isInstalled")
val debugProbesActivated = if (f != null) (debugProbes.getValue(f) as BooleanValue).value() else true
if (debugProbesActivated)
return CoroutineLibraryAgentProxy(debugProbesClsRef, executionContext)
}
} catch (e: EvaluateException) {
}
return null
}
}
}
@@ -6,7 +6,8 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.openapi.util.registry.Registry
import com.sun.jdi.*
import com.sun.jdi.Field
import com.sun.jdi.ObjectReference
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.CancellableContinuationImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.util.findCancellableContinuationImplReferenceType
@@ -15,10 +16,10 @@ import org.jetbrains.kotlin.idea.debugger.coroutine.util.findDispatchedContinuat
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class CoroutineNoLibraryProxy(val executionContext: DefaultExecutionContext) : CoroutineInfoProvider {
class CoroutineNoLibraryProxy(private val executionContext: DefaultExecutionContext) : CoroutineInfoProvider {
val log by logger
val debugMetadataKtType = executionContext.findCoroutineMetadataType()
val holder = ContinuationHolder.instance(executionContext)
private val debugMetadataKtType = executionContext.findCoroutineMetadataType()
private val holder = ContinuationHolder.instance(executionContext)
override fun dumpCoroutinesInfo(): List<CoroutineInfoData> {
val vm = executionContext.vm
@@ -49,13 +50,13 @@ class CoroutineNoLibraryProxy(val executionContext: DefaultExecutionContext) : C
return false
}
fun extractCancellableContinuation(
private fun extractCancellableContinuation(
dispatchedContinuation: ObjectReference,
ccMirrorProvider: CancellableContinuationImpl
): CoroutineInfoData? {
val mirror = ccMirrorProvider.mirror(dispatchedContinuation, executionContext) ?: return null
val continuation = mirror.delegate?.continuation ?: return null
return holder?.extractCoroutineInfoData(continuation)
return holder.extractCoroutineInfoData(continuation)
}
private fun dispatchedContinuation(resultList: MutableList<CoroutineInfoData>): Boolean {
@@ -75,7 +76,7 @@ class CoroutineNoLibraryProxy(val executionContext: DefaultExecutionContext) : C
private fun extractDispatchedContinuation(dispatchedContinuation: ObjectReference, continuation: Field): CoroutineInfoData? {
debugMetadataKtType ?: return null
val initialContinuation = dispatchedContinuation.getValue(continuation) as ObjectReference
return holder?.extractCoroutineInfoData(initialContinuation)
return holder.extractCoroutineInfoData(initialContinuation)
}
}
@@ -50,15 +50,7 @@ class ManagerThreadExecutor(val debugProcess: DebugProcessImpl) {
}
class ApplicationThreadExecutor {
fun <T> readAction(f: () -> T): T {
return ApplicationManager.getApplication().runReadAction(Computable(f))
}
fun schedule(f: () -> Unit, component: Component) {
return ApplicationManager.getApplication().invokeLater({ f() }, ModalityState.stateForComponent(component))
}
fun schedule(f: () -> Unit) {
return ApplicationManager.getApplication().invokeLater { f() }
}
}
@@ -6,16 +6,6 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
import com.sun.jdi.Location
import com.sun.jdi.StackFrame
class LocationStackFrameProxyImpl(val location: Location, frame: StackFrameProxyImpl) :
StackFrameProxyImpl(frame.threadProxy(), frame.stackFrame, frame.indexFromBottom) {
override fun location(): Location {
return location
}
}
class SkipCoroutineStackFrameProxyImpl(frame: StackFrameProxyImpl) :
StackFrameProxyImpl(frame.threadProxy(), frame.stackFrame, frame.indexFromBottom)
@@ -13,26 +13,26 @@ 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()
private val cls: ReferenceType? = value.referenceType()
fun makeField(fieldName: String): Field? =
cls?.let { it.fieldByName(fieldName) }
private fun makeField(fieldName: String): Field? =
cls?.fieldByName(fieldName)
fun findMethod(methodName: String): Method? =
cls?.let { it.methodsByName(methodName).single() }
cls?.methodsByName(methodName)?.single()
fun findMethod(methodName: String, signature: String): Method? =
cls?.let { it.methodsByName(methodName, signature).single() }
cls?.methodsByName(methodName, signature)?.single()
fun isCompatible(value: ObjectReference?) =
value?.let { it.referenceType().isSubTypeOrSame(name) } ?: false
value?.referenceType()?.isSubTypeOrSame(name) ?: false
fun mirror(): T? {
if (!isCompatible(value)) {
return if (!isCompatible(value)) {
log.trace("Value ${value.referenceType()} is not compatible with $name.")
return null
null
} else
return fetchMirror(value, context)
fetchMirror(value, context)
}
fun staticObjectValue(fieldName: String): ObjectReference? {
@@ -5,18 +5,16 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror
import com.sun.jdi.Method
import com.sun.jdi.ObjectReference
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
import java.lang.StackTraceElement
class CoroutineContext(context: DefaultExecutionContext) :
BaseMirror<MirrorOfCoroutineContext>("kotlin.coroutines.CombinedContext", context) {
val coroutineNameRef = CoroutineName(context)
val coroutineIdRef = CoroutineId(context)
val jobRef = Job(context)
val dispatcherRef = CoroutineDispatcher(context)
val getContextElement = makeMethod("get")
private val coroutineNameRef = CoroutineName(context)
private val coroutineIdRef = CoroutineId(context)
private val jobRef = Job(context)
private val dispatcherRef = CoroutineDispatcher(context)
private val getContextElement = makeMethod("get")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCoroutineContext? {
val coroutineName = getElementValue(value, context, coroutineNameRef)
@@ -26,7 +24,7 @@ class CoroutineContext(context: DefaultExecutionContext) :
return MirrorOfCoroutineContext(value, coroutineName, coroutineId, dispatcher, job)
}
fun <T> getElementValue(value: ObjectReference, context: DefaultExecutionContext, keyProvider: ContextKey<T>): T? {
private fun <T> getElementValue(value: ObjectReference, context: DefaultExecutionContext, keyProvider: ContextKey<T>): T? {
val key = keyProvider.key() ?: return null
val elementValue = objectValue(value, getContextElement, context, key) ?: return null
return keyProvider.mirror(elementValue, context)
@@ -42,12 +40,12 @@ data class MirrorOfCoroutineContext(
)
abstract class ContextKey<T>(name: String, context: DefaultExecutionContext) : BaseMirror<T>(name, context) {
abstract fun key() : ObjectReference?
abstract fun key(): ObjectReference?
}
class CoroutineName(context: DefaultExecutionContext) : ContextKey<String>("kotlinx.coroutines.CoroutineName", context) {
val key = staticObjectValue("Key")
val getNameRef = makeMethod("getName")
private val getNameRef = makeMethod("getName")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): String? {
return stringValue(value, getNameRef, context)
@@ -57,8 +55,8 @@ class CoroutineName(context: DefaultExecutionContext) : ContextKey<String>("kotl
}
class CoroutineId(context: DefaultExecutionContext) : ContextKey<Long>("kotlinx.coroutines.CoroutineId", context) {
val key = staticObjectValue("Key")
val getIdRef = makeMethod("getId")
private val key = staticObjectValue("Key")
private val getIdRef = makeMethod("getId")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): Long? {
return longValue(value, getIdRef, context)
@@ -79,8 +77,8 @@ class Job(context: DefaultExecutionContext) : ContextKey<ObjectReference>("kotli
class CoroutineDispatcher(context: DefaultExecutionContext) : ContextKey<String>("kotlinx.coroutines.CoroutineDispatcher", context) {
val key = staticObjectValue("Key")
val jlm = JavaLangMirror(context)
private val key = staticObjectValue("Key")
private val jlm = JavaLangMirror(context)
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): String? {
return jlm.string(value, context)
@@ -5,29 +5,31 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror
import com.sun.jdi.*
import com.sun.jdi.ObjectReference
import com.sun.jdi.ThreadReference
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 private constructor(context: DefaultExecutionContext) :
BaseMirror<MirrorOfDebugProbesImpl>("kotlinx.coroutines.debug.internal.DebugProbesImpl", context) {
val javaLangListMirror = JavaUtilAbstractCollection(context)
val stackTraceElement = StackTraceElement(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")
private val javaLangListMirror = JavaUtilAbstractCollection(context)
private val stackTraceElement = StackTraceElement(context)
private val coroutineInfo =
CoroutineInfo.instance(this, context) ?: throw IllegalStateException("CoroutineInfo implementation not found.")
private val debugProbesCoroutineOwner = DebugProbesImplCoroutineOwner(coroutineInfo, context)
private val instance = staticObjectValue("INSTANCE")
private 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;")
private val enhanceStackTraceWithThreadDumpMethod = makeMethod("enhanceStackTraceWithThreadDump")
private val dumpMethod = makeMethod("dumpCoroutinesInfo", "()Ljava/util/List;")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfDebugProbesImpl? {
return MirrorOfDebugProbesImpl(value, instance, isInstalledValue)
}
fun enchanceStackTraceWithThreadDump(
fun enhanceStackTraceWithThreadDump(
context: DefaultExecutionContext,
coroutineInfo: ObjectReference,
lastObservedStackTrace: ObjectReference
@@ -57,12 +59,13 @@ class DebugProbesImpl private constructor(context: DefaultExecutionContext) :
try {
DebugProbesImpl(context)
} catch (e: IllegalStateException) {
log.debug("Attempt to access DebugProbesImpl but none found.", e)
null
}
}
}
class DebugProbesImpl_CoroutineOwner(val coroutineInfo: CoroutineInfo, context: DefaultExecutionContext) :
class DebugProbesImplCoroutineOwner(private val coroutineInfo: CoroutineInfo, context: DefaultExecutionContext) :
BaseMirror<MirrorOfCoroutineOwner>(COROUTINE_OWNER_CLASS_NAME, context) {
private val infoField = makeField("info")
@@ -75,7 +78,7 @@ class DebugProbesImpl_CoroutineOwner(val coroutineInfo: CoroutineInfo, context:
const val COROUTINE_OWNER_CLASS_NAME = "kotlinx.coroutines.debug.internal.DebugProbesImpl\$CoroutineOwner"
fun instanceOf(value: ObjectReference?) =
value?.let { it.referenceType().isSubTypeOrSame(COROUTINE_OWNER_CLASS_NAME) } ?: false
value?.referenceType()?.isSubTypeOrSame(COROUTINE_OWNER_CLASS_NAME) ?: false
}
}
@@ -84,13 +87,13 @@ data class MirrorOfCoroutineOwner(val that: ObjectReference, val coroutineInfo:
data class MirrorOfDebugProbesImpl(val that: ObjectReference, val instance: ObjectReference?, val isInstalled: Boolean?)
class CoroutineInfo private constructor(
val debugProbesImplMirror: DebugProbesImpl,
private 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 javaLangMirror = JavaLangMirror(context)
private val javaLangListMirror = JavaUtilAbstractCollection(context)
private val coroutineContextMirror = CoroutineContext(context)
private val stackTraceElement = StackTraceElement(context)
private val contextFieldRef = makeField("context")
@@ -105,16 +108,16 @@ class CoroutineInfo private constructor(
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"
private const val AGENT_134_CLASS_NAME = "kotlinx.coroutines.debug.CoroutineInfo"
private 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())
return try {
CoroutineInfo(debugProbesImplMirror, context, classType.name())
} catch (e: IllegalStateException) {
log.warn("coroutine-debugger: $classType not found", e)
return null
null
}
}
}
@@ -133,9 +136,9 @@ class CoroutineInfo private constructor(
val creationStackTrace = creationStackTraceMirror?.values?.mapNotNull { stackTraceElement.mirror(it, context) }
val lastObservedStackTrace = objectValue(value, lastObservedStackTraceMethod, context)
val enchancedList =
val enhancedList =
if (lastObservedStackTrace != null)
debugProbesImplMirror.enchanceStackTraceWithThreadDump(context, value, lastObservedStackTrace)
debugProbesImplMirror.enhanceStackTraceWithThreadDump(context, value, lastObservedStackTrace)
else emptyList()
val lastObservedThread = threadValue(value, lastObservedThreadField)
val lastObservedFrame = threadValue(value, lastObservedFrameField)
@@ -144,7 +147,7 @@ class CoroutineInfo private constructor(
coroutineContext,
creationStackBottom,
sequenceNumber,
enchancedList,
enhancedList,
creationStackTrace,
state,
lastObservedThread,
@@ -159,7 +162,7 @@ data class MirrorOfCoroutineInfo(
val context: MirrorOfCoroutineContext?,
val creationStackBottom: MirrorOfCoroutineStackFrame?,
val sequenceNumber: Long?,
val enchancedStackTrace: List<MirrorOfStackTraceElement>?,
val enhancedStackTrace: List<MirrorOfStackTraceElement>?,
val creationStackTrace: List<MirrorOfStackTraceElement>?,
val state: String?,
val lastObservedThread: ThreadReference?,
@@ -234,11 +237,8 @@ data class MirrorOfStackTraceElement(
val lineNumber: Int?,
val format: Byte?
) {
fun isInvokeSuspend() =
"invokeSuspend" == methodName
fun stackTraceElement() =
java.lang.StackTraceElement(
StackTraceElement(
declaringClass,
methodName,
fileName,
@@ -9,36 +9,35 @@ 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) ?: throw IllegalStateException("coroutine-debugger: class $name not found.")
fun makeField(fieldName: String): Field? =
cls?.let { it.fieldByName(fieldName) }
cls.fieldByName(fieldName)
fun makeMethod(methodName: String): Method? =
cls?.let { it.methodsByName(methodName).singleOrNull() }
cls.methodsByName(methodName).singleOrNull()
fun makeMethod(methodName: String, signature: String): Method? =
cls?.let { it.methodsByName(methodName, signature).singleOrNull() }
cls.methodsByName(methodName, signature).singleOrNull()
fun isCompatible(value: ObjectReference?) =
value?.let { it.referenceType().isSubTypeOrSame(name) } ?: false
value?.referenceType()?.isSubTypeOrSame(name) ?: false
fun mirror(value: ObjectReference?, context: DefaultExecutionContext): T? {
value ?: return null
if (!isCompatible(value)) {
return if (!isCompatible(value)) {
log.trace("Value ${value.referenceType()} is not compatible with $name.")
return null
null
} else
return fetchMirror(value, context)
fetchMirror(value, context)
}
fun staticObjectValue(fieldName: String): ObjectReference? {
val keyFieldRef = makeField(fieldName)
return cls?.let { it.getValue(keyFieldRef) as? ObjectReference }
return cls.let { it.getValue(keyFieldRef) as? ObjectReference }
}
fun staticMethodValue(instance: ObjectReference?, method: Method?, context: DefaultExecutionContext, vararg values: Value?) =
@@ -49,7 +48,7 @@ abstract class BaseMirror<T>(val name: String, context: DefaultExecutionContext)
}
fun staticMethodValue(method: Method?, context: DefaultExecutionContext, vararg values: Value?) =
cls?.let {
cls.let {
method?.let {
context.invokeMethodSafe(cls, method, values.asList()) as? ObjectReference
}
@@ -109,7 +108,7 @@ abstract class BaseMirror<T>(val name: String, context: DefaultExecutionContext)
protected abstract fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): T?
}
class StandaloneCoroutine(context: DefaultExecutionContext) :
class StandaloneCoroutine private constructor(context: DefaultExecutionContext) :
BaseMirror<MirrorOfStandaloneCoroutine>("kotlinx.coroutines.StandaloneCoroutine", context) {
private val coroutineContextMirror = CoroutineContext(context)
private val childContinuationMirror = ChildContinuation(context)
@@ -118,19 +117,22 @@ class StandaloneCoroutine(context: DefaultExecutionContext) :
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfStandaloneCoroutine {
val state = objectValue(value, stateFieldRef)
val childcontinuation = childContinuationMirror.mirror(state, context)
val childContinuation = childContinuationMirror.mirror(state, context)
val cc = objectValue(value, contextFieldRef)
val coroutineContext = coroutineContextMirror.mirror(cc, context)
return MirrorOfStandaloneCoroutine(value, childcontinuation, coroutineContext)
return MirrorOfStandaloneCoroutine(value, childContinuation, coroutineContext)
}
companion object {
val log by logger
fun instance(context: DefaultExecutionContext): StandaloneCoroutine? {
val sc = StandaloneCoroutine(context)
if (sc.cls == null)
return null
else
return sc
return try {
StandaloneCoroutine(context)
} catch (e: IllegalStateException) {
log.debug("Attempt to access DebugProbesImpl but none found.", e)
null
}
}
}
}
@@ -160,7 +162,7 @@ data class MirrorOfChildContinuation(
class CancellableContinuationImpl(context: DefaultExecutionContext) :
BaseMirror<MirrorOfCancellableContinuationImpl>("kotlinx.coroutines.CancellableContinuationImpl", context) {
private val coroutineContextMirror = CoroutineContext(context)
private val dispatchedContinuationtMirror = DispatchedContinuation(context)
private val dispatchedContinuationMirror = DispatchedContinuation(context)
private val decisionFieldRef = makeField("_decision")
private val delegateFieldRef = makeField("delegate") // DispatchedContinuation
private val resumeModeFieldRef = makeField("resumeMode")
@@ -169,7 +171,7 @@ class CancellableContinuationImpl(context: DefaultExecutionContext) :
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCancellableContinuationImpl? {
val decision = intValue(value, decisionFieldRef)
val dispatchedContinuation = dispatchedContinuationtMirror.mirror(objectValue(value, delegateFieldRef), context)
val dispatchedContinuation = dispatchedContinuationMirror.mirror(objectValue(value, delegateFieldRef), context)
val submissionTime = longValue(value, submissionTimeFieldRef)
val resumeMode = intValue(value, resumeModeFieldRef)
val coroutineContext = objectValue(value, contextFieldRef)
@@ -183,7 +185,7 @@ data class MirrorOfCancellableContinuationImpl(
val decision: Int?,
val delegate: MirrorOfDispatchedContinuation?,
val resumeMode: Int?,
val submissionTyme: Long?,
val submissionTime: Long?,
val jobContext: MirrorOfCoroutineContext?
)
@@ -45,7 +45,7 @@ class DebugMetadata private constructor(context: DefaultExecutionContext) :
try {
return DebugMetadata(context)
} catch (e: IllegalStateException) {
log.warn("Attempt to access DebugMetadata", e)
log.debug("Attempt to access DebugMetadata but none found.", e)
}
return null
}
@@ -73,7 +73,8 @@ class BaseContinuationImpl(context: DefaultExecutionContext, private val debugMe
val fieldVariables = getSpilledVariableFieldMapping(value, context)
val completionValue = objectValue(value, getCompletion, context)
val completion = if (completionValue != null && isCompatible(completionValue)) completionValue else null
val coroutineOwner = if (completionValue != null && DebugProbesImpl_CoroutineOwner.instanceOf(completionValue)) completionValue else null
val coroutineOwner =
if (completionValue != null && DebugProbesImplCoroutineOwner.instanceOf(completionValue)) completionValue else null
return MirrorOfBaseContinuationImpl(value, stackTraceElementMirror, fieldVariables, completion, coroutineOwner)
}
@@ -84,7 +85,7 @@ class BaseContinuationImpl(context: DefaultExecutionContext, private val debugMe
val length = getSpilledVariableFieldMappingReference.length() / 2
val fieldVariables = ArrayList<FieldVariable>()
for (index in 0 until length) {
var fieldVariable = getFieldVariableName(getSpilledVariableFieldMappingReference, index) ?: continue
val fieldVariable = getFieldVariableName(getSpilledVariableFieldMappingReference, index) ?: continue
fieldVariables.add(fieldVariable)
}
return fieldVariables
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class JavaLangMirror(context: DefaultExecutionContext) {
// java.lang.Object
val classClsRef = context.findClass("java.lang.Object") as ClassType
private val classClsRef = context.findClass("java.lang.Object") as ClassType
val toString: Method = classClsRef.concreteMethodByName("toString", "()Ljava/lang/String;")
// java.util.List
@@ -25,9 +25,6 @@ class JavaLangMirror(context: DefaultExecutionContext) {
private val fileNameFieldRef: Field = stackTraceElementClsRef.fieldByName("fileName")
private val lineNumberFieldRef: Field = stackTraceElementClsRef.fieldByName("lineNumber")
// java.lang.Class
val classType = context.findClass("java.lang.Class") as ClassType
fun string(state: ObjectReference, context: DefaultExecutionContext): String =
(context.invokeMethod(state, toString, emptyList()) as StringReference).value()
@@ -63,14 +60,14 @@ class JavaLangMirror(context: DefaultExecutionContext) {
class JavaUtilAbstractCollection(context: DefaultExecutionContext) :
BaseMirror<MirrorOfJavaLangAbstractCollection>("java.util.AbstractCollection", context) {
val abstractList = JavaUtilAbstractList(context)
val sizeMethod = makeMethod("size")
private val abstractList = JavaUtilAbstractList(context)
private val sizeMethod = makeMethod("size")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfJavaLangAbstractCollection? {
val list = mutableListOf<ObjectReference>()
val size = intValue(value, sizeMethod, context) ?: 0
for (index in 0 until size) {
val reference = abstractList.get(value, index, context) ?: continue
val reference = abstractList.get(value, index, context) ?: continue
list.add(reference)
}
return MirrorOfJavaLangAbstractCollection(value, list)
@@ -81,7 +78,7 @@ class JavaUtilAbstractList(context: DefaultExecutionContext) :
BaseMirror<ObjectReference>("java.util.AbstractList", context) {
val getMethod = makeMethod("get")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext) =
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): Nothing? =
null
fun get(value: ObjectReference, index: Int, context: DefaultExecutionContext): ObjectReference? =
@@ -20,7 +20,7 @@ class CoroutineFrameBuilder {
companion object {
val log by logger
const val PRE_FETCH_FRAME_COUNT = 5
private const val PRE_FETCH_FRAME_COUNT = 5
fun build(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): DoubleFrameList? =
when {
@@ -81,10 +81,10 @@ class CoroutineFrameBuilder {
frame: StackFrameProxyImpl
): RunningCoroutineStackFrameItem? {
val location = frame.location()
if (!location.safeCoroutineExitPointLineNumber())
return RunningCoroutineStackFrameItem(frame, location)
return if (!location.safeCoroutineExitPointLineNumber())
RunningCoroutineStackFrameItem(frame, location)
else
return null
null
}
/**
@@ -130,7 +130,7 @@ class CoroutineFrameBuilder {
else -> null
} ?: return null
val continuationHolder = ContinuationHolder.instance(context) ?: return null
val continuationHolder = ContinuationHolder.instance(context)
val coroutineInfo = continuationHolder.extractCoroutineInfoData(continuation) ?: return null
return preflight(frame, theFollowingFrames, coroutineInfo, mode)
}
@@ -139,7 +139,7 @@ class CoroutineFrameBuilder {
private fun lookForTheFollowingFrame(theFollowingFrames: List<StackFrameProxyImpl>): StackFrameProxyImpl? {
for (i in 0 until min(PRE_FETCH_FRAME_COUNT, theFollowingFrames.size)) { // pre-scan PRE_FETCH_FRAME_COUNT frames
val nextFrame = theFollowingFrames.get(i)
val nextFrame = theFollowingFrames[i]
if (nextFrame.location().isPreFlight() == SuspendExitMode.SUSPEND_METHOD) {
return nextFrame
}
@@ -168,7 +168,7 @@ class CoroutineFrameBuilder {
private fun getThisContinuation(frame: StackFrameProxyImpl?): ObjectReference? =
frame?.thisVariableValue()
fun theFollowingFrames(frame: StackFrameProxyImpl): List<StackFrameProxyImpl>? {
private fun theFollowingFrames(frame: StackFrameProxyImpl): List<StackFrameProxyImpl>? {
val frames = frame.threadProxy().frames()
val indexOfCurrentFrame = frames.indexOf(frame)
if (indexOfCurrentFrame >= 0) {
@@ -30,9 +30,6 @@ fun Method.isInvokeSuspend(): Boolean =
fun Method.isInvoke(): Boolean =
name() == "invoke" && signature().contains("Ljava/lang/Object;)Ljava/lang/Object;")
fun Method.isContinuation() =
isInvokeSuspend() && declaringType().isContinuation() /* Perhaps need to check for "Lkotlin/coroutines/Continuation;)" in signature() ? */
fun Method.isSuspendLambda() =
isInvokeSuspend() && declaringType().isSuspendLambda()
@@ -79,16 +76,13 @@ fun StackFrameProxyImpl.variableValue(variableName: String): ObjectReference? {
return getValue(continuationVariable) as? ObjectReference ?: return null
}
fun StackFrameProxyImpl.completionVariableValue(): ObjectReference? =
variableValue("completion")
fun StackFrameProxyImpl.continuationVariableValue(): ObjectReference? =
variableValue("\$continuation")
fun StackFrameProxyImpl.thisVariableValue(): ObjectReference? =
this.thisObject()
private fun Method.isGetCOROUTINE_SUSPENDED() =
private fun Method.isGetCoroutineSuspended() =
signature() == "()Ljava/lang/Object;" && name() == "getCOROUTINE_SUSPENDED" && declaringType().name() == "kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsKt"
fun DefaultExecutionContext.findCoroutineMetadataType() =
@@ -101,7 +95,7 @@ fun DefaultExecutionContext.findCancellableContinuationImplReferenceType(): List
vm.classesByName("kotlinx.coroutines.CancellableContinuationImpl")
fun hasGetCoroutineSuspended(frames: List<StackFrameProxyImpl>) =
frames.indexOfFirst { it.safeLocation()?.safeMethod()?.isGetCOROUTINE_SUSPENDED() == true }
frames.indexOfFirst { it.safeLocation()?.safeMethod()?.isGetCoroutineSuspended() == true }
fun StackTraceElement.isCreationSeparatorFrame() =
className.startsWith(CREATION_STACK_TRACE_SEPARATOR)
@@ -112,9 +106,6 @@ fun StackTraceElement.findPosition(project: Project): XSourcePosition? =
fun Location.findPosition(project: Project) =
getPosition(project, declaringType().name(), lineNumber())
fun ClassType.completionField() =
fieldByName("completion")
private fun getPosition(project: Project, className: String, lineNumber: Int): XSourcePosition? {
val psiFacade = JavaPsiFacade.getInstance(project)
val psiClass = psiFacade.findClass(
@@ -130,7 +121,7 @@ private fun getPosition(project: Project, className: String, lineNumber: Int): X
fun SuspendContextImpl.executionContext() =
invokeInManagerThread { DefaultExecutionContext(EvaluationContextImpl(this, this.frameProxy)) }
fun <T : Any> SuspendContextImpl.invokeInManagerThread(f: () -> T?) : T? =
fun <T : Any> SuspendContextImpl.invokeInManagerThread(f: () -> T?): T? =
debugProcess.invokeInManagerThread { f() }
fun ThreadReferenceProxyImpl.supportsEvaluation(): Boolean =
@@ -10,37 +10,16 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.MessageType
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.xdebugger.XDebuggerUtil
import com.intellij.xdebugger.XSourcePosition
import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.impl.XDebuggerManagerImpl
import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ApplicationThreadExecutor
import org.jetbrains.kotlin.idea.debugger.safeLineNumber
import org.jetbrains.kotlin.idea.debugger.safeLocation
import org.jetbrains.kotlin.idea.debugger.safeMethod
fun getPosition(stackTraceElement: StackTraceElement, project: Project): XSourcePosition? {
val psiFacade = JavaPsiFacade.getInstance(project)
val psiClass = ApplicationThreadExecutor().readAction {
@Suppress("DEPRECATION")
psiFacade.findClass(
stackTraceElement.className.substringBefore("$"), // find outer class, for which psi exists TODO
GlobalSearchScope.everythingScope(project)
)
}
val classFile = psiClass?.containingFile?.virtualFile
// to convert to 0-based line number or '-1' to do not move
val lineNumber = if (stackTraceElement.lineNumber > 0) stackTraceElement.lineNumber - 1 else return null
return XDebuggerUtil.getInstance().createPosition(classFile, lineNumber)
}
fun Location.format(): String {
val method = safeMethod()
return "${method?.name() ?: "noname"}:${safeLineNumber()}, ${method?.declaringType()?.name() ?: "empty"}"
@@ -48,7 +27,7 @@ fun Location.format(): String {
fun JavaStackFrame.format(): String {
val location = descriptor.location
return location?.let { it.format() } ?: "emptyLocation"
return location?.format() ?: "emptyLocation"
}
fun StackFrameItem.format(): String {
@@ -63,5 +42,3 @@ fun StackFrameProxyImpl.format(): String {
}
fun isInUnitTest() = ApplicationManager.getApplication().isUnitTestMode
fun coroutineDebuggerTraceEnabled() = Registry.`is`("kotlin.debugger.coroutines.trace") || isInUnitTest()
@@ -5,27 +5,29 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.util
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.DefaultLogger
import com.intellij.openapi.diagnostic.Logger
import com.intellij.xdebugger.XDebugSession
import com.intellij.xdebugger.XDebugSessionListener
import org.apache.log4j.Level
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
import javax.swing.Icon
import javax.swing.JComponent
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
interface CreateContentParamsProvider {
fun createContentParams() : CreateContentParams
fun createContentParams(): CreateContentParams
}
data class CreateContentParams(val id: String, val component: JComponent, val displayName: String, val icon: Icon?, val parentComponent: JComponent)
data class CreateContentParams(
val id: String,
val component: JComponent,
val displayName: String,
val icon: Icon?,
val parentComponent: JComponent
)
interface XDebugSessionListenerProvider {
fun debugSessionListener(session: XDebugSession) : XDebugSessionListener
fun debugSessionListener(session: XDebugSession): XDebugSessionListener
}
/**
@@ -45,7 +45,12 @@ import javax.swing.event.DocumentEvent
/**
* Panel with dump of coroutines
*/
class CoroutineDumpPanel(project: Project, consoleView: ConsoleView, toolbarActions: DefaultActionGroup, val dump: List<CoroutineInfoData>) :
class CoroutineDumpPanel(
project: Project,
consoleView: ConsoleView,
toolbarActions: DefaultActionGroup,
val dump: List<CoroutineInfoData>
) :
JPanel(BorderLayout()), DataProvider {
private var exporterToTextFile: ExporterToTextFile
private var mergedDump = ArrayList<CoroutineInfoData>()
@@ -180,14 +185,6 @@ class CoroutineDumpPanel(project: Project, consoleView: ConsoleView, toolbarActi
override fun getData(dataId: String): Any? = if (PlatformDataKeys.EXPORTER_TO_TEXT_FILE.`is`(dataId)) exporterToTextFile else null
private fun getCoroutineStateIcon(infoData: CoroutineInfoData): Icon {
return when (infoData.key.state) {
State.RUNNING -> LayeredIcon(AllIcons.Actions.Resume, Daemon_sign)
State.SUSPENDED -> AllIcons.Actions.Pause
else -> EmptyIcon.create(6)
}
}
private fun getAttributes(infoData: CoroutineInfoData): SimpleTextAttributes {
return when {
infoData.isSuspended() -> SimpleTextAttributes.GRAY_ATTRIBUTES
@@ -296,9 +293,9 @@ class CoroutineDumpPanel(project: Project, consoleView: ConsoleView, toolbarActi
private fun stringStackTrace(info: CoroutineInfoData) =
buildString {
appendln("\"${info.key.name}\", state: ${info.key.state}")
appendLine("\"${info.key.name}\", state: ${info.key.state}")
info.stackTrace.forEach {
appendln("\t$it")
appendLine("\t$it")
}
}
@@ -48,7 +48,7 @@ class CoroutineViewDebugSessionListener(
renew(suspendContext)
}
fun renew(suspendContext: XSuspendContext) {
private fun renew(suspendContext: XSuspendContext) {
if (suspendContext is SuspendContextImpl) {
DebuggerUIUtil.invokeLater {
xCoroutineView.renewRoot(suspendContext)
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.idea.debugger.coroutine.view
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.intellij.debugger.settings.ThreadsViewSettings
import com.intellij.icons.AllIcons
import com.intellij.icons.AllIcons.General.Information
import com.intellij.openapi.editor.HighlighterColors
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.ui.ColoredTextContainer
@@ -25,11 +24,11 @@ import org.jetbrains.kotlin.idea.debugger.coroutine.data.State
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import javax.swing.Icon
class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
val texts = mutableListOf<String>()
val textKeyAttributes = mutableListOf<TextAttributesKey>()
class SimpleColoredTextIcon(val icon: Icon?, val hasChildren: Boolean) {
private val texts = mutableListOf<String>()
private val textKeyAttributes = mutableListOf<TextAttributesKey>()
constructor(icon: Icon?, hasChildrens: Boolean, text: String) : this(icon, hasChildrens) {
constructor(icon: Icon?, hasChildren: Boolean, text: String) : this(icon, hasChildren) {
append(text)
}
@@ -43,14 +42,14 @@ class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
textKeyAttributes.add(CoroutineDebuggerColors.VALUE_ATTRIBUTES)
}
fun appendToComponent(component: ColoredTextContainer) {
private fun appendToComponent(component: ColoredTextContainer) {
val size: Int = texts.size
for (i in 0 until size) {
val text: String = texts.get(i)
val attribute: TextAttributesKey = textKeyAttributes.get(i)
val simpleTextAttrinbute = toSimpleTextAttribute(attribute)
val text: String = texts[i]
val attribute: TextAttributesKey = textKeyAttributes[i]
val simpleTextAttribute = toSimpleTextAttribute(attribute)
component.append(text, simpleTextAttrinbute)
component.append(text, simpleTextAttribute)
}
}
@@ -90,7 +89,7 @@ class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
interface CoroutineDebuggerColors {
companion object {
val REGULAR_ATTRIBUTES = HighlighterColors.TEXT
val REGULAR_ATTRIBUTES: TextAttributesKey = HighlighterColors.TEXT
val VALUE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("KOTLIN_COROUTINE_DEBUGGER_VALUE", HighlighterColors.TEXT)
}
}
@@ -185,7 +184,7 @@ class SimpleColoredTextIconPresentationRenderer {
)
fun renderErrorNode(error: String) =
SimpleColoredTextIcon(AllIcons.Actions.Lightning,false, KotlinDebuggerCoroutinesBundle.message(error))
SimpleColoredTextIcon(AllIcons.Actions.Lightning, false, KotlinDebuggerCoroutinesBundle.message(error))
fun renderInfoNode(text: String) =
SimpleColoredTextIcon(AllIcons.General.Information, false, KotlinDebuggerCoroutinesBundle.message(text))
@@ -32,16 +32,12 @@ import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineDebuggerContentInfo
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineDebuggerContentInfo.Companion.XCOROUTINE_POPUP_ACTION_GROUP
import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import org.jetbrains.kotlin.idea.debugger.coroutine.VersionedImplementationProvider
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CoroutineFrameBuilder
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineStackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CreationCoroutineStackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.CoroutineDebugProbesProxy
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ManagerThreadExecutor
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParams
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParamsProvider
import org.jetbrains.kotlin.idea.debugger.coroutine.util.XDebugSessionListenerProvider
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.coroutine.util.*
import java.awt.BorderLayout
import javax.swing.JPanel
@@ -49,20 +45,20 @@ import javax.swing.JPanel
class XCoroutineView(val project: Project, val session: XDebugSession) :
Disposable, XDebugSessionListenerProvider, CreateContentParamsProvider {
val log by logger
val versionedImplementationProvider = VersionedImplementationProvider()
private val versionedImplementationProvider = VersionedImplementationProvider()
val mainPanel = JPanel(BorderLayout())
val someCombobox = ComboBox<String>()
val panel = XDebuggerTreePanel(project, session.debugProcess.editorsProvider, this, null, XCOROUTINE_POPUP_ACTION_GROUP, null)
val alarm = SingleAlarm(Runnable { resetRoot() }, VIEW_CLEAR_DELAY, this)
val alarm = SingleAlarm({ resetRoot() }, VIEW_CLEAR_DELAY, this)
val renderer = SimpleColoredTextIconPresentationRenderer()
val managerThreadExecutor = ManagerThreadExecutor(session)
var treeState: XDebuggerTreeState? = null
private var treeState: XDebuggerTreeState? = null
private var restorer: XDebuggerTreeRestorer? = null
private var selectedNodeListener: XDebuggerTreeSelectedNodeListener? = null
companion object {
private val VIEW_CLEAR_DELAY = 100 //ms
private const val VIEW_CLEAR_DELAY = 100 //ms
}
init {
@@ -97,7 +93,7 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
fun saveState() {
DebuggerUIUtil.invokeLater {
if (!(panel.tree.root is EmptyNode)) {
if (panel.tree.root !is EmptyNode) {
treeState = XDebuggerTreeState.saveState(panel.tree)
}
}
@@ -124,10 +120,6 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
}
}
fun forceClear() {
alarm.cancel()
}
override fun debugSessionListener(session: XDebugSession) =
CoroutineViewDebugSessionListener(session, this)
@@ -155,10 +147,10 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
val debugProbesProxy = CoroutineDebugProbesProxy(suspendContext)
val emptyDispatcherName = KotlinDebuggerCoroutinesBundle.message("coroutine.view.dispatcher.empty")
var coroutineCache = debugProbesProxy.dumpCoroutines()
val coroutineCache = debugProbesProxy.dumpCoroutines()
if (coroutineCache.isOk()) {
val children = XValueChildrenList()
var groups = coroutineCache.cache.groupBy { it.key.dispatcher }
val groups = coroutineCache.cache.groupBy { it.key.dispatcher }
for (dispatcher in groups.keys) {
children.add(CoroutineContainer(suspendContext, dispatcher ?: emptyDispatcherName, groups[dispatcher]))
}
@@ -248,7 +240,7 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
}
private fun applyRenderer(node: XValueNode, presentation: SimpleColoredTextIcon) =
node.setPresentation(presentation.icon, presentation.valuePresentation(), presentation.hasChildrens)
node.setPresentation(presentation.icon, presentation.valuePresentation(), presentation.hasChildren)
open inner class RendererContainer(val presentation: SimpleColoredTextIcon) : XNamedValue(presentation.simpleString()) {
override fun computePresentation(node: XValueNode, place: XValuePlace) =
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
@@ -46,4 +47,6 @@ abstract class AbstractPrimitiveRangeToInspection : AbstractKotlinInspection() {
return constant.toConstantValue(getType(c) ?: return null)
}
}
}
}
var q = 12