(CoroutineDebugger) Minor issued fixed, log level adjusted for missing classes in JVM.
This commit is contained in:
+1
-2
@@ -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();
|
||||
|
||||
+2
-4
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
-4
@@ -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
|
||||
|
||||
+12
-9
@@ -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
|
||||
|
||||
+2
-2
@@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+1
-2
@@ -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
|
||||
|
||||
|
||||
+3
-3
@@ -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())
|
||||
|
||||
+2
-3
@@ -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
|
||||
*/
|
||||
|
||||
+4
-9
@@ -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) {
|
||||
|
||||
+7
-6
@@ -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))
|
||||
|
||||
-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
|
||||
|
||||
+15
-12
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-190
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+8
-7
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-8
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
-10
@@ -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)
|
||||
+9
-9
@@ -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? {
|
||||
|
||||
+12
-14
@@ -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)
|
||||
|
||||
+28
-28
@@ -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,
|
||||
|
||||
+23
-21
@@ -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?
|
||||
)
|
||||
|
||||
|
||||
+4
-3
@@ -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
|
||||
|
||||
+5
-8
@@ -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? =
|
||||
|
||||
+7
-7
@@ -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) {
|
||||
|
||||
+3
-12
@@ -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 =
|
||||
|
||||
+1
-24
@@ -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()
|
||||
|
||||
+9
-7
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+8
-11
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+11
-12
@@ -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))
|
||||
|
||||
+9
-17
@@ -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) =
|
||||
|
||||
+4
-1
@@ -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
|
||||
Reference in New Issue
Block a user