[coroutine][debugger] implementation basing on X-* approach
XCoroutine view added for direct comparison with Coroutines
This commit is contained in:
committed by
Vladimir Ilmov
parent
61c5ef61cc
commit
b1b0817336
+13
-4
@@ -8,10 +8,11 @@ package org.jetbrains.kotlin.idea.debugger.coroutines
|
|||||||
import com.intellij.debugger.engine.*
|
import com.intellij.debugger.engine.*
|
||||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
import com.intellij.xdebugger.frame.XSuspendContext
|
||||||
import com.sun.jdi.*
|
import com.sun.jdi.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.*
|
import org.jetbrains.kotlin.idea.debugger.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineAsyncStackFrameItem
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineAsyncStackFrameItem
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.AsyncStackTraceContext
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
||||||
|
|
||||||
class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
||||||
@@ -21,7 +22,12 @@ class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAsyncStackTraceSafe(frameProxy: StackFrameProxyImpl, suspendContext: SuspendContext): List<CoroutineAsyncStackFrameItem> {
|
fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: XSuspendContext): List<CoroutineAsyncStackFrameItem>? {
|
||||||
|
val stackFrameList = hopelessAware { getAsyncStackTraceSafe(stackFrame.stackFrameProxy, suspendContext) } ?: emptyList()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAsyncStackTraceSafe(frameProxy: StackFrameProxyImpl, suspendContext: XSuspendContext): List<CoroutineAsyncStackFrameItem> {
|
||||||
val defaultResult = emptyList<CoroutineAsyncStackFrameItem>()
|
val defaultResult = emptyList<CoroutineAsyncStackFrameItem>()
|
||||||
|
|
||||||
val location = frameProxy.location()
|
val location = frameProxy.location()
|
||||||
@@ -31,7 +37,7 @@ class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
|||||||
val method = location.safeMethod() ?: return defaultResult
|
val method = location.safeMethod() ?: return defaultResult
|
||||||
val threadReference = frameProxy.threadProxy().threadReference
|
val threadReference = frameProxy.threadProxy().threadReference
|
||||||
|
|
||||||
if (threadReference == null || !threadReference.isSuspended || !(suspendContext.debugProcess as DebugProcessImpl).canRunEvaluation)
|
if (threadReference == null || !threadReference.isSuspended || !canRunEvaluation(suspendContext))
|
||||||
return defaultResult
|
return defaultResult
|
||||||
|
|
||||||
|
|
||||||
@@ -41,7 +47,7 @@ class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
|||||||
|
|
||||||
private fun createAsyncStackTraceContext(
|
private fun createAsyncStackTraceContext(
|
||||||
frameProxy: StackFrameProxyImpl,
|
frameProxy: StackFrameProxyImpl,
|
||||||
suspendContext: SuspendContext,
|
suspendContext: XSuspendContext,
|
||||||
method: Method
|
method: Method
|
||||||
): AsyncStackTraceContext {
|
): AsyncStackTraceContext {
|
||||||
val evaluationContext = EvaluationContextImpl(suspendContext as SuspendContextImpl, frameProxy)
|
val evaluationContext = EvaluationContextImpl(suspendContext as SuspendContextImpl, frameProxy)
|
||||||
@@ -49,5 +55,8 @@ class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
|||||||
// DebugMetadataKt not found, probably old kotlin-stdlib version
|
// DebugMetadataKt not found, probably old kotlin-stdlib version
|
||||||
return AsyncStackTraceContext(context, method)
|
return AsyncStackTraceContext(context, method)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun canRunEvaluation(suspendContext: XSuspendContext) =
|
||||||
|
(suspendContext as SuspendContextImpl).debugProcess.canRunEvaluation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -13,6 +13,7 @@ import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
|||||||
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl
|
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl
|
||||||
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl
|
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl
|
||||||
|
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
open class BuildCoroutineNodeCommand(
|
open class BuildCoroutineNodeCommand(
|
||||||
val node: DebuggerTreeNodeImpl,
|
val node: DebuggerTreeNodeImpl,
|
||||||
debuggerContext: DebuggerContextImpl,
|
debuggerContext: DebuggerContextImpl,
|
||||||
|
|||||||
+3
-1
@@ -11,6 +11,7 @@ import com.intellij.debugger.ui.impl.watch.NodeManagerImpl
|
|||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CreationFramesDescriptor
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CreationFramesDescriptor
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineCreatedStackFrameDescriptor
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineCreatedStackFrameDescriptor
|
||||||
|
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
class CoroutineBuildCreationFrameCommand(
|
class CoroutineBuildCreationFrameCommand(
|
||||||
node: DebuggerTreeNodeImpl,
|
node: DebuggerTreeNodeImpl,
|
||||||
val descriptor: CreationFramesDescriptor,
|
val descriptor: CreationFramesDescriptor,
|
||||||
@@ -21,10 +22,11 @@ class CoroutineBuildCreationFrameCommand(
|
|||||||
val threadProxy = debuggerContext.suspendContext?.thread ?: return
|
val threadProxy = debuggerContext.suspendContext?.thread ?: return
|
||||||
val evalContext = debuggerContext.createEvaluationContext() ?: return
|
val evalContext = debuggerContext.createEvaluationContext() ?: return
|
||||||
val proxy = threadProxy.forceFrames().first()
|
val proxy = threadProxy.forceFrames().first()
|
||||||
descriptor.frames.forEach {
|
for(it in descriptor.frames) {
|
||||||
val descriptor = myNodeManager.createNode(
|
val descriptor = myNodeManager.createNode(
|
||||||
CoroutineCreatedStackFrameDescriptor(it, proxy), evalContext)
|
CoroutineCreatedStackFrameDescriptor(it, proxy), evalContext)
|
||||||
myChildren.add(descriptor)
|
myChildren.add(descriptor)
|
||||||
|
|
||||||
}
|
}
|
||||||
updateUI(true)
|
updateUI(true)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -89,13 +89,13 @@ class CoroutineBuildFrameCommand(
|
|||||||
debugProcess: DebugProcessImpl,
|
debugProcess: DebugProcessImpl,
|
||||||
evalContext: EvaluationContextImpl
|
evalContext: EvaluationContextImpl
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (descriptor.infoData.thread == null) {
|
if (descriptor.infoData.activeThread == null) {
|
||||||
myChildren.add(myNodeManager.createMessageNode("Frames are not available"))
|
myChildren.add(myNodeManager.createMessageNode("Frames are not available"))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
val proxy = ThreadReferenceProxyImpl(
|
val proxy = ThreadReferenceProxyImpl(
|
||||||
debugProcess.virtualMachineProxy,
|
debugProcess.virtualMachineProxy,
|
||||||
descriptor.infoData.thread
|
descriptor.infoData.activeThread
|
||||||
)
|
)
|
||||||
val frames = proxy.forceFrames()
|
val frames = proxy.forceFrames()
|
||||||
var endRange = findResumeWithMethodFrameIndex(frames)
|
var endRange = findResumeWithMethodFrameIndex(frames)
|
||||||
|
|||||||
+79
-64
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutines.command
|
package org.jetbrains.kotlin.idea.debugger.coroutines.command
|
||||||
|
|
||||||
|
import com.intellij.debugger.DebuggerManagerEx
|
||||||
import com.intellij.debugger.engine.*
|
import com.intellij.debugger.engine.*
|
||||||
import com.intellij.debugger.jdi.ClassesByNameProvider
|
import com.intellij.debugger.jdi.ClassesByNameProvider
|
||||||
import com.intellij.debugger.jdi.GeneratedLocation
|
import com.intellij.debugger.jdi.GeneratedLocation
|
||||||
@@ -13,40 +14,46 @@ import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
|||||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
import com.intellij.debugger.memory.utils.StackFrameItem
|
||||||
import com.intellij.debugger.ui.impl.watch.MethodsTracker
|
import com.intellij.debugger.ui.impl.watch.MethodsTracker
|
||||||
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
|
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import com.intellij.xdebugger.XSourcePosition
|
import com.intellij.xdebugger.XSourcePosition
|
||||||
|
import com.intellij.xdebugger.frame.XExecutionStack
|
||||||
import com.intellij.xdebugger.frame.XStackFrame
|
import com.intellij.xdebugger.frame.XStackFrame
|
||||||
import com.sun.jdi.AbsentInformationException
|
import com.intellij.xdebugger.frame.XSuspendContext
|
||||||
import com.sun.jdi.Location
|
import com.sun.jdi.*
|
||||||
import com.sun.jdi.ReferenceType
|
import org.jetbrains.kotlin.idea.debugger.*
|
||||||
import com.sun.jdi.ThreadReference
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.CoroutineAsyncStackTraceProvider
|
import org.jetbrains.kotlin.idea.debugger.coroutines.CoroutineAsyncStackTraceProvider
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.util.getPosition
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.SuspendStackFrameDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.SyntheticStackFrame
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.AsyncStackTraceContext
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.util.EmptyStackFrameDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.util.logger
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
||||||
|
|
||||||
|
|
||||||
class CoroutineBuilder(val suspendContext: SuspendContextImpl) {
|
class CoroutineBuilder(val suspendContext: XSuspendContext) {
|
||||||
private val methodsTracker = MethodsTracker()
|
private val methodsTracker = MethodsTracker()
|
||||||
private val coroutineStackFrameProvider = CoroutineAsyncStackTraceProvider()
|
private val coroutineStackFrameProvider = CoroutineAsyncStackTraceProvider()
|
||||||
val virtualMachineProxy = suspendContext.debugProcess.virtualMachineProxy
|
val debugProcess = (suspendContext as SuspendContextImpl).debugProcess
|
||||||
|
val virtualMachineProxy = debugProcess.virtualMachineProxy
|
||||||
val classesByName = ClassesByNameProvider.createCache(virtualMachineProxy.allClasses())
|
val classesByName = ClassesByNameProvider.createCache(virtualMachineProxy.allClasses())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val CREATION_STACK_TRACE_SEPARATOR = "\b\b\b" // the "\b\b\b" is used for creation stacktrace separator in kotlinx.coroutines
|
val CREATION_STACK_TRACE_SEPARATOR = "\b\b\b" // the "\b\b\b" is used for creation stacktrace separator in kotlinx.coroutines
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(ci: CoroutineInfoData): List<CoroutineStackFrameItem> {
|
fun build(coroutine: CoroutineInfoData): List<CoroutineStackFrameItem> {
|
||||||
val coroutineStackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
val coroutineStackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
||||||
val suspendedThreadProxy = suspendedThreadProxy()
|
|
||||||
val firstSuspendedStackFrameProxyImpl = firstSuspendedThreadFrame()
|
val firstSuspendedStackFrameProxyImpl = firstSuspendedThreadFrame()
|
||||||
val creationFrameSeparatorIndex = findCreationFrameIndex(ci.stackTrace)
|
val creationFrameSeparatorIndex = findCreationFrameIndex(coroutine.stackTrace)
|
||||||
val runningThreadReferenceProxyImpl = currentRunningThreadProxy(ci.thread ?: suspendedThreadProxy().threadReference)
|
val positionManager = debugProcess.positionManager
|
||||||
val positionManager = suspendContext.debugProcess.positionManager
|
|
||||||
|
|
||||||
if (ci.state == CoroutineInfoData.State.RUNNING && runningThreadReferenceProxyImpl is ThreadReferenceProxyImpl) {
|
if (coroutine.state == CoroutineInfoData.State.RUNNING && coroutine.activeThread is ThreadReference) {
|
||||||
val executionStack = JavaExecutionStack(runningThreadReferenceProxyImpl, suspendContext.debugProcess, suspendContext.thread == runningThreadReferenceProxyImpl)
|
val threadReferenceProxyImpl = runningThreadProxy(coroutine.activeThread)
|
||||||
|
val executionStack = JavaExecutionStack(threadReferenceProxyImpl, debugProcess, suspendedSameThread(coroutine.activeThread))
|
||||||
|
|
||||||
val frames = runningThreadReferenceProxyImpl.forceFrames()
|
val frames = threadReferenceProxyImpl.forceFrames()
|
||||||
var resumeMethodIndex = findResumeMethodIndex(frames)
|
var resumeMethodIndex = findResumeMethodIndex(frames)
|
||||||
for (frameIndex in 0..frames.lastIndex) {
|
for (frameIndex in 0..frames.lastIndex) {
|
||||||
val runningStackFrameProxy = frames[frameIndex]
|
val runningStackFrameProxy = frames[frameIndex]
|
||||||
@@ -55,47 +62,46 @@ class CoroutineBuilder(val suspendContext: SuspendContextImpl) {
|
|||||||
val previousJavaFrame = JavaStackFrame(StackFrameDescriptorImpl(previousFrame, methodsTracker), true)
|
val previousJavaFrame = JavaStackFrame(StackFrameDescriptorImpl(previousFrame, methodsTracker), true)
|
||||||
val asyncStackTrace = coroutineStackFrameProvider
|
val asyncStackTrace = coroutineStackFrameProvider
|
||||||
.getAsyncStackTrace(previousJavaFrame, suspendContext)
|
.getAsyncStackTrace(previousJavaFrame, suspendContext)
|
||||||
asyncStackTrace?.forEach {
|
asyncStackTrace?.forEach { asyncFrame ->
|
||||||
// val xStackFrame = positionManager.createStackFrame(runningStackFrameProxy, suspendContext.debugProcess, it.location)
|
|
||||||
val xStackFrame = JavaStackFrame(StackFrameDescriptorImpl(previousFrame, methodsTracker), true)
|
val xStackFrame = JavaStackFrame(StackFrameDescriptorImpl(previousFrame, methodsTracker), true)
|
||||||
// val itStackFrame = null
|
coroutineStackFrameList.add(AsyncCoroutineStackFrameItem(runningStackFrameProxy, asyncFrame, xStackFrame))
|
||||||
coroutineStackFrameList.add(AsyncCoroutineStackFrameItem(runningStackFrameProxy, "some label", it, xStackFrame))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// val xStackFrame = stackFrame(positionManager, runningStackFrameProxy, runningStackFrameProxy.location())
|
|
||||||
val xStackFrame = executionStack.createStackFrame(runningStackFrameProxy)
|
val xStackFrame = executionStack.createStackFrame(runningStackFrameProxy)
|
||||||
coroutineStackFrameList.add(RunningCoroutineStackFrameItem(runningStackFrameProxy, "", xStackFrame))
|
coroutineStackFrameList.add(RunningCoroutineStackFrameItem(runningStackFrameProxy, xStackFrame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ci.state == CoroutineInfoData.State
|
} else if (coroutine.state == CoroutineInfoData.State.SUSPENDED || coroutine.activeThread == null) {
|
||||||
.SUSPENDED || runningThreadReferenceProxyImpl == null
|
// to get frames from CoroutineInfo anyway
|
||||||
) { // to get frames from CoroutineInfo anyway
|
|
||||||
// the thread is paused on breakpoint - it has at least one frame
|
// the thread is paused on breakpoint - it has at least one frame
|
||||||
ci.stackTrace.subList(0, creationFrameSeparatorIndex).forEach {
|
val suspendedStackTrace = coroutine.stackTrace.take(creationFrameSeparatorIndex + 1)
|
||||||
val xStackFrame = stackFrame(positionManager, firstSuspendedStackFrameProxyImpl, it)
|
for (suspendedFrame in suspendedStackTrace) {
|
||||||
// val xStackFrame = null
|
val suspendedXStackFrame = stackFrame(positionManager, firstSuspendedStackFrameProxyImpl, suspendedFrame)
|
||||||
coroutineStackFrameList.add(SuspendCoroutineStackFrameItem(firstSuspendedStackFrameProxyImpl, "suspended frame", it, xStackFrame))
|
coroutineStackFrameList.add(
|
||||||
|
SuspendCoroutineStackFrameItem(firstSuspendedStackFrameProxyImpl, suspendedFrame, suspendedXStackFrame)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val executionStack = JavaExecutionStack(suspendedThreadProxy(), suspendContext.debugProcess, false)
|
val executionStack = JavaExecutionStack(suspendedThreadProxy(), debugProcess, false)
|
||||||
val xStackFrame = executionStack.createStackFrame(firstSuspendedStackFrameProxyImpl)
|
val xStackFrame = executionStack.createStackFrame(firstSuspendedStackFrameProxyImpl)
|
||||||
|
|
||||||
ci.stackTrace.subList(creationFrameSeparatorIndex + 1, ci.stackTrace.size).forEach {
|
coroutine.stackTrace.subList(creationFrameSeparatorIndex + 1, coroutine.stackTrace.size).forEach {
|
||||||
var location = createLocation(it)
|
var location = createLocation(it)
|
||||||
coroutineStackFrameList.add(CreationCoroutineStackFrameItem(firstSuspendedStackFrameProxyImpl, "creation frame", it, xStackFrame, location))
|
coroutineStackFrameList.add(CreationCoroutineStackFrameItem(firstSuspendedStackFrameProxyImpl, it, xStackFrame, location))
|
||||||
}
|
}
|
||||||
ci.stackFrameList.addAll(coroutineStackFrameList)
|
coroutine.stackFrameList.addAll(coroutineStackFrameList)
|
||||||
return coroutineStackFrameList
|
return coroutineStackFrameList
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createLocation(stackTraceElement: StackTraceElement): Location {
|
private fun suspendedSameThread(activeThread: ThreadReference) =
|
||||||
return findLocation(
|
activeThread == suspendedThreadProxy().threadReference
|
||||||
ContainerUtil.getFirstItem(classesByName[stackTraceElement.className]),
|
|
||||||
stackTraceElement.methodName,
|
private fun createLocation(stackTraceElement: StackTraceElement): Location = findLocation(
|
||||||
stackTraceElement.lineNumber
|
ContainerUtil.getFirstItem(classesByName[stackTraceElement.className]),
|
||||||
)
|
stackTraceElement.methodName,
|
||||||
}
|
stackTraceElement.lineNumber
|
||||||
|
)
|
||||||
|
|
||||||
private fun findLocation(
|
private fun findLocation(
|
||||||
type: ReferenceType?,
|
type: ReferenceType?,
|
||||||
@@ -113,17 +119,16 @@ class CoroutineBuilder(val suspendContext: SuspendContextImpl) {
|
|||||||
} catch (ignored: AbsentInformationException) {
|
} catch (ignored: AbsentInformationException) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return GeneratedLocation(suspendContext.debugProcess, type, methodName, line)
|
return GeneratedLocation(debugProcess, type, methodName, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stackFrame(positionManager: CompoundPositionManager, runningStackFrameProxy: StackFrameProxyImpl, location: Location): XStackFrame {
|
fun stackFrame(positionManager: CompoundPositionManager, runningStackFrameProxy: StackFrameProxyImpl, location: Location): XStackFrame {
|
||||||
return positionManager.createStackFrame(runningStackFrameProxy, suspendContext.debugProcess, location)!!
|
return positionManager.createStackFrame(runningStackFrameProxy, debugProcess, location)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stackFrame(positionManager: CompoundPositionManager, runningStackFrameProxy: StackFrameProxyImpl, stackTraceElement: StackTraceElement) : XStackFrame {
|
fun stackFrame(positionManager: CompoundPositionManager, runningStackFrameProxy: StackFrameProxyImpl, stackTraceElement: StackTraceElement) : XStackFrame {
|
||||||
val location = createLocation(stackTraceElement)
|
val location = createLocation(stackTraceElement)
|
||||||
// val sourcePosition = getPosition(stackTraceElement)
|
return positionManager.createStackFrame(runningStackFrameProxy, debugProcess, location)!!
|
||||||
return positionManager.createStackFrame(runningStackFrameProxy, suspendContext.debugProcess, location)!!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -143,28 +148,29 @@ class CoroutineBuilder(val suspendContext: SuspendContextImpl) {
|
|||||||
private fun firstSuspendedThreadFrame(): StackFrameProxyImpl =
|
private fun firstSuspendedThreadFrame(): StackFrameProxyImpl =
|
||||||
suspendedThreadProxy().forceFrames().first()
|
suspendedThreadProxy().forceFrames().first()
|
||||||
|
|
||||||
// retrieves currently suspended but active and executing corouting thread proxy
|
// retrieves currently suspended but active and executing coroutine thread proxy
|
||||||
fun currentRunningThreadProxy(threadReference: ThreadReference?): ThreadReferenceProxyImpl? =
|
fun runningThreadProxy(threadReference: ThreadReference) =
|
||||||
ThreadReferenceProxyImpl(suspendContext.debugProcess.virtualMachineProxy, threadReference)
|
ThreadReferenceProxyImpl(debugProcess.virtualMachineProxy, threadReference)
|
||||||
|
|
||||||
// retrieves current suspended thread proxy
|
// retrieves current suspended thread proxy
|
||||||
fun suspendedThreadProxy(): ThreadReferenceProxyImpl =
|
private fun suspendedThreadProxy(): ThreadReferenceProxyImpl =
|
||||||
suspendContext.thread!! // @TODO hash replace !!
|
(suspendContext as SuspendContextImpl).thread!! // @TODO hash replace !!
|
||||||
|
|
||||||
private fun findResumeMethodIndex(frames: List<StackFrameProxyImpl>): Int {
|
private fun findResumeMethodIndex(frames: List<StackFrameProxyImpl>): Int {
|
||||||
for (j: Int in frames.lastIndex downTo 0)
|
for (i: Int in frames.lastIndex downTo 0)
|
||||||
if (isResumeMethodFrame(frames[j])) {
|
if (isResumeMethodFrame(frames[i])) {
|
||||||
return j
|
return i
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isResumeMethodFrame(frame: StackFrameProxyImpl) = frame.location().method().name() == "resumeWith"
|
private fun isResumeMethodFrame(frame: StackFrameProxyImpl) =
|
||||||
|
frame.safeLocation()?.safeMethod()?.name() == "resumeWith"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be invoked on manager thread
|
* Should be invoked on manager thread
|
||||||
|
* This code was migrated from previous implementation, has to be refactored @TODO
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
private fun createSyntheticStackFrame(
|
private fun createSyntheticStackFrame(
|
||||||
descriptor: SuspendStackFrameDescriptor,
|
descriptor: SuspendStackFrameDescriptor,
|
||||||
pos: XSourcePosition,
|
pos: XSourcePosition,
|
||||||
@@ -191,50 +197,59 @@ class CoroutineBuilder(val suspendContext: SuspendContextImpl) {
|
|||||||
} ?: return null
|
} ?: return null
|
||||||
return executionStack to SyntheticStackFrame(descriptor, vars, pos)
|
return executionStack to SyntheticStackFrame(descriptor, vars, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreationCoroutineStackFrameItem(
|
class CreationCoroutineStackFrameItem(
|
||||||
frame: StackFrameProxyImpl,
|
frame: StackFrameProxyImpl,
|
||||||
label: String = "",
|
|
||||||
val stackTraceElement: StackTraceElement,
|
val stackTraceElement: StackTraceElement,
|
||||||
stackFrame: XStackFrame,
|
stackFrame: XStackFrame,
|
||||||
val location: Location
|
val location: Location
|
||||||
) : CoroutineStackFrameItem(frame, label, stackFrame) {
|
) : CoroutineStackFrameItem(frame, stackFrame) {
|
||||||
override fun location() = location
|
override fun location() = location
|
||||||
|
fun emptyDescriptor() =
|
||||||
|
EmptyStackFrameDescriptor(stackTraceElement, frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
class SuspendCoroutineStackFrameItem(
|
class SuspendCoroutineStackFrameItem(
|
||||||
frame: StackFrameProxyImpl,
|
frame: StackFrameProxyImpl,
|
||||||
label: String = "",
|
|
||||||
val stackTraceElement: StackTraceElement,
|
val stackTraceElement: StackTraceElement,
|
||||||
stackFrame: XStackFrame
|
stackFrame: XStackFrame
|
||||||
) : CoroutineStackFrameItem(frame, label, stackFrame) {
|
) : CoroutineStackFrameItem(frame, stackFrame) {
|
||||||
override fun location() = frame.location()
|
override fun location() = frame.location()
|
||||||
}
|
}
|
||||||
|
|
||||||
class AsyncCoroutineStackFrameItem(
|
class AsyncCoroutineStackFrameItem(
|
||||||
frame: StackFrameProxyImpl,
|
frame: StackFrameProxyImpl,
|
||||||
label: String = "",
|
|
||||||
val frameItem: StackFrameItem,
|
val frameItem: StackFrameItem,
|
||||||
stackFrame: XStackFrame
|
stackFrame: XStackFrame
|
||||||
) : CoroutineStackFrameItem(frame, label, stackFrame) {
|
) : CoroutineStackFrameItem(frame, stackFrame) {
|
||||||
override fun location() : Location = frame.location()
|
override fun location() : Location = frame.location()
|
||||||
}
|
}
|
||||||
|
|
||||||
class RunningCoroutineStackFrameItem(
|
class RunningCoroutineStackFrameItem(
|
||||||
frame: StackFrameProxyImpl,
|
frame: StackFrameProxyImpl,
|
||||||
label: String = "",
|
|
||||||
stackFrame: XStackFrame
|
stackFrame: XStackFrame
|
||||||
) : CoroutineStackFrameItem(frame, label, stackFrame) {
|
) : CoroutineStackFrameItem(frame, stackFrame) {
|
||||||
val location = frame.location() // it should be invoked in manager thread
|
val location = frame.location() // it should be invoked in manager thread
|
||||||
|
|
||||||
override fun location() = location
|
override fun location() = location
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class CoroutineStackFrameItem(val frame: StackFrameProxyImpl, val label: String = "", val stackFrame: XStackFrame) {
|
abstract class CoroutineStackFrameItem(val frame: StackFrameProxyImpl, val stackFrame: XStackFrame) {
|
||||||
|
val log by logger
|
||||||
|
|
||||||
fun sourcePosition() : XSourcePosition? = stackFrame.sourcePosition
|
fun sourcePosition() : XSourcePosition? = stackFrame.sourcePosition
|
||||||
|
|
||||||
abstract fun location(): Location
|
abstract fun location(): Location
|
||||||
|
|
||||||
|
fun uniqueId(): String {
|
||||||
|
val location = location()
|
||||||
|
try {
|
||||||
|
return location.safeSourceName() + ":" + location.safeMethod().toString() + ":" +
|
||||||
|
location.safeLineNumber() + ":" + location.safeSourceLineNumber()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.error(e)
|
||||||
|
return location.method().toString() + ":" + location.lineNumber()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+7
-13
@@ -17,7 +17,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent
|
|||||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.application.ModalityState
|
import com.intellij.openapi.application.ModalityState
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.ui.MessageType
|
import com.intellij.openapi.ui.MessageType
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
@@ -29,10 +28,10 @@ import org.jetbrains.kotlin.idea.debugger.coroutines.view.CoroutineDumpPanel
|
|||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.coroutineDebuggerEnabled
|
import org.jetbrains.kotlin.idea.debugger.coroutines.coroutineDebuggerEnabled
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.CoroutinesDebugProbesProxy
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.CoroutinesDebugProbesProxy
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.util.logger
|
||||||
|
|
||||||
@Suppress("ComponentNotRegistered")
|
@Suppress("ComponentNotRegistered")
|
||||||
class CoroutineDumpAction : AnAction(), AnAction.TransparentUpdate {
|
class CoroutineDumpAction : AnAction(), AnAction.TransparentUpdate {
|
||||||
private val logger = Logger.getInstance(this::class.java)
|
|
||||||
|
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
val project = e.project ?: return
|
val project = e.project ?: return
|
||||||
@@ -42,20 +41,15 @@ class CoroutineDumpAction : AnAction(), AnAction.TransparentUpdate {
|
|||||||
val process = context.debugProcess ?: return
|
val process = context.debugProcess ?: return
|
||||||
process.managerThread.schedule(object : SuspendContextCommandImpl(context.suspendContext) {
|
process.managerThread.schedule(object : SuspendContextCommandImpl(context.suspendContext) {
|
||||||
override fun contextAction() {
|
override fun contextAction() {
|
||||||
val states = CoroutinesDebugProbesProxy(context.suspendContext!!).dumpCoroutines() ?: return
|
val states = CoroutinesDebugProbesProxy(context.suspendContext ?: return)
|
||||||
|
.dumpCoroutines()
|
||||||
if (states.isOk()) {
|
if (states.isOk()) {
|
||||||
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(
|
val message = KotlinBundle.message("debugger.session.tab.coroutine.message.error")
|
||||||
KotlinBundle.message("debugger.session.tab.coroutine.message.error"),
|
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(message,MessageType.ERROR).notify(project)
|
||||||
MessageType.ERROR
|
|
||||||
).notify(project)
|
|
||||||
} else {
|
} else {
|
||||||
val f = fun() {
|
val f = fun() {
|
||||||
addCoroutineDump(
|
val ui = session.xDebugSession?.ui ?: return
|
||||||
project,
|
addCoroutineDump(project, states.cache, ui, session.searchScope)
|
||||||
states.cache,
|
|
||||||
session.xDebugSession?.ui ?: return,
|
|
||||||
session.searchScope
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
ApplicationManager.getApplication().invokeLater(f, ModalityState.NON_MODAL)
|
ApplicationManager.getApplication().invokeLater(f, ModalityState.NON_MODAL)
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-21
@@ -13,60 +13,57 @@ import com.intellij.debugger.impl.DebuggerContextImpl
|
|||||||
import com.intellij.debugger.impl.DebuggerSession
|
import com.intellij.debugger.impl.DebuggerSession
|
||||||
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl
|
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl
|
||||||
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl
|
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl
|
||||||
import com.intellij.openapi.ui.MessageType
|
|
||||||
import com.intellij.xdebugger.impl.XDebuggerManagerImpl
|
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineDescriptorData
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineDescriptorData
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.CoroutinesDebugProbesProxy
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.CoroutinesDebugProbesProxy
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.ManagerThreadExecutor
|
import org.jetbrains.kotlin.idea.debugger.coroutines.util.ProjectNotification
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.createEvaluationContext
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.view.CoroutinesDebuggerTree
|
import org.jetbrains.kotlin.idea.debugger.coroutines.view.CoroutinesDebuggerTree
|
||||||
|
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
class RefreshCoroutinesTreeCommand(
|
class RefreshCoroutinesTreeCommand(
|
||||||
val context: DebuggerContextImpl,
|
val context: DebuggerContextImpl,
|
||||||
private val debuggerTree: CoroutinesDebuggerTree
|
private val debuggerTree: CoroutinesDebuggerTree
|
||||||
) : SuspendContextCommandImpl(context.suspendContext) {
|
) : SuspendContextCommandImpl(context.suspendContext) {
|
||||||
|
val notification = ProjectNotification(debuggerTree.project)
|
||||||
|
|
||||||
override fun contextAction() {
|
override fun contextAction() {
|
||||||
val nf = debuggerTree.nodeFactory
|
val nodeManagerImpl = debuggerTree.nodeFactory
|
||||||
val root = nf.defaultNode
|
val root = nodeManagerImpl.defaultNode
|
||||||
val sc: SuspendContextImpl? = suspendContext
|
val suspendContext: SuspendContextImpl? = suspendContext
|
||||||
if (context.debuggerSession is DebuggerSession && sc is SuspendContextImpl && !sc.isResumed) {
|
if (context.debuggerSession is DebuggerSession && suspendContext is SuspendContextImpl && !suspendContext.isResumed) {
|
||||||
val infoCache = CoroutinesDebugProbesProxy(sc).dumpCoroutines()
|
var infoCache = CoroutinesDebugProbesProxy(suspendContext).dumpCoroutines()
|
||||||
if (infoCache.isOk()) {
|
if (infoCache.isOk()) {
|
||||||
val evaluationContext = sc.createEvaluationContext()
|
val evaluationContext = evaluationContext(suspendContext)
|
||||||
|
|
||||||
for (state in infoCache.cache) {
|
for (state in infoCache.cache) {
|
||||||
val descriptor = createCoroutineDescriptorNode(nf, state, evaluationContext)
|
val descriptor = createCoroutineDescriptorNode(nodeManagerImpl, state, evaluationContext)
|
||||||
root.add(descriptor)
|
root.add(descriptor)
|
||||||
}
|
}
|
||||||
setRoot(root)
|
setRoot(root)
|
||||||
} else {
|
} else {
|
||||||
debuggerTree.showMessage(KotlinBundle.message("debugger.session.tab.coroutine.message.failure"))
|
debuggerTree.showMessage(KotlinBundle.message("debugger.session.tab.coroutine.message.failure"))
|
||||||
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(
|
notification.error(KotlinBundle.message("debugger.session.tab.coroutine.message.error"))
|
||||||
KotlinBundle.message("debugger.session.tab.coroutine.message.error"),
|
|
||||||
MessageType.ERROR
|
|
||||||
)
|
|
||||||
.notify(debuggerTree.project)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else
|
||||||
debuggerTree.showMessage(KotlinBundle.message("debugger.session.tab.coroutine.message.resume"))
|
debuggerTree.showMessage(KotlinBundle.message("debugger.session.tab.coroutine.message.resume"))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun evaluationContext(suspendContext : SuspendContextImpl) =
|
||||||
|
EvaluationContextImpl(suspendContext, suspendContext.frameProxy)
|
||||||
|
|
||||||
private fun createCoroutineDescriptorNode(
|
private fun createCoroutineDescriptorNode(
|
||||||
nodeFactory: NodeManagerImpl,
|
nodeFactory: NodeManagerImpl,
|
||||||
coroutineInfoData: CoroutineInfoData,
|
coroutineInfoData: CoroutineInfoData,
|
||||||
evaluationContext: EvaluationContextImpl
|
evaluationContext: EvaluationContextImpl
|
||||||
) =
|
): DebuggerTreeNodeImpl {
|
||||||
nodeFactory.createNode(
|
return nodeFactory.createNode(
|
||||||
nodeFactory.getDescriptor(
|
nodeFactory.getDescriptor(
|
||||||
null,
|
null,
|
||||||
CoroutineDescriptorData(coroutineInfoData)
|
CoroutineDescriptorData(coroutineInfoData)
|
||||||
),
|
),
|
||||||
evaluationContext
|
evaluationContext
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setRoot(root: DebuggerTreeNodeImpl) {
|
private fun setRoot(root: DebuggerTreeNodeImpl) {
|
||||||
DebuggerInvocationUtil.swingInvokeLater(debuggerTree.project) {
|
DebuggerInvocationUtil.swingInvokeLater(debuggerTree.project) {
|
||||||
|
|||||||
+1
-3
@@ -12,6 +12,4 @@ import com.intellij.xdebugger.frame.XNamedValue
|
|||||||
class CoroutineAsyncStackFrameItem(
|
class CoroutineAsyncStackFrameItem(
|
||||||
val location: GeneratedLocation,
|
val location: GeneratedLocation,
|
||||||
spilledVariables: List<XNamedValue>
|
spilledVariables: List<XNamedValue>
|
||||||
) : StackFrameItem(location, spilledVariables) {
|
) : StackFrameItem(location, spilledVariables)
|
||||||
|
|
||||||
}
|
|
||||||
+1
-3
@@ -15,12 +15,10 @@ import org.jetbrains.kotlin.idea.debugger.coroutines.command.CoroutineStackFrame
|
|||||||
data class CoroutineInfoData(
|
data class CoroutineInfoData(
|
||||||
val name: String,
|
val name: String,
|
||||||
val state: State,
|
val state: State,
|
||||||
val threadName: String,
|
|
||||||
val threadStatus: Int,
|
|
||||||
|
|
||||||
val stackTrace: List<StackTraceElement>,
|
val stackTrace: List<StackTraceElement>,
|
||||||
// links to jdi.* references
|
// links to jdi.* references
|
||||||
val thread: ThreadReference? = null, // for suspended coroutines should be null
|
val activeThread: ThreadReference? = null, // for suspended coroutines should be null
|
||||||
val frame: ObjectReference?
|
val frame: ObjectReference?
|
||||||
) {
|
) {
|
||||||
var stackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
var stackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
||||||
|
|||||||
+18
-11
@@ -19,6 +19,7 @@ import com.intellij.icons.AllIcons
|
|||||||
import com.sun.jdi.ObjectReference
|
import com.sun.jdi.ObjectReference
|
||||||
import javax.swing.Icon
|
import javax.swing.Icon
|
||||||
|
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
class CoroutineDescriptorImpl(val infoData: CoroutineInfoData) : NodeDescriptorImpl() {
|
class CoroutineDescriptorImpl(val infoData: CoroutineInfoData) : NodeDescriptorImpl() {
|
||||||
lateinit var icon: Icon
|
lateinit var icon: Icon
|
||||||
|
|
||||||
@@ -26,10 +27,11 @@ class CoroutineDescriptorImpl(val infoData: CoroutineInfoData) : NodeDescriptorI
|
|||||||
|
|
||||||
@Throws(EvaluateException::class)
|
@Throws(EvaluateException::class)
|
||||||
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener): String {
|
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener): String {
|
||||||
val thread = infoData.thread
|
val thread = infoData.activeThread
|
||||||
val name = thread?.name()?.substringBefore(" @${infoData.name}") ?: ""
|
val name = thread?.name()?.substringBefore(" @${infoData.name}") ?: ""
|
||||||
val threadState = if (thread != null) DebuggerUtilsEx.getThreadStatusText(thread.status()) else ""
|
val threadState = if (thread != null) DebuggerUtilsEx.getThreadStatusText(thread.status()) else ""
|
||||||
return "${infoData.name}: ${infoData.state}${if (name.isNotEmpty()) " on thread \"$name\":$threadState" else ""}"
|
val threadName = if (name.isNotEmpty()) " on thread \"$name\":$threadState" else ""
|
||||||
|
return "${infoData.name}: ${infoData.state} $threadName"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isExpandable() = infoData.state != CoroutineInfoData.State.CREATED
|
override fun isExpandable() = infoData.state != CoroutineInfoData.State.CREATED
|
||||||
@@ -45,8 +47,9 @@ class CoroutineDescriptorImpl(val infoData: CoroutineInfoData) : NodeDescriptorI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreationFramesDescriptor(val frames: List<StackTraceElement>)
|
@Deprecated("moved to XCoroutineView")
|
||||||
: MessageDescriptor("Coroutine creation stack trace", INFORMATION) {
|
class CreationFramesDescriptor(val frames: List<StackTraceElement>) :
|
||||||
|
MessageDescriptor("Coroutine creation stack trace", INFORMATION) {
|
||||||
|
|
||||||
override fun isExpandable() = true
|
override fun isExpandable() = true
|
||||||
}
|
}
|
||||||
@@ -54,6 +57,7 @@ class CreationFramesDescriptor(val frames: List<StackTraceElement>)
|
|||||||
/**
|
/**
|
||||||
* Descriptor for suspend functions
|
* Descriptor for suspend functions
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
class SuspendStackFrameDescriptor(
|
class SuspendStackFrameDescriptor(
|
||||||
val infoData: CoroutineInfoData,
|
val infoData: CoroutineInfoData,
|
||||||
val frame: StackTraceElement,
|
val frame: StackTraceElement,
|
||||||
@@ -64,8 +68,8 @@ class SuspendStackFrameDescriptor(
|
|||||||
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener?): String {
|
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener?): String {
|
||||||
return with(frame) {
|
return with(frame) {
|
||||||
val pack = className.substringBeforeLast(".", "")
|
val pack = className.substringBeforeLast(".", "")
|
||||||
"$methodName:$lineNumber, ${className.substringAfterLast(".")} " +
|
val packDisplay = if (pack.isNotEmpty()) "{$pack}" else ""
|
||||||
if (pack.isNotEmpty()) "{$pack}" else ""
|
"$methodName:$lineNumber, ${className.substringAfterLast(".")} $packDisplay"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,32 +80,35 @@ class SuspendStackFrameDescriptor(
|
|||||||
/**
|
/**
|
||||||
* For the case when no data inside frame is available
|
* For the case when no data inside frame is available
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
class CoroutineCreatedStackFrameDescriptor(val frame: StackTraceElement, proxy: StackFrameProxyImpl) :
|
class CoroutineCreatedStackFrameDescriptor(val frame: StackTraceElement, proxy: StackFrameProxyImpl) :
|
||||||
CoroutineStackFrameDescriptor(proxy) {
|
CoroutineStackFrameDescriptor(proxy) {
|
||||||
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener?): String {
|
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener?): String {
|
||||||
return with(frame) {
|
return with(frame) {
|
||||||
val pack = className.substringBeforeLast(".", "")
|
val pack = className.substringBeforeLast(".", "")
|
||||||
"$methodName:$lineNumber, ${className.substringAfterLast(".")} ${if (pack.isNotEmpty()) "{$pack}" else ""}"
|
val packDisplay = if (pack.isNotEmpty()) "{$pack}" else ""
|
||||||
|
"$methodName:$lineNumber, ${className.substringAfterLast(".")} $packDisplay"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getName() = null
|
override fun getName() = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
class AsyncStackFrameDescriptor(val infoData: CoroutineInfoData, val frame: StackFrameItem, proxy: StackFrameProxyImpl) :
|
class AsyncStackFrameDescriptor(val infoData: CoroutineInfoData, val frame: StackFrameItem, proxy: StackFrameProxyImpl) :
|
||||||
CoroutineStackFrameDescriptor(proxy) {
|
CoroutineStackFrameDescriptor(proxy) {
|
||||||
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener?): String {
|
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener?): String {
|
||||||
return with(frame) {
|
return with(frame) {
|
||||||
val pack = path().substringBeforeLast(".", "")
|
val pack = path().substringBeforeLast(".", "")
|
||||||
"${method()}:${line()}, ${path().substringAfterLast(".")} ${if (pack.isNotEmpty()) "{$pack}" else ""}"
|
val packDisplay = if (pack.isNotEmpty()) "{$pack}" else ""
|
||||||
|
"${method()}:${line()}, ${path().substringAfterLast(".")} $packDisplay"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getName() = frame.method()
|
override fun getName() = frame.method()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
open class CoroutineStackFrameDescriptor(proxy: StackFrameProxyImpl) : StackFrameDescriptorImpl(proxy, MethodsTracker()) {
|
open class CoroutineStackFrameDescriptor(proxy: StackFrameProxyImpl) : StackFrameDescriptorImpl(proxy, MethodsTracker()) {
|
||||||
override fun isExpandable() = false
|
override fun isExpandable() = false
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-7
@@ -17,8 +17,13 @@ import com.sun.jdi.*
|
|||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.LookupContinuation
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.LookupContinuation
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
||||||
|
|
||||||
class CoroutineStackTraceData(infoData: CoroutineInfoData, proxy: StackFrameProxyImpl, evalContext: EvaluationContextImpl, val frame: StackTraceElement)
|
@Deprecated("moved to XCoroutineView")
|
||||||
: CoroutineStackDescriptorData(infoData, proxy, evalContext) {
|
class CoroutineStackTraceData(
|
||||||
|
infoData: CoroutineInfoData,
|
||||||
|
proxy: StackFrameProxyImpl,
|
||||||
|
evalContext: EvaluationContextImpl,
|
||||||
|
val frame: StackTraceElement
|
||||||
|
) : CoroutineStackDescriptorData(infoData, proxy, evalContext) {
|
||||||
|
|
||||||
override fun hashCode() = frame.hashCode()
|
override fun hashCode() = frame.hashCode()
|
||||||
|
|
||||||
@@ -34,13 +39,19 @@ class CoroutineStackTraceData(infoData: CoroutineInfoData, proxy: StackFrameProx
|
|||||||
|
|
||||||
return if (continuation is ObjectReference)
|
return if (continuation is ObjectReference)
|
||||||
SuspendStackFrameDescriptor(infoData, frame, proxy, continuation)
|
SuspendStackFrameDescriptor(infoData, frame, proxy, continuation)
|
||||||
else
|
else
|
||||||
CoroutineCreatedStackFrameDescriptor(frame, proxy)
|
CoroutineCreatedStackFrameDescriptor(frame, proxy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CoroutineStackFrameData(infoData: CoroutineInfoData, proxy: StackFrameProxyImpl, evalContext: EvaluationContextImpl, val frame: StackFrameItem)
|
@Deprecated("moved to XCoroutineView")
|
||||||
: CoroutineStackDescriptorData(infoData, proxy, evalContext) {
|
class CoroutineStackFrameData(
|
||||||
|
infoData: CoroutineInfoData,
|
||||||
|
proxy: StackFrameProxyImpl,
|
||||||
|
evalContext: EvaluationContextImpl,
|
||||||
|
val frame: StackFrameItem
|
||||||
|
) :
|
||||||
|
CoroutineStackDescriptorData(infoData, proxy, evalContext) {
|
||||||
override fun createDescriptorImpl(project: Project): NodeDescriptorImpl =
|
override fun createDescriptorImpl(project: Project): NodeDescriptorImpl =
|
||||||
AsyncStackFrameDescriptor(infoData, frame, proxy)
|
AsyncStackFrameDescriptor(infoData, frame, proxy)
|
||||||
|
|
||||||
@@ -49,8 +60,12 @@ class CoroutineStackFrameData(infoData: CoroutineInfoData, proxy: StackFrameProx
|
|||||||
override fun equals(other: Any?) = other is CoroutineStackFrameData && frame == other.frame
|
override fun equals(other: Any?) = other is CoroutineStackFrameData && frame == other.frame
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class CoroutineStackDescriptorData(val infoData: CoroutineInfoData, val proxy: StackFrameProxyImpl, val evalContext: EvaluationContextImpl)
|
@Deprecated("moved to XCoroutineView")
|
||||||
: DescriptorData<NodeDescriptorImpl>() {
|
abstract class CoroutineStackDescriptorData(
|
||||||
|
val infoData: CoroutineInfoData,
|
||||||
|
val proxy: StackFrameProxyImpl,
|
||||||
|
val evalContext: EvaluationContextImpl
|
||||||
|
) : DescriptorData<NodeDescriptorImpl>() {
|
||||||
|
|
||||||
override fun getDisplayKey(): DisplayKey<NodeDescriptorImpl> = SimpleDisplayKey(infoData)
|
override fun getDisplayKey(): DisplayKey<NodeDescriptorImpl> = SimpleDisplayKey(infoData)
|
||||||
}
|
}
|
||||||
@@ -58,6 +73,7 @@ abstract class CoroutineStackDescriptorData(val infoData: CoroutineInfoData, val
|
|||||||
/**
|
/**
|
||||||
* Describes coroutine itself in the tree (name: STATE), has children if stacktrace is not empty (state = CREATED)
|
* Describes coroutine itself in the tree (name: STATE), has children if stacktrace is not empty (state = CREATED)
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
class CoroutineDescriptorData(private val infoData: CoroutineInfoData) : DescriptorData<CoroutineDescriptorImpl>() {
|
class CoroutineDescriptorData(private val infoData: CoroutineInfoData) : DescriptorData<CoroutineDescriptorImpl>() {
|
||||||
|
|
||||||
override fun createDescriptorImpl(project: Project) =
|
override fun createDescriptorImpl(project: Project) =
|
||||||
|
|||||||
+3
-3
@@ -3,7 +3,7 @@
|
|||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* 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.coroutines
|
package org.jetbrains.kotlin.idea.debugger.coroutines.proxy
|
||||||
|
|
||||||
import com.intellij.debugger.DebuggerContext
|
import com.intellij.debugger.DebuggerContext
|
||||||
import com.intellij.debugger.engine.JavaValue
|
import com.intellij.debugger.engine.JavaValue
|
||||||
@@ -37,7 +37,7 @@ class AsyncStackTraceContext(
|
|||||||
val continuation = locateContinuation() ?: return emptyList()
|
val continuation = locateContinuation() ?: return emptyList()
|
||||||
val frames = mutableListOf<CoroutineAsyncStackFrameItem>()
|
val frames = mutableListOf<CoroutineAsyncStackFrameItem>()
|
||||||
collectFramesRecursively(continuation, frames)
|
collectFramesRecursively(continuation, frames)
|
||||||
return frames;
|
return frames
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun locateContinuation() : ObjectReference? {
|
private fun locateContinuation() : ObjectReference? {
|
||||||
@@ -105,7 +105,7 @@ class AsyncStackTraceContext(
|
|||||||
fun getSpilledVariables(continuation: ObjectReference): List<XNamedValue>? {
|
fun getSpilledVariables(continuation: ObjectReference): List<XNamedValue>? {
|
||||||
|
|
||||||
val rawSpilledVariables =
|
val rawSpilledVariables =
|
||||||
context.invokeMethodAsArray(debugMetadataKtType, "getSpilledVariableFieldMapping", "(Lkotlin/coroutines/jvm/internal/BaseContinuationImpl;)[Ljava/lang/String;", continuation) as? ArrayReference ?: return null
|
context.invokeMethodAsArray(debugMetadataKtType, "getSpilledVariableFieldMapping", "(Lkotlin/coroutines/jvm/internal/BaseContinuationImpl;)[Ljava/lang/String;", continuation) ?: return null
|
||||||
|
|
||||||
context.keepReference(rawSpilledVariables)
|
context.keepReference(rawSpilledVariables)
|
||||||
|
|
||||||
+10
-23
@@ -4,12 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutines.proxy
|
package org.jetbrains.kotlin.idea.debugger.coroutines.proxy
|
||||||
|
|
||||||
import com.intellij.debugger.engine.DebuggerManagerThreadImpl
|
|
||||||
import com.intellij.debugger.engine.SuspendContextImpl
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||||
import com.intellij.debugger.impl.PrioritizedTask
|
|
||||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||||
import com.intellij.xdebugger.XDebugProcess
|
import com.intellij.xdebugger.frame.XSuspendContext
|
||||||
import com.sun.jdi.*
|
import com.sun.jdi.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.command.CoroutineBuilder
|
import org.jetbrains.kotlin.idea.debugger.coroutines.command.CoroutineBuilder
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.view.CoroutineInfoCache
|
import org.jetbrains.kotlin.idea.debugger.coroutines.view.CoroutineInfoCache
|
||||||
@@ -17,16 +15,10 @@ import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
|||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.util.logger
|
import org.jetbrains.kotlin.idea.debugger.coroutines.util.logger
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
||||||
|
|
||||||
fun SuspendContextImpl.createEvaluationContext() =
|
class CoroutinesDebugProbesProxy(val suspendContext: XSuspendContext) {
|
||||||
EvaluationContextImpl(this, this.frameProxy)
|
|
||||||
|
|
||||||
class CoroutinesDebugProbesProxy(val suspendContext: SuspendContextImpl) {
|
|
||||||
private val log by logger
|
private val log by logger
|
||||||
|
|
||||||
// @TODO refactor to extract initialization logic
|
private var executionContext: ExecutionContext = executionContext()
|
||||||
private var executionContext: ExecutionContext = ExecutionContext(
|
|
||||||
EvaluationContextImpl(suspendContext, suspendContext.frameProxy),
|
|
||||||
suspendContext.frameProxy as StackFrameProxyImpl)
|
|
||||||
// might want to use inner class but also having to monitor order of fields
|
// might want to use inner class but also having to monitor order of fields
|
||||||
private var refs: ProcessReferences = ProcessReferences(executionContext)
|
private var refs: ProcessReferences = ProcessReferences(executionContext)
|
||||||
|
|
||||||
@@ -89,26 +81,16 @@ class CoroutinesDebugProbesProxy(val suspendContext: SuspendContextImpl) {
|
|||||||
val state = getState(instance)
|
val state = getState(instance)
|
||||||
val thread = getLastObservedThread(instance, refs.lastObservedThreadFieldRef)
|
val thread = getLastObservedThread(instance, refs.lastObservedThreadFieldRef)
|
||||||
val lastObservedFrameFieldRef = instance.getValue(refs.lastObservedFrameFieldRef) as? ObjectReference
|
val lastObservedFrameFieldRef = instance.getValue(refs.lastObservedFrameFieldRef) as? ObjectReference
|
||||||
|
val stackTrace = getStackTrace(instance)
|
||||||
return CoroutineInfoData(
|
return CoroutineInfoData(
|
||||||
name,
|
name,
|
||||||
CoroutineInfoData.State.valueOf(state),
|
CoroutineInfoData.State.valueOf(state),
|
||||||
getThreadName(instance),
|
stackTrace,
|
||||||
getThreadState(instance),
|
|
||||||
|
|
||||||
getStackTrace(instance),
|
|
||||||
thread,
|
thread,
|
||||||
lastObservedFrameFieldRef
|
lastObservedFrameFieldRef
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getThreadName(instance: ObjectReference) : String {
|
|
||||||
return "thread name"
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getThreadState(instance: ObjectReference) : Int {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getName(
|
private fun getName(
|
||||||
info: ObjectReference // CoroutineInfo instance
|
info: ObjectReference // CoroutineInfo instance
|
||||||
): String {
|
): String {
|
||||||
@@ -203,6 +185,11 @@ class CoroutinesDebugProbesProxy(val suspendContext: SuspendContextImpl) {
|
|||||||
private fun sizeOf(args: ObjectReference): Int =
|
private fun sizeOf(args: ObjectReference): Int =
|
||||||
(executionContext.invokeMethod(args, refs.sizeRef, emptyList()) as IntegerValue).value()
|
(executionContext.invokeMethod(args, refs.sizeRef, emptyList()) as IntegerValue).value()
|
||||||
|
|
||||||
|
private fun executionContext() : ExecutionContext {
|
||||||
|
val evaluationContextImpl = EvaluationContextImpl(suspendContext as SuspendContextImpl, suspendContext.frameProxy)
|
||||||
|
return ExecutionContext(evaluationContextImpl, suspendContext.frameProxy as StackFrameProxyImpl)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @TODO refactor later
|
* @TODO refactor later
|
||||||
* Holds ClassTypes, Methods, ObjectReferences and Fields for a particular jvm
|
* Holds ClassTypes, Methods, ObjectReferences and Fields for a particular jvm
|
||||||
|
|||||||
+35
-11
@@ -8,25 +8,49 @@ package org.jetbrains.kotlin.idea.debugger.coroutines.proxy
|
|||||||
import com.intellij.debugger.engine.DebugProcessImpl
|
import com.intellij.debugger.engine.DebugProcessImpl
|
||||||
import com.intellij.debugger.engine.SuspendContextImpl
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
import com.intellij.debugger.engine.events.DebuggerCommandImpl
|
import com.intellij.debugger.engine.events.DebuggerCommandImpl
|
||||||
|
import com.intellij.debugger.engine.events.SuspendContextCommandImpl
|
||||||
import com.intellij.debugger.impl.PrioritizedTask
|
import com.intellij.debugger.impl.PrioritizedTask
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.application.ModalityState
|
||||||
import com.intellij.openapi.util.Computable
|
import com.intellij.openapi.util.Computable
|
||||||
|
import com.intellij.xdebugger.frame.XSuspendContext
|
||||||
|
import java.awt.Component
|
||||||
|
|
||||||
class ManagerThreadExecutor(val debugProcess: DebugProcessImpl, val priority: PrioritizedTask.Priority = PrioritizedTask.Priority.NORMAL) {
|
class ManagerThreadExecutor(val debugProcess: DebugProcessImpl) {
|
||||||
fun schedule(f: () -> Unit) {
|
fun on(suspendContext: SuspendContextImpl, priority: PrioritizedTask.Priority = PrioritizedTask.Priority.NORMAL) =
|
||||||
val runnable = object : Runnable {
|
ManagerThreadExecutorInstance(suspendContext, priority)
|
||||||
override fun run() {f()}
|
|
||||||
|
fun on(suspendContext: XSuspendContext, priority: PrioritizedTask.Priority = PrioritizedTask.Priority.NORMAL) =
|
||||||
|
ManagerThreadExecutorInstance(suspendContext as SuspendContextImpl, priority)
|
||||||
|
|
||||||
|
inner class ManagerThreadExecutorInstance(
|
||||||
|
val suspendContext: SuspendContextImpl,
|
||||||
|
val priority: PrioritizedTask.Priority = PrioritizedTask.Priority.NORMAL
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun schedule(f: (SuspendContextImpl) -> Unit) {
|
||||||
|
val suspendContextCommand = object : SuspendContextCommandImpl(suspendContext) {
|
||||||
|
override fun getPriority() = this@ManagerThreadExecutorInstance.priority
|
||||||
|
|
||||||
|
override fun contextAction(suspendContext: SuspendContextImpl) {
|
||||||
|
f(suspendContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debugProcess.managerThread.invoke(suspendContextCommand)
|
||||||
}
|
}
|
||||||
debugProcess.managerThread.schedule(priority, runnable)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun schedule(f: DebuggerCommandImpl) {
|
|
||||||
debugProcess.managerThread.schedule(f)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApplicationThreadExecutor() {
|
class ApplicationThreadExecutor {
|
||||||
fun <T> invoke(f: () -> T) : T {
|
fun <T> readAction(f: () -> T): T {
|
||||||
return ApplicationManager.getApplication().runReadAction(Computable(f))
|
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() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-73
@@ -5,33 +5,22 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutines.util
|
package org.jetbrains.kotlin.idea.debugger.coroutines.util
|
||||||
|
|
||||||
import com.intellij.debugger.DebuggerManagerEx
|
|
||||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
|
||||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
|
||||||
import com.intellij.debugger.ui.impl.watch.MethodsTracker
|
import com.intellij.debugger.ui.impl.watch.MethodsTracker
|
||||||
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
|
|
||||||
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
|
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Computable
|
import com.intellij.openapi.ui.MessageType
|
||||||
import com.intellij.psi.JavaPsiFacade
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.xdebugger.XDebuggerUtil
|
import com.intellij.xdebugger.XDebuggerUtil
|
||||||
import com.intellij.xdebugger.XSourcePosition
|
import com.intellij.xdebugger.XSourcePosition
|
||||||
import com.sun.jdi.ClassType
|
import com.intellij.xdebugger.impl.XDebuggerManagerImpl
|
||||||
import javaslang.control.Either
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.AsyncStackFrameDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.SuspendStackFrameDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.ApplicationThreadExecutor
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.ApplicationThreadExecutor
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.LookupContinuation
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
|
||||||
|
|
||||||
fun getPosition(stackTraceElement: StackTraceElement, project: Project): XSourcePosition? {
|
fun getPosition(stackTraceElement: StackTraceElement, project: Project): XSourcePosition? {
|
||||||
val psiFacade = JavaPsiFacade.getInstance(project)
|
val psiFacade = JavaPsiFacade.getInstance(project)
|
||||||
|
|
||||||
val psiClass = ApplicationThreadExecutor().invoke {
|
val psiClass = ApplicationThreadExecutor().readAction {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
psiFacade.findClass(
|
psiFacade.findClass(
|
||||||
stackTraceElement.className.substringBefore("$"), // find outer class, for which psi exists TODO
|
stackTraceElement.className.substringBefore("$"), // find outer class, for which psi exists TODO
|
||||||
@@ -44,64 +33,10 @@ fun getPosition(stackTraceElement: StackTraceElement, project: Project): XSource
|
|||||||
return XDebuggerUtil.getInstance().createPosition(classFile, lineNumber)
|
return XDebuggerUtil.getInstance().createPosition(classFile, lineNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
private fun buildSuspendStackFrameChildren(descriptor: SuspendStackFrameDescriptor, frame: StackTraceElement, project: Project) {
|
|
||||||
val context = DebuggerManagerEx.getInstanceEx(project).context
|
|
||||||
val pos = getPosition(frame) ?: return
|
|
||||||
context.debugProcess?.managerThread?.schedule(object : SuspendContextCommandImpl(context.suspendContext) {
|
|
||||||
override fun contextAction() {
|
|
||||||
val (stack, stackFrame) = createSyntheticStackFrame(descriptor, pos) ?: return
|
|
||||||
val action: () -> Unit = { context.debuggerSession?.xDebugSession?.setCurrentStackFrame(stack, stackFrame) }
|
|
||||||
ApplicationManager.getApplication()
|
|
||||||
.invokeLater(action, ModalityState.stateForComponent(this@CoroutinesDebuggerTree))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildAsyncStackFrameChildren(descriptor: AsyncStackFrameDescriptor, process: DebugProcessImpl) {
|
|
||||||
process.managerThread?.schedule(object : DebuggerCommandImpl() {
|
|
||||||
override fun action() {
|
|
||||||
val context = DebuggerManagerEx.getInstanceEx(project).context
|
|
||||||
val proxy = ThreadReferenceProxyImpl(
|
|
||||||
process.virtualMachineProxy,
|
|
||||||
descriptor.state.thread // is not null because it's a running coroutine
|
|
||||||
)
|
|
||||||
val executionStack = JavaExecutionStack(proxy, process, false)
|
|
||||||
executionStack.initTopFrame()
|
|
||||||
val frame = descriptor.frame.createFrame(process)
|
|
||||||
DebuggerUIUtil.invokeLater {
|
|
||||||
context.debuggerSession?.xDebugSession?.setCurrentStackFrame(
|
|
||||||
executionStack,
|
|
||||||
frame
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildEmptyStackFrameChildren(descriptor: EmptyStackFrameDescriptor, project: Project) {
|
|
||||||
val position = getPosition(descriptor.frame) ?: return
|
|
||||||
val context = DebuggerManagerEx.getInstanceEx(project).context
|
|
||||||
val suspendContext = context.suspendContext ?: return
|
|
||||||
val proxy = suspendContext.thread ?: return
|
|
||||||
context.debugProcess?.managerThread?.schedule(object : DebuggerCommandImpl() {
|
|
||||||
override fun action() {
|
|
||||||
val executionStack =
|
|
||||||
JavaExecutionStack(proxy, context.debugProcess!!, false)
|
|
||||||
executionStack.initTopFrame()
|
|
||||||
val frame = SyntheticStackFrame(descriptor, emptyList(), position)
|
|
||||||
val action: () -> Unit =
|
|
||||||
{ context.debuggerSession?.xDebugSession?.setCurrentStackFrame(executionStack, frame) }
|
|
||||||
ApplicationManager.getApplication()
|
|
||||||
.invokeLater(action, ModalityState.stateForComponent(this@CoroutinesDebuggerTree))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class EmptyStackFrameDescriptor(val frame: StackTraceElement, proxy: StackFrameProxyImpl) :
|
class EmptyStackFrameDescriptor(val frame: StackTraceElement, proxy: StackFrameProxyImpl) :
|
||||||
StackFrameDescriptorImpl(proxy, MethodsTracker()) {
|
StackFrameDescriptorImpl(proxy, MethodsTracker())
|
||||||
|
|
||||||
|
class ProjectNotification(val project: Project) {
|
||||||
|
fun error(message: String) =
|
||||||
|
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(message, MessageType.ERROR).notify(project)
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-10
@@ -5,11 +5,13 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutines.view
|
package org.jetbrains.kotlin.idea.debugger.coroutines.view
|
||||||
|
|
||||||
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.xdebugger.XDebugSession
|
import com.intellij.xdebugger.XDebugSession
|
||||||
import com.intellij.xdebugger.XDebugSessionListener
|
import com.intellij.xdebugger.XDebugSessionListener
|
||||||
import com.intellij.xdebugger.frame.XSuspendContext
|
import com.intellij.xdebugger.frame.XSuspendContext
|
||||||
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
|
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
|
||||||
|
import com.sun.jdi.request.EventRequest
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.util.logger
|
import org.jetbrains.kotlin.idea.debugger.coroutines.util.logger
|
||||||
|
|
||||||
class CoroutineViewDebugSessionListener(
|
class CoroutineViewDebugSessionListener(
|
||||||
@@ -19,48 +21,56 @@ class CoroutineViewDebugSessionListener(
|
|||||||
val log by logger
|
val log by logger
|
||||||
|
|
||||||
override fun sessionPaused() {
|
override fun sessionPaused() {
|
||||||
|
log.info("XListener: sessionPaused")
|
||||||
val suspendContext = session.suspendContext ?: return requestClear()
|
val suspendContext = session.suspendContext ?: return requestClear()
|
||||||
xCoroutineView.forceClear()
|
xCoroutineView.alarm.cancel()
|
||||||
renew(suspendContext)
|
renew(suspendContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sessionResumed() {
|
override fun sessionResumed() {
|
||||||
|
xCoroutineView.saveState()
|
||||||
|
log.info("XListener: sessionResumed")
|
||||||
val suspendContext = session.suspendContext ?: return requestClear()
|
val suspendContext = session.suspendContext ?: return requestClear()
|
||||||
log.warn("sessionResumed ${session}")
|
|
||||||
renew(suspendContext)
|
renew(suspendContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sessionStopped() {
|
override fun sessionStopped() {
|
||||||
|
log.info("XListener: sessionStopped")
|
||||||
val suspendContext = session.suspendContext ?: return requestClear()
|
val suspendContext = session.suspendContext ?: return requestClear()
|
||||||
log.warn("sessionStopped ${session}")
|
|
||||||
renew(suspendContext)
|
renew(suspendContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stackFrameChanged() {
|
override fun stackFrameChanged() {
|
||||||
val suspendContext = session.suspendContext ?: return requestClear()
|
xCoroutineView.saveState()
|
||||||
log.warn("stackFrameChanged ${session}")
|
log.info("XListener: stackFrameChanged")
|
||||||
renew(suspendContext)
|
// val suspendContext = session.suspendContext ?: return requestClear()
|
||||||
|
// log.warn("stackFrameChanged ${session}")
|
||||||
|
// renew(suspendContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beforeSessionResume() {
|
override fun beforeSessionResume() {
|
||||||
|
log.info("XListener: beforeSessionResume")
|
||||||
log.warn("beforeSessionResume ${session}")
|
log.warn("beforeSessionResume ${session}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun settingsChanged() {
|
override fun settingsChanged() {
|
||||||
|
log.info("XListener: settingsChanged")
|
||||||
val suspendContext = session.suspendContext ?: return requestClear()
|
val suspendContext = session.suspendContext ?: return requestClear()
|
||||||
log.warn("settingsChanged ${session}")
|
log.warn("settingsChanged ${session}")
|
||||||
renew(suspendContext)
|
renew(suspendContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun renew(suspendContext: XSuspendContext) {
|
fun renew(suspendContext: XSuspendContext) {
|
||||||
DebuggerUIUtil.invokeLater {
|
if(suspendContext is SuspendContextImpl && suspendContext.suspendPolicy == EventRequest.SUSPEND_ALL) {
|
||||||
xCoroutineView.panel.tree.setRoot(xCoroutineView.createRoot(suspendContext), false)
|
DebuggerUIUtil.invokeLater {
|
||||||
|
xCoroutineView.renewRoot(suspendContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestClear() {
|
private fun requestClear() {
|
||||||
if (ApplicationManager.getApplication().isUnitTestMode) { // no delay in tests
|
if (ApplicationManager.getApplication().isUnitTestMode) { // no delay in tests
|
||||||
xCoroutineView.clear()
|
xCoroutineView.resetRoot()
|
||||||
} else {
|
} else {
|
||||||
xCoroutineView.alarm.cancelAndRequest()
|
xCoroutineView.alarm.cancelAndRequest()
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-9
@@ -28,9 +28,8 @@ import java.util.*
|
|||||||
/**
|
/**
|
||||||
* Tree of coroutines for [CoroutinesPanel]
|
* Tree of coroutines for [CoroutinesPanel]
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
class CoroutinesDebuggerTree(project: Project) : ThreadsDebuggerTree(project) {
|
class CoroutinesDebuggerTree(project: Project) : ThreadsDebuggerTree(project) {
|
||||||
private val log by logger
|
|
||||||
|
|
||||||
// called on every step/frame
|
// called on every step/frame
|
||||||
override fun build(context: DebuggerContextImpl) {
|
override fun build(context: DebuggerContextImpl) {
|
||||||
val session = context.debuggerSession
|
val session = context.debuggerSession
|
||||||
@@ -38,9 +37,11 @@ class CoroutinesDebuggerTree(project: Project) : ThreadsDebuggerTree(project) {
|
|||||||
val command = RefreshCoroutinesTreeCommand(context, this)
|
val command = RefreshCoroutinesTreeCommand(context, this)
|
||||||
val debuggerSessionState = session?.state ?: DebuggerSession.State.DISPOSED
|
val debuggerSessionState = session?.state ?: DebuggerSession.State.DISPOSED
|
||||||
|
|
||||||
if (ApplicationManager.getApplication().isUnitTestMode || debuggerSessionState in EnumSet.of(DebuggerSession.State.PAUSED, DebuggerSession.State.RUNNING)) {
|
if (ApplicationManager.getApplication().isUnitTestMode || debuggerSessionState in EnumSet
|
||||||
|
.of(DebuggerSession.State.PAUSED, DebuggerSession.State.RUNNING)
|
||||||
|
) {
|
||||||
showMessage(MessageDescriptor.EVALUATING)
|
showMessage(MessageDescriptor.EVALUATING)
|
||||||
ManagerThreadExecutor(context.debugProcess!!).schedule(command)
|
context.debugProcess!!.managerThread.schedule(command)
|
||||||
} else {
|
} else {
|
||||||
showMessage(session?.stateDescription ?: DebuggerBundle.message("status.debug.stopped"))
|
showMessage(session?.stateDescription ?: DebuggerBundle.message("status.debug.stopped"))
|
||||||
}
|
}
|
||||||
@@ -48,7 +49,7 @@ class CoroutinesDebuggerTree(project: Project) : ThreadsDebuggerTree(project) {
|
|||||||
|
|
||||||
|
|
||||||
override fun getBuildNodeCommand(node: DebuggerTreeNodeImpl): DebuggerCommandImpl? {
|
override fun getBuildNodeCommand(node: DebuggerTreeNodeImpl): DebuggerCommandImpl? {
|
||||||
return when(val descriptor = node.descriptor) {
|
return when (val descriptor = node.descriptor) {
|
||||||
is CoroutineDescriptorImpl ->
|
is CoroutineDescriptorImpl ->
|
||||||
CoroutineBuildFrameCommand(node, descriptor, myNodeManager, debuggerContext)
|
CoroutineBuildFrameCommand(node, descriptor, myNodeManager, debuggerContext)
|
||||||
is CreationFramesDescriptor ->
|
is CreationFramesDescriptor ->
|
||||||
@@ -71,7 +72,8 @@ class CoroutinesDebuggerTree(project: Project) : ThreadsDebuggerTree(project) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CoroutineInfoCache(val cache: MutableList<CoroutineInfoData> = mutableListOf(), var state: CacheState = CacheState.INIT
|
class CoroutineInfoCache(
|
||||||
|
val cache: MutableList<CoroutineInfoData> = mutableListOf(), var state: CacheState = CacheState.INIT
|
||||||
) {
|
) {
|
||||||
fun ok(infoList: List<CoroutineInfoData>) {
|
fun ok(infoList: List<CoroutineInfoData>) {
|
||||||
cache.clear()
|
cache.clear()
|
||||||
@@ -84,11 +86,11 @@ class CoroutineInfoCache(val cache: MutableList<CoroutineInfoData> = mutableList
|
|||||||
state = CacheState.FAIL
|
state = CacheState.FAIL
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isOk() : Boolean {
|
fun isOk(): Boolean {
|
||||||
return state == CacheState.OK
|
return state == CacheState.OK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class CacheState() {
|
enum class CacheState {
|
||||||
OK,FAIL,INIT
|
OK, FAIL, INIT
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.idea.debugger.coroutines.CoroutineDebuggerActions
|
|||||||
/**
|
/**
|
||||||
* Added into ui in [CoroutineProjectConnectionListener.registerCoroutinesPanel]
|
* Added into ui in [CoroutineProjectConnectionListener.registerCoroutinesPanel]
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("moved to XCoroutineView")
|
||||||
class CoroutinesPanel(project: Project, stateManager: DebuggerStateManager) : ThreadsPanel(project, stateManager) {
|
class CoroutinesPanel(project: Project, stateManager: DebuggerStateManager) : ThreadsPanel(project, stateManager) {
|
||||||
|
|
||||||
override fun createTreeView(): DebuggerTree {
|
override fun createTreeView(): DebuggerTree {
|
||||||
|
|||||||
+177
@@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 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.coroutines.view
|
||||||
|
|
||||||
|
import com.intellij.debugger.impl.DebuggerUtilsEx
|
||||||
|
import com.intellij.debugger.settings.ThreadsViewSettings
|
||||||
|
import com.intellij.icons.AllIcons
|
||||||
|
import com.intellij.openapi.editor.HighlighterColors
|
||||||
|
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||||
|
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||||
|
import com.intellij.ui.ColoredTextContainer
|
||||||
|
import com.intellij.ui.SimpleColoredComponent
|
||||||
|
import com.intellij.ui.SimpleTextAttributes
|
||||||
|
import com.intellij.xdebugger.frame.presentation.XValuePresentation
|
||||||
|
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
|
||||||
|
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants
|
||||||
|
import com.sun.jdi.Location
|
||||||
|
import com.sun.jdi.ReferenceType
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
||||||
|
import javax.swing.Icon
|
||||||
|
|
||||||
|
class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
|
||||||
|
val texts = mutableListOf<String>()
|
||||||
|
val textKeyAttributes = mutableListOf<TextAttributesKey>()
|
||||||
|
|
||||||
|
constructor(icon: Icon?, hasChildrens: Boolean, text: String) : this(icon, hasChildrens) {
|
||||||
|
append(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun append(value: String) {
|
||||||
|
texts.add(value)
|
||||||
|
textKeyAttributes.add(CoroutineDebuggerColors.REGULAR_ATTRIBUTES)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun appendValue(value: String) {
|
||||||
|
texts.add(value)
|
||||||
|
textKeyAttributes.add(CoroutineDebuggerColors.VALUE_ATTRIBUTES)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
component.append(text, when(attribute) {
|
||||||
|
CoroutineDebuggerColors.REGULAR_ATTRIBUTES -> SimpleTextAttributes.REGULAR_ATTRIBUTES
|
||||||
|
CoroutineDebuggerColors.VALUE_ATTRIBUTES -> XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES
|
||||||
|
else -> SimpleTextAttributes.REGULAR_ATTRIBUTES
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forEachTextBlock(f: (Pair<String, TextAttributesKey>) -> Unit) {
|
||||||
|
for (pair in texts zip textKeyAttributes)
|
||||||
|
f(pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun simpleString(): String {
|
||||||
|
val component = SimpleColoredComponent()
|
||||||
|
appendToComponent(component)
|
||||||
|
return component.getCharSequence(false).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun valuePresentation(): XValuePresentation {
|
||||||
|
return object : XValuePresentation() {
|
||||||
|
override fun isShowName() = false
|
||||||
|
|
||||||
|
override fun getSeparator() = ""
|
||||||
|
|
||||||
|
override fun renderValue(renderer: XValueTextRenderer) {
|
||||||
|
forEachTextBlock {
|
||||||
|
renderer.renderValue(it.first, it.second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CoroutineDebuggerColors {
|
||||||
|
companion object {
|
||||||
|
val REGULAR_ATTRIBUTES =
|
||||||
|
TextAttributesKey.createTextAttributesKey("REGULAR_ATTRIBUTES", HighlighterColors.NO_HIGHLIGHTING)
|
||||||
|
val VALUE_ATTRIBUTES =
|
||||||
|
TextAttributesKey.createTextAttributesKey("VALUE_ATTRIBUTES", CodeInsightColors.WARNINGS_ATTRIBUTES)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleColoredTextIconPresentationRenderer {
|
||||||
|
private val settings: ThreadsViewSettings = ThreadsViewSettings.getInstance()
|
||||||
|
|
||||||
|
fun render(infoData: CoroutineInfoData): SimpleColoredTextIcon {
|
||||||
|
val thread = infoData.activeThread
|
||||||
|
val name = thread?.name()?.substringBefore(" @${infoData.name}") ?: ""
|
||||||
|
val threadState = if (thread != null) DebuggerUtilsEx.getThreadStatusText(thread.status()) else ""
|
||||||
|
|
||||||
|
val icon = when (infoData.state) {
|
||||||
|
CoroutineInfoData.State.SUSPENDED -> AllIcons.Debugger.ThreadSuspended
|
||||||
|
CoroutineInfoData.State.RUNNING -> AllIcons.Debugger.ThreadRunning
|
||||||
|
CoroutineInfoData.State.CREATED -> AllIcons.Debugger.ThreadStates.Idle
|
||||||
|
}
|
||||||
|
|
||||||
|
val label = SimpleColoredTextIcon(icon, true)
|
||||||
|
label.append("\"")
|
||||||
|
label.appendValue(infoData.name)
|
||||||
|
label.append("\": ${infoData.state}")
|
||||||
|
if(name.isNotEmpty()) {
|
||||||
|
label.append(" on thread \"")
|
||||||
|
label.appendValue(name)
|
||||||
|
label.append("\": $threadState")
|
||||||
|
}
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taken from #StackFrameDescriptorImpl.calcRepresentation
|
||||||
|
*/
|
||||||
|
fun render(location: Location): SimpleColoredTextIcon {
|
||||||
|
val label = SimpleColoredTextIcon(null, false)
|
||||||
|
DebuggerUIUtil.getColorScheme(null)
|
||||||
|
if (location.method() != null) {
|
||||||
|
val myName = location.method().name()
|
||||||
|
val methodDisplay = if (settings.SHOW_ARGUMENTS_TYPES)
|
||||||
|
DebuggerUtilsEx.methodNameWithArguments(location.method())
|
||||||
|
else
|
||||||
|
myName
|
||||||
|
label.appendValue(methodDisplay)
|
||||||
|
}
|
||||||
|
if (settings.SHOW_LINE_NUMBER) {
|
||||||
|
label.append(":")
|
||||||
|
label.append("" + DebuggerUtilsEx.getLineNumber(location, false))
|
||||||
|
}
|
||||||
|
if (settings.SHOW_CLASS_NAME) {
|
||||||
|
val name: String?
|
||||||
|
name = try {
|
||||||
|
val refType: ReferenceType = location.declaringType()
|
||||||
|
refType.name()
|
||||||
|
} catch (e: InternalError) {
|
||||||
|
e.toString()
|
||||||
|
}
|
||||||
|
if (name != null) {
|
||||||
|
label.append(", ")
|
||||||
|
val dotIndex = name.lastIndexOf('.')
|
||||||
|
if (dotIndex < 0) {
|
||||||
|
label.append(name)
|
||||||
|
} else {
|
||||||
|
label.append(name.substring(dotIndex + 1))
|
||||||
|
if (settings.SHOW_PACKAGE_NAME) {
|
||||||
|
label.append(" (${name.substring( 0, dotIndex)})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (settings.SHOW_SOURCE_NAME) {
|
||||||
|
label.append(", ")
|
||||||
|
val sourceName = DebuggerUtilsEx.getSourceName(location) { e: Throwable? -> "Unknown Source" }
|
||||||
|
label.append(sourceName)
|
||||||
|
}
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
fun renderCreationNode(infoData: CoroutineInfoData) =
|
||||||
|
SimpleColoredTextIcon(AllIcons.Debugger.ThreadSuspended, true, "Creation stack frame of ${infoData.name}")
|
||||||
|
|
||||||
|
fun renderErrorNode(error: String) =
|
||||||
|
SimpleColoredTextIcon(AllIcons.Actions.Lightning,false, error)
|
||||||
|
|
||||||
|
fun renderRoorNode(text: String) =
|
||||||
|
SimpleColoredTextIcon(null, true, text)
|
||||||
|
|
||||||
|
fun renderGroup(groupName: String) =
|
||||||
|
SimpleColoredTextIcon(AllIcons.Debugger.ThreadGroup,true, groupName)
|
||||||
|
}
|
||||||
+241
-174
@@ -5,54 +5,66 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutines.view
|
package org.jetbrains.kotlin.idea.debugger.coroutines.view
|
||||||
|
|
||||||
|
import com.intellij.debugger.DebuggerManagerEx
|
||||||
import com.intellij.debugger.engine.DebugProcessImpl
|
import com.intellij.debugger.engine.DebugProcessImpl
|
||||||
import com.intellij.debugger.engine.JavaDebugProcess
|
import com.intellij.debugger.engine.JavaDebugProcess
|
||||||
|
import com.intellij.debugger.engine.JavaExecutionStack
|
||||||
import com.intellij.debugger.engine.SuspendContextImpl
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
import com.intellij.debugger.impl.DebuggerUtilsEx
|
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
||||||
import com.intellij.debugger.settings.ThreadsViewSettings
|
|
||||||
import com.intellij.icons.AllIcons
|
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.JavaPsiFacade
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.ui.DoubleClickListener
|
||||||
import com.intellij.ui.OnePixelSplitter
|
import com.intellij.ui.OnePixelSplitter
|
||||||
import com.intellij.ui.SimpleColoredComponent
|
|
||||||
import com.intellij.ui.SimpleColoredText
|
|
||||||
import com.intellij.ui.SimpleTextAttributes
|
|
||||||
import com.intellij.util.SingleAlarm
|
import com.intellij.util.SingleAlarm
|
||||||
import com.intellij.xdebugger.XDebugSession
|
import com.intellij.xdebugger.XDebugSession
|
||||||
|
import com.intellij.xdebugger.XDebuggerUtil
|
||||||
import com.intellij.xdebugger.XSourcePosition
|
import com.intellij.xdebugger.XSourcePosition
|
||||||
import com.intellij.xdebugger.frame.*
|
import com.intellij.xdebugger.frame.*
|
||||||
import com.intellij.xdebugger.frame.presentation.XRegularValuePresentation
|
|
||||||
import com.intellij.xdebugger.frame.presentation.XValuePresentation
|
|
||||||
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
|
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
|
||||||
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants
|
|
||||||
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
|
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
|
||||||
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreePanel
|
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreePanel
|
||||||
|
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeRestorer
|
||||||
|
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState
|
||||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode
|
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode
|
||||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl
|
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl
|
||||||
import com.sun.jdi.Location
|
import com.sun.jdi.ClassType
|
||||||
import com.sun.jdi.ReferenceType
|
import javaslang.control.Either
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.CoroutineAsyncStackTraceProvider
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.CoroutineDebuggerContentInfo
|
import org.jetbrains.kotlin.idea.debugger.coroutines.CoroutineDebuggerContentInfo
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.CoroutineDebuggerContentInfo.Companion.XCOROUTINE_POPUP_ACTION_GROUP
|
import org.jetbrains.kotlin.idea.debugger.coroutines.CoroutineDebuggerContentInfo.Companion.XCOROUTINE_POPUP_ACTION_GROUP
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.command.CoroutineStackFrameItem
|
import org.jetbrains.kotlin.idea.debugger.coroutines.command.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.command.CreationCoroutineStackFrameItem
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.CoroutineInfoData
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.SuspendStackFrameDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.data.SyntheticStackFrame
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.ApplicationThreadExecutor
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.AsyncStackTraceContext
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.CoroutinesDebugProbesProxy
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.CoroutinesDebugProbesProxy
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.ManagerThreadExecutor
|
import org.jetbrains.kotlin.idea.debugger.coroutines.proxy.ManagerThreadExecutor
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.util.CreateContentParams
|
import org.jetbrains.kotlin.idea.debugger.coroutines.util.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.util.CreateContentParamsProvider
|
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.util.XDebugSessionListenerProvider
|
import java.awt.event.KeyAdapter
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutines.util.logger
|
import java.awt.event.KeyEvent
|
||||||
import javax.swing.Icon
|
import java.awt.event.MouseEvent
|
||||||
|
|
||||||
|
|
||||||
class XCoroutineView(val project: Project, val session: XDebugSession) :
|
class XCoroutineView(val project: Project, val session: XDebugSession) :
|
||||||
Disposable, XDebugSessionListenerProvider, CreateContentParamsProvider {
|
Disposable, XDebugSessionListenerProvider, CreateContentParamsProvider {
|
||||||
|
private var needToRestoreState: Boolean = false
|
||||||
val log by logger
|
val log by logger
|
||||||
val splitter = OnePixelSplitter("SomeKey", 0.25f)
|
val splitter = OnePixelSplitter("SomeKey", 0.25f)
|
||||||
val panel = XDebuggerTreePanel(project, session.debugProcess.editorsProvider, this, null, XCOROUTINE_POPUP_ACTION_GROUP, null)
|
val panel = XDebuggerTreePanel(project, session.debugProcess.editorsProvider, this, null, XCOROUTINE_POPUP_ACTION_GROUP, null)
|
||||||
val alarm = SingleAlarm(Runnable { clear() }, VIEW_CLEAR_DELAY, this)
|
val alarm = SingleAlarm(Runnable { resetRoot() }, VIEW_CLEAR_DELAY, this)
|
||||||
val debugProcess: DebugProcessImpl = (session.debugProcess as JavaDebugProcess).debuggerSession.process
|
val javaDebugProcess = session.debugProcess as JavaDebugProcess
|
||||||
|
val debugProcess: DebugProcessImpl = javaDebugProcess.debuggerSession.process
|
||||||
|
val renderer = SimpleColoredTextIconPresentationRenderer()
|
||||||
|
val managerThreadExecutor = ManagerThreadExecutor(debugProcess)
|
||||||
|
val applicationThreadExecutor = ApplicationThreadExecutor()
|
||||||
|
var treeState: XDebuggerTreeState? = null
|
||||||
|
private var restorer: XDebuggerTreeRestorer? = null
|
||||||
|
private var selectedNodeListener = XDebuggerTreeSelectedNodeListener(panel.tree)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val VIEW_CLEAR_DELAY = 100 //ms
|
private val VIEW_CLEAR_DELAY = 100 //ms
|
||||||
@@ -60,26 +72,42 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
splitter.firstComponent = panel.mainPanel
|
splitter.firstComponent = panel.mainPanel
|
||||||
|
selectedNodeListener.installOn()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
|
fun saveState() {
|
||||||
DebuggerUIUtil.invokeLater {
|
DebuggerUIUtil.invokeLater {
|
||||||
panel.tree
|
if (! (panel.tree.root is EmptyNode)) {
|
||||||
.setRoot(object : XValueContainerNode<XValueContainer>(panel.tree, null, true, object : XValueContainer() {}) {}, false)
|
treeState = XDebuggerTreeState.saveState(panel.tree)
|
||||||
|
log.info("Tree state saved")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetRoot() {
|
||||||
|
DebuggerUIUtil.invokeLater {
|
||||||
|
panel.tree.setRoot(EmptyNode(), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun renewRoot(suspendContext: XSuspendContext) {
|
||||||
|
panel.tree.setRoot(XCoroutinesRootNode(suspendContext), false)
|
||||||
|
if(treeState != null) {
|
||||||
|
restorer?.dispose()
|
||||||
|
restorer = treeState?.restoreState(panel.tree)
|
||||||
|
log.info("Tree state restored")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
|
restorer?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun forceClear() {
|
fun forceClear() {
|
||||||
alarm.cancel()
|
alarm.cancel()
|
||||||
clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRoot(suspendContext: XSuspendContext) =
|
|
||||||
XCoroutinesRootNode(panel.tree, suspendContext)
|
|
||||||
|
|
||||||
override fun debugSessionListener(session: XDebugSession) =
|
override fun debugSessionListener(session: XDebugSession) =
|
||||||
CoroutineViewDebugSessionListener(session, this)
|
CoroutineViewDebugSessionListener(session, this)
|
||||||
|
|
||||||
@@ -92,172 +120,211 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
panel.tree
|
panel.tree
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
inner class EmptyNode : XValueContainerNode<XValueContainer>(panel.tree, null, true, object : XValueContainer() {})
|
||||||
|
|
||||||
class XCoroutinesRootNode(tree: XDebuggerTree, suspendContext: XSuspendContext) :
|
inner class XCoroutinesRootNode(suspendContext: XSuspendContext) :
|
||||||
XValueContainerNode<CoroutineGroupContainer>(tree, null, false, CoroutineGroupContainer(suspendContext)) {
|
XValueContainerNode<CoroutineGroupContainer>(panel.tree, null, false, CoroutineGroupContainer(suspendContext, "Default group"))
|
||||||
}
|
|
||||||
|
|
||||||
class CoroutineGroupContainer(
|
inner class CoroutineGroupContainer(val suspendContext: XSuspendContext, val groupName: String) : XValueContainer() {
|
||||||
val suspendContext: XSuspendContext
|
override fun computeChildren(node: XCompositeNode) {
|
||||||
) : XValueContainer() {
|
val groups = XValueChildrenList.singleton(CoroutineContainer(suspendContext, groupName))
|
||||||
override fun computeChildren(node: XCompositeNode) {
|
node.addChildren(groups, true)
|
||||||
val children = XValueChildrenList()
|
|
||||||
children.add("", CoroutineContainer(suspendContext as SuspendContextImpl, "Default group"))
|
|
||||||
node.addChildren(children, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CoroutineContainer(
|
|
||||||
val suspendContext: SuspendContextImpl,
|
|
||||||
val groupName: String
|
|
||||||
) : XValue() {
|
|
||||||
override fun computeChildren(node: XCompositeNode) {
|
|
||||||
val managerThreadExecutor = ManagerThreadExecutor(suspendContext.debugProcess)
|
|
||||||
managerThreadExecutor.schedule {
|
|
||||||
val debugProbesProxy = CoroutinesDebugProbesProxy(suspendContext)
|
|
||||||
|
|
||||||
var coroutineCache = debugProbesProxy.dumpCoroutines()
|
|
||||||
if(coroutineCache.isOk()) {
|
|
||||||
val children = XValueChildrenList()
|
|
||||||
coroutineCache.cache.forEach {
|
|
||||||
children.add("", FramesContainer(it, debugProbesProxy, managerThreadExecutor))
|
|
||||||
}
|
|
||||||
node.addChildren(children, true)
|
|
||||||
} else
|
|
||||||
node.addChildren(XValueChildrenList.EMPTY, true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun computePresentation(node: XValueNode, place: XValuePlace) {
|
inner class CoroutineContainer(
|
||||||
node.setPresentation(AllIcons.Debugger.ThreadGroup, XRegularValuePresentation(groupName, null, ""), true)
|
val suspendContext: XSuspendContext,
|
||||||
}
|
val groupName: String
|
||||||
}
|
) : RendererContainer(renderer.renderGroup(groupName)) {
|
||||||
|
|
||||||
class FramesContainer(
|
override fun computeChildren(node: XCompositeNode) {
|
||||||
private val coroutineInfoData: CoroutineInfoData,
|
managerThreadExecutor.on(suspendContext).schedule {
|
||||||
private val debugProbesProxy: CoroutinesDebugProbesProxy,
|
val debugProbesProxy = CoroutinesDebugProbesProxy(suspendContext)
|
||||||
private val managerThreadExecutor: ManagerThreadExecutor
|
|
||||||
) : XValue() {
|
var coroutineCache = debugProbesProxy.dumpCoroutines()
|
||||||
override fun computeChildren(node: XCompositeNode) {
|
if (coroutineCache.isOk()) {
|
||||||
managerThreadExecutor.schedule {
|
val children = XValueChildrenList()
|
||||||
val children = XValueChildrenList()
|
coroutineCache.cache.forEach {
|
||||||
debugProbesProxy.frameBuilder().build(coroutineInfoData)
|
children.add(FramesContainer(it, suspendContext))
|
||||||
val creationStack = mutableListOf<CreationCoroutineStackFrameItem>()
|
|
||||||
coroutineInfoData.stackFrameList.forEach {
|
|
||||||
val frameValue = when (it) {
|
|
||||||
is CreationCoroutineStackFrameItem -> {
|
|
||||||
creationStack.add(it)
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
else -> CoroutineFrameValue(it)
|
node.addChildren(children, true)
|
||||||
}
|
} else {
|
||||||
frameValue?.let {
|
node.addChildren(XValueChildrenList.singleton(ErrorNode("Error occured while fetching information")), true)
|
||||||
children.add("", frameValue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
children.add("", CreationFramesContainer(creationStack))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ErrorNode(val error: String) : RendererContainer(renderer.renderErrorNode(error))
|
||||||
|
|
||||||
|
inner class FramesContainer(
|
||||||
|
private val infoData: CoroutineInfoData,
|
||||||
|
private val suspendContext: XSuspendContext
|
||||||
|
) : RendererContainer(renderer.render(infoData)) {
|
||||||
|
|
||||||
|
override fun computeChildren(node: XCompositeNode) {
|
||||||
|
managerThreadExecutor.on(suspendContext).schedule {
|
||||||
|
val debugProbesProxy = CoroutinesDebugProbesProxy(suspendContext)
|
||||||
|
val children = XValueChildrenList()
|
||||||
|
debugProbesProxy.frameBuilder().build(infoData)
|
||||||
|
val creationStack = mutableListOf<CreationCoroutineStackFrameItem>()
|
||||||
|
infoData.stackFrameList.forEach {
|
||||||
|
if (it is CreationCoroutineStackFrameItem)
|
||||||
|
creationStack.add(it)
|
||||||
|
else
|
||||||
|
children.add(CoroutineFrameValue(it))
|
||||||
|
}
|
||||||
|
children.add(CreationFramesContainer(infoData, creationStack))
|
||||||
|
node.addChildren(children, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class CreationFramesContainer(
|
||||||
|
private val infoData: CoroutineInfoData,
|
||||||
|
private val creationFrames: List<CreationCoroutineStackFrameItem>
|
||||||
|
) : RendererContainer(renderer.renderCreationNode(infoData)) {
|
||||||
|
|
||||||
|
override fun computeChildren(node: XCompositeNode) {
|
||||||
|
val children = XValueChildrenList()
|
||||||
|
|
||||||
|
creationFrames.forEach {
|
||||||
|
children.add(CoroutineFrameValue(it))
|
||||||
|
}
|
||||||
node.addChildren(children, true)
|
node.addChildren(children, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun computePresentation(node: XValueNode, place: XValuePlace) {
|
inner class CoroutineFrameValue(val frame: CoroutineStackFrameItem
|
||||||
val icon = when (coroutineInfoData.state) {
|
) : XNamedValue(frame.uniqueId()) {
|
||||||
CoroutineInfoData.State.SUSPENDED -> AllIcons.Debugger.ThreadSuspended
|
override fun computePresentation(node: XValueNode, place: XValuePlace) =
|
||||||
CoroutineInfoData.State.RUNNING -> AllIcons.Debugger.ThreadRunning
|
applyRenderer(node, renderer.render(frame.location()))
|
||||||
CoroutineInfoData.State.CREATED -> AllIcons.Debugger.ThreadStates.Idle
|
}
|
||||||
|
|
||||||
|
private fun applyRenderer(node: XValueNode, presentation: SimpleColoredTextIcon) =
|
||||||
|
node.setPresentation(presentation.icon, presentation.valuePresentation(), presentation.hasChildrens)
|
||||||
|
|
||||||
|
open inner class RendererContainer(val presentation: SimpleColoredTextIcon) : XNamedValue(presentation.simpleString()) {
|
||||||
|
override fun computePresentation(node: XValueNode, place: XValuePlace) =
|
||||||
|
applyRenderer(node, presentation)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class XDebuggerTreeSelectedNodeListener(val tree: XDebuggerTree) {
|
||||||
|
|
||||||
|
fun installOn() {
|
||||||
|
object : DoubleClickListener() {
|
||||||
|
override fun onDoubleClick(e: MouseEvent) =
|
||||||
|
nodeSelected(Either.left(e))
|
||||||
|
}.installOn(tree)
|
||||||
|
|
||||||
|
tree.addKeyListener(object : KeyAdapter() {
|
||||||
|
override fun keyPressed(e: KeyEvent) {
|
||||||
|
val key = e.keyCode
|
||||||
|
if (key == KeyEvent.VK_ENTER || key == KeyEvent.VK_SPACE || key == KeyEvent.VK_RIGHT)
|
||||||
|
nodeSelected(Either.right(e))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
val valuePresentation = customizePresentation(coroutineInfoData)
|
fun nodeSelected(event: Either<MouseEvent, KeyEvent>) : Boolean {
|
||||||
node.setPresentation(icon, valuePresentation, true)
|
val selectedNodes = tree.getSelectedNodes(XValueNodeImpl::class.java, null)
|
||||||
}
|
if (selectedNodes.size == 1) {
|
||||||
|
val node = selectedNodes[0]
|
||||||
|
val valueContainer = node.valueContainer
|
||||||
|
if (valueContainer is XCoroutineView.CoroutineFrameValue) {
|
||||||
|
val frame = valueContainer.frame
|
||||||
|
val threadSuspendContext = session.suspendContext as SuspendContextImpl
|
||||||
|
when (frame) {
|
||||||
|
is RunningCoroutineStackFrameItem -> {
|
||||||
|
val threadProxy = valueContainer.frame.frame.threadProxy()
|
||||||
|
val isCurrentContext = threadSuspendContext.thread == threadProxy
|
||||||
|
createStackAndSetFrame(threadProxy, { frame.stackFrame }, isCurrentContext)
|
||||||
|
}
|
||||||
|
is CreationCoroutineStackFrameItem -> {
|
||||||
|
val position = getPosition(frame.stackTraceElement) ?: return false
|
||||||
|
val threadProxy = threadSuspendContext.thread as ThreadReferenceProxyImpl
|
||||||
|
val stackFrame =
|
||||||
|
createStackAndSetFrame(threadProxy, { SyntheticStackFrame(frame.emptyDescriptor(), emptyList(), position) })
|
||||||
|
}
|
||||||
|
is SuspendCoroutineStackFrameItem -> {
|
||||||
|
val position = getPosition(frame.stackTraceElement) ?: return false
|
||||||
|
}
|
||||||
|
is AsyncCoroutineStackFrameItem -> {
|
||||||
|
|
||||||
private fun customizePresentation(coroutineInfoData: CoroutineInfoData): XRegularValuePresentation {
|
}
|
||||||
val component = SimpleColoredComponent()
|
// else -> {
|
||||||
val thread = coroutineInfoData.thread
|
// val (stack, stackFrame) = createSyntheticStackFrame(descriptor, pos) ?: return
|
||||||
val name = thread?.name()?.substringBefore(" @${coroutineInfoData.name}") ?: ""
|
// val action: () -> Unit = { context.debuggerSession?.xDebugSession?.setCurrentStackFrame(stack, stackFrame) }
|
||||||
val threadState = if (thread != null) DebuggerUtilsEx.getThreadStatusText(thread.status()) else ""
|
// - ApplicationManager.getApplication()
|
||||||
|
// - .invokeLater(action, ModalityState.stateForComponent(this@CoroutinesDebuggerTree))
|
||||||
component.append("\"").append(coroutineInfoData.name, XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES)
|
// }
|
||||||
component.append("\": ${coroutineInfoData.state} ${if (name.isNotEmpty()) "on thread \"$name\":$threadState" else ""}")
|
}
|
||||||
return XRegularValuePresentation(component.getCharSequence(false).toString(), null, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreationFramesContainer(val creationFrames: List<CreationCoroutineStackFrameItem>) : XValue() {
|
|
||||||
override fun computeChildren(node: XCompositeNode) {
|
|
||||||
val children = XValueChildrenList()
|
|
||||||
|
|
||||||
creationFrames.forEach {
|
|
||||||
children.add("", CoroutineFrameValue(it))
|
|
||||||
}
|
|
||||||
node.addChildren(children, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun computePresentation(node: XValueNode, place: XValuePlace) {
|
|
||||||
node.setPresentation(AllIcons.Debugger.ThreadSuspended, XRegularValuePresentation("Creation stack frame", null, ""), true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CoroutineFrameValue(val frame: CoroutineStackFrameItem) : XValue() {
|
|
||||||
override fun computePresentation(node: XValueNode, place: XValuePlace) {
|
|
||||||
val presentation = customizePresentation(frame)
|
|
||||||
if(node is XValueNodeImpl) {
|
|
||||||
node.setPresentation(null, XRegularValuePresentation("", null, ""), false)
|
|
||||||
presentation.texts.forEachIndexed { i, s ->
|
|
||||||
node.text.append(s, presentation.attributes[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val component = SimpleColoredComponent()
|
|
||||||
presentation.appendToComponent(component)
|
|
||||||
val valuePresentation = XRegularValuePresentation(component.getCharSequence(false).toString(), null, "")
|
|
||||||
node.setPresentation(null, valuePresentation, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun customizePresentation(frame: CoroutineStackFrameItem) : SimpleColoredText =
|
|
||||||
calcRepresentation(frame.location(), ThreadsViewSettings.getInstance())
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Taken from #StackFrameDescriptorImpl.calcRepresentation
|
|
||||||
*/
|
|
||||||
fun calcRepresentation(location: Location, settings: ThreadsViewSettings): SimpleColoredText {
|
|
||||||
val label = SimpleColoredText()
|
|
||||||
DebuggerUIUtil.getColorScheme(null)
|
|
||||||
if (location.method() != null) {
|
|
||||||
val myName = location.method().name()
|
|
||||||
label.append(if (settings.SHOW_ARGUMENTS_TYPES) DebuggerUtilsEx.methodNameWithArguments(location.method()) else myName, XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES)
|
|
||||||
}
|
|
||||||
if (settings.SHOW_LINE_NUMBER) {
|
|
||||||
label.append(":", SimpleTextAttributes.REGULAR_ATTRIBUTES)
|
|
||||||
label.append("" + DebuggerUtilsEx.getLineNumber(location, false), SimpleTextAttributes.REGULAR_ATTRIBUTES)
|
|
||||||
}
|
|
||||||
if (settings.SHOW_CLASS_NAME) {
|
|
||||||
val name: String?
|
|
||||||
name = try {
|
|
||||||
val refType: ReferenceType = location.declaringType()
|
|
||||||
refType?.name()
|
|
||||||
} catch (e: InternalError) {
|
|
||||||
e.toString()
|
|
||||||
}
|
|
||||||
if (name != null) {
|
|
||||||
label.append(", ", SimpleTextAttributes.REGULAR_ATTRIBUTES)
|
|
||||||
val dotIndex = name.lastIndexOf('.')
|
|
||||||
if (dotIndex < 0) {
|
|
||||||
label.append(name, SimpleTextAttributes.REGULAR_ATTRIBUTES)
|
|
||||||
} else {
|
|
||||||
label.append(name.substring(dotIndex + 1), SimpleTextAttributes.REGULAR_ATTRIBUTES)
|
|
||||||
if (settings.SHOW_PACKAGE_NAME) {
|
|
||||||
label.append(" (${name.substring( 0, dotIndex)})", SimpleTextAttributes.REGULAR_ATTRIBUTES)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (settings.SHOW_SOURCE_NAME) {
|
|
||||||
label.append(", ", SimpleTextAttributes.REGULAR_ATTRIBUTES)
|
fun createSyntheticStackFrame() {}
|
||||||
label.append(DebuggerUtilsEx.getSourceName(location) { e: Throwable? -> "Unknown Source" }, SimpleTextAttributes.REGULAR_ATTRIBUTES)
|
|
||||||
|
fun createStackAndSetFrame(threadReferenceProxy: ThreadReferenceProxyImpl, stackFrameProvider: () -> XStackFrame, isCurrentContext: Boolean = false) {
|
||||||
|
val threadSuspendContext = session.suspendContext as SuspendContextImpl
|
||||||
|
managerThreadExecutor.on(threadSuspendContext).schedule {
|
||||||
|
val stackFrame = stackFrameProvider.invoke()
|
||||||
|
val executionStack = createExecutionStack(threadReferenceProxy, isCurrentContext)
|
||||||
|
applicationThreadExecutor.schedule(
|
||||||
|
{
|
||||||
|
session.setCurrentStackFrame(executionStack, stackFrame)
|
||||||
|
}, panel.tree)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createExecutionStack(proxy: ThreadReferenceProxyImpl, isCurrentContext: Boolean = false) : XExecutionStack {
|
||||||
|
val executionStack = CoroutineDebuggerExecutionStack(proxy, isCurrentContext)
|
||||||
|
executionStack.initTopFrame()
|
||||||
|
return executionStack
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class CoroutineDebuggerExecutionStack(threadReferenceProxy: ThreadReferenceProxyImpl, isCurrentContext: Boolean) :
|
||||||
|
JavaExecutionStack(threadReferenceProxy, debugProcess, isCurrentContext)
|
||||||
|
|
||||||
|
|
||||||
|
private fun getPosition(frame: StackTraceElement): XSourcePosition? {
|
||||||
|
val psiFacade = JavaPsiFacade.getInstance(project)
|
||||||
|
val psiClass = psiFacade.findClass(
|
||||||
|
frame.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 (frame.lineNumber > 0) frame.lineNumber - 1 else return null
|
||||||
|
return XDebuggerUtil.getInstance().createPosition(classFile, lineNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSyntheticStackFrame(
|
||||||
|
descriptor: SuspendStackFrameDescriptor,
|
||||||
|
pos: XSourcePosition
|
||||||
|
): Pair<XExecutionStack, SyntheticStackFrame>? {
|
||||||
|
val context = DebuggerManagerEx.getInstanceEx(project).context
|
||||||
|
val suspendContext = context.suspendContext ?: return null
|
||||||
|
val proxy = suspendContext.thread ?: return null
|
||||||
|
val executionStack = JavaExecutionStack(proxy, suspendContext.debugProcess, false)
|
||||||
|
executionStack.initTopFrame()
|
||||||
|
val evalContext = context.createEvaluationContext()
|
||||||
|
val frameProxy = evalContext?.frameProxy ?: return null
|
||||||
|
val execContext = ExecutionContext(evalContext, frameProxy)
|
||||||
|
val continuation = descriptor.continuation // guaranteed that it is a BaseContinuationImpl
|
||||||
|
val aMethod = (continuation.type() as ClassType).concreteMethodByName(
|
||||||
|
"getStackTraceElement",
|
||||||
|
"()Ljava/lang/StackTraceElement;"
|
||||||
|
)
|
||||||
|
val vars = with(CoroutineAsyncStackTraceProvider()) {
|
||||||
|
AsyncStackTraceContext(
|
||||||
|
execContext,
|
||||||
|
aMethod
|
||||||
|
).getSpilledVariables(continuation)
|
||||||
|
} ?: return null
|
||||||
|
return executionStack to SyntheticStackFrame(descriptor, vars, pos)
|
||||||
}
|
}
|
||||||
return label
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user