[coroutine] running coroutine stack frame merged with coroutine info
This commit is contained in:
committed by
Vladimir Ilmov
parent
f1669e2230
commit
e4d157c126
+7
-18
@@ -11,23 +11,22 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl
|
|||||||
import com.intellij.xdebugger.frame.XSuspendContext
|
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.coroutine.data.CoroutineAsyncStackFrameItem
|
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineStackFrameItem
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.AsyncStackTraceContext
|
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.AsyncStackTraceContext
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
||||||
|
|
||||||
class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
||||||
|
|
||||||
override fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: SuspendContextImpl): List<CoroutineAsyncStackFrameItem>? =
|
override fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: SuspendContextImpl): List<CoroutineStackFrameItem>? =
|
||||||
getAsyncStackTrace(stackFrame, suspendContext as XSuspendContext)
|
getAsyncStackTrace(stackFrame, suspendContext as XSuspendContext)
|
||||||
|
|
||||||
|
fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: XSuspendContext): List<CoroutineStackFrameItem>? {
|
||||||
fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: XSuspendContext): List<CoroutineAsyncStackFrameItem>? {
|
|
||||||
val stackFrameList = hopelessAware { getAsyncStackTraceSafe(stackFrame.stackFrameProxy, suspendContext) }
|
val stackFrameList = hopelessAware { getAsyncStackTraceSafe(stackFrame.stackFrameProxy, suspendContext) }
|
||||||
return if (stackFrameList == null || stackFrameList.isEmpty()) null else stackFrameList
|
return if (stackFrameList == null || stackFrameList.isEmpty()) null else stackFrameList
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAsyncStackTraceSafe(frameProxy: StackFrameProxyImpl, suspendContext: XSuspendContext): List<CoroutineAsyncStackFrameItem> {
|
fun getAsyncStackTraceSafe(frameProxy: StackFrameProxyImpl, suspendContext: XSuspendContext): List<CoroutineStackFrameItem> {
|
||||||
val defaultResult = emptyList<CoroutineAsyncStackFrameItem>()
|
val defaultResult = emptyList<CoroutineStackFrameItem>()
|
||||||
|
|
||||||
val location = frameProxy.location()
|
val location = frameProxy.location()
|
||||||
if (!location.isInKotlinSources())
|
if (!location.isInKotlinSources())
|
||||||
@@ -39,20 +38,10 @@ class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
|||||||
if (threadReference == null || !threadReference.isSuspended || !canRunEvaluation(suspendContext))
|
if (threadReference == null || !threadReference.isSuspended || !canRunEvaluation(suspendContext))
|
||||||
return defaultResult
|
return defaultResult
|
||||||
|
|
||||||
|
|
||||||
val astContext = createAsyncStackTraceContext(frameProxy, suspendContext, method)
|
|
||||||
return astContext.getAsyncStackTraceIfAny()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createAsyncStackTraceContext(
|
|
||||||
frameProxy: StackFrameProxyImpl,
|
|
||||||
suspendContext: XSuspendContext,
|
|
||||||
method: Method
|
|
||||||
): AsyncStackTraceContext {
|
|
||||||
val evaluationContext = EvaluationContextImpl(suspendContext as SuspendContextImpl, frameProxy)
|
val evaluationContext = EvaluationContextImpl(suspendContext as SuspendContextImpl, frameProxy)
|
||||||
val context = ExecutionContext(evaluationContext, frameProxy)
|
val context = ExecutionContext(evaluationContext, frameProxy)
|
||||||
// DebugMetadataKt not found, probably old kotlin-stdlib version
|
val astContext = AsyncStackTraceContext(context, method)
|
||||||
return AsyncStackTraceContext(context, method)
|
return astContext.getAsyncStackTraceIfAny()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canRunEvaluation(suspendContext: XSuspendContext) =
|
fun canRunEvaluation(suspendContext: XSuspendContext) =
|
||||||
|
|||||||
-1
@@ -9,7 +9,6 @@ import org.jetbrains.annotations.NonNls
|
|||||||
|
|
||||||
class CoroutineDebuggerContentInfo {
|
class CoroutineDebuggerContentInfo {
|
||||||
companion object {
|
companion object {
|
||||||
val COROUTINE_THREADS_CONTENT = "CoroutineThreadsContent"
|
|
||||||
val XCOROUTINE_THREADS_CONTENT = "XCoroutineThreadsContent"
|
val XCOROUTINE_THREADS_CONTENT = "XCoroutineThreadsContent"
|
||||||
val XCOROUTINE_POPUP_ACTION_GROUP = "Kotlin.XDebugger.Actions"
|
val XCOROUTINE_POPUP_ACTION_GROUP = "Kotlin.XDebugger.Actions"
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -27,7 +27,7 @@ import kotlin.reflect.KProperty
|
|||||||
|
|
||||||
class CoroutineProjectConnectionListener(val project: Project) : XDebuggerManagerListener {
|
class CoroutineProjectConnectionListener(val project: Project) : XDebuggerManagerListener {
|
||||||
var connection: MessageBusConnection? = null
|
var connection: MessageBusConnection? = null
|
||||||
val processCounter = AtomicInteger(0)
|
private val processCounter = AtomicInteger(0)
|
||||||
private val log by logger
|
private val log by logger
|
||||||
|
|
||||||
private fun connect() {
|
private fun connect() {
|
||||||
@@ -40,7 +40,7 @@ class CoroutineProjectConnectionListener(val project: Project) : XDebuggerManage
|
|||||||
params: JavaParameters?,
|
params: JavaParameters?,
|
||||||
runnerSettings: RunnerSettings?
|
runnerSettings: RunnerSettings?
|
||||||
) {
|
) {
|
||||||
if(coroutineDebuggerEnabled()) {
|
if (coroutineDebuggerEnabled()) {
|
||||||
val configurationName = configuration.type.id
|
val configurationName = configuration.type.id
|
||||||
try {
|
try {
|
||||||
if (!gradleConfiguration(configurationName)) { // gradle test logic in KotlinGradleCoroutineDebugProjectResolver
|
if (!gradleConfiguration(configurationName)) { // gradle test logic in KotlinGradleCoroutineDebugProjectResolver
|
||||||
@@ -63,7 +63,7 @@ class CoroutineProjectConnectionListener(val project: Project) : XDebuggerManage
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun gradleConfiguration(configurationName: String) =
|
private fun gradleConfiguration(configurationName: String) =
|
||||||
"GradleRunConfiguration".equals(configurationName) || "KotlinGradleRunConfiguration".equals(configurationName)
|
"GradleRunConfiguration" == configurationName || "KotlinGradleRunConfiguration" == configurationName
|
||||||
|
|
||||||
override fun processStarted(debugProcess: XDebugProcess) =
|
override fun processStarted(debugProcess: XDebugProcess) =
|
||||||
DebuggerInvocationUtil.swingInvokeLater(project) {
|
DebuggerInvocationUtil.swingInvokeLater(project) {
|
||||||
@@ -102,9 +102,9 @@ val projectListener
|
|||||||
get() = object : ReadOnlyProperty<Project, CoroutineProjectConnectionListener> {
|
get() = object : ReadOnlyProperty<Project, CoroutineProjectConnectionListener> {
|
||||||
lateinit var listenerProject: CoroutineProjectConnectionListener
|
lateinit var listenerProject: CoroutineProjectConnectionListener
|
||||||
|
|
||||||
override fun getValue(project: Project, property: KProperty<*>): CoroutineProjectConnectionListener {
|
override fun getValue(thisRef: Project, property: KProperty<*>): CoroutineProjectConnectionListener {
|
||||||
if (!::listenerProject.isInitialized)
|
if (!::listenerProject.isInitialized)
|
||||||
listenerProject = CoroutineProjectConnectionListener(project)
|
listenerProject = CoroutineProjectConnectionListener(thisRef)
|
||||||
return listenerProject
|
return listenerProject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -9,9 +9,9 @@ import com.intellij.ui.components.JBLabel
|
|||||||
import com.intellij.ui.SimpleListCellRenderer
|
import com.intellij.ui.SimpleListCellRenderer
|
||||||
import javax.swing.ListCellRenderer
|
import javax.swing.ListCellRenderer
|
||||||
|
|
||||||
class VersionedImplementationProvider() {
|
class VersionedImplementationProvider {
|
||||||
fun comboboxListCellRenderer(): ListCellRenderer<in String>? =
|
fun comboboxListCellRenderer(): ListCellRenderer<in String>? =
|
||||||
SimpleListCellRenderer.create<String> { label: JBLabel, value: String?, index: Int ->
|
SimpleListCellRenderer.create { label: JBLabel, value: String?, index: Int ->
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
label.text = value
|
label.text = value
|
||||||
} else if (index >= 0) {
|
} else if (index >= 0) {
|
||||||
|
|||||||
+2
-2
@@ -9,9 +9,9 @@ import com.intellij.ui.components.JBLabel
|
|||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.override.com.intellij.ui.SimpleListCellRenderer
|
import org.jetbrains.kotlin.idea.debugger.coroutine.override.com.intellij.ui.SimpleListCellRenderer
|
||||||
import javax.swing.ListCellRenderer
|
import javax.swing.ListCellRenderer
|
||||||
|
|
||||||
class VersionedImplementationProvider() {
|
class VersionedImplementationProvider {
|
||||||
fun comboboxListCellRenderer(): ListCellRenderer<in String>? =
|
fun comboboxListCellRenderer(): ListCellRenderer<in String>? =
|
||||||
SimpleListCellRenderer.create<String> { label: JBLabel, value: String?, index: Int ->
|
SimpleListCellRenderer.create { label: JBLabel, value: String?, index: Int ->
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
label.text = value
|
label.text = value
|
||||||
} else if (index >= 0) {
|
} else if (index >= 0) {
|
||||||
|
|||||||
+70
-136
@@ -5,96 +5,118 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutine.command
|
package org.jetbrains.kotlin.idea.debugger.coroutine.command
|
||||||
|
|
||||||
import com.intellij.debugger.DebuggerManagerEx
|
import com.intellij.debugger.engine.DebugProcess
|
||||||
import com.intellij.debugger.engine.*
|
import com.intellij.debugger.engine.JavaExecutionStack
|
||||||
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
import com.intellij.debugger.jdi.ClassesByNameProvider
|
import com.intellij.debugger.jdi.ClassesByNameProvider
|
||||||
import com.intellij.debugger.jdi.GeneratedLocation
|
import com.intellij.debugger.jdi.GeneratedLocation
|
||||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
||||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
|
||||||
import com.intellij.debugger.ui.impl.watch.MethodsTracker
|
|
||||||
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.frame.XExecutionStack
|
|
||||||
import com.intellij.xdebugger.frame.XStackFrame
|
import com.intellij.xdebugger.frame.XStackFrame
|
||||||
import com.intellij.xdebugger.frame.XSuspendContext
|
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.coroutine.CoroutineAsyncStackTraceProvider
|
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineAsyncStackTraceProvider
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
|
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.SuspendStackFrameDescriptor
|
import org.jetbrains.kotlin.idea.debugger.safeLineNumber
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.SyntheticStackFrame
|
import org.jetbrains.kotlin.idea.debugger.safeLocation
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.AsyncStackTraceContext
|
import org.jetbrains.kotlin.idea.debugger.safeMethod
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.EmptyStackFrameDescriptor
|
import org.jetbrains.kotlin.idea.debugger.safeSignature
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
|
||||||
|
|
||||||
|
|
||||||
class CoroutineBuilder(val suspendContext: XSuspendContext) {
|
class CoroutineBuilder(val suspendContext: XSuspendContext) {
|
||||||
private val methodsTracker = MethodsTracker()
|
|
||||||
private val coroutineStackFrameProvider = CoroutineAsyncStackTraceProvider()
|
private val coroutineStackFrameProvider = CoroutineAsyncStackTraceProvider()
|
||||||
val debugProcess = (suspendContext as SuspendContextImpl).debugProcess
|
val debugProcess = (suspendContext as SuspendContextImpl).debugProcess
|
||||||
val virtualMachineProxy = debugProcess.virtualMachineProxy
|
private val virtualMachineProxy = debugProcess.virtualMachineProxy
|
||||||
val classesByName = ClassesByNameProvider.createCache(virtualMachineProxy.allClasses())
|
private 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
|
const val CREATION_STACK_TRACE_SEPARATOR = "\b\b\b" // the "\b\b\b" is used for creation stacktrace separator in kotlinx.coroutines
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(coroutine: CoroutineInfoData): List<CoroutineStackFrameItem> {
|
fun build(coroutine: CoroutineInfoData): List<CoroutineStackFrameItem> {
|
||||||
val coroutineStackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
val coroutineStackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
||||||
val firstSuspendedStackFrameProxyImpl = firstSuspendedThreadFrame()
|
val firstSuspendedStackFrameProxyImpl = firstSuspendedThreadFrame()
|
||||||
val creationFrameSeparatorIndex = findCreationFrameIndex(coroutine.stackTrace)
|
val creationFrameSeparatorIndex = findCreationFrameIndex(coroutine.stackTrace)
|
||||||
val positionManager = debugProcess.positionManager
|
|
||||||
|
|
||||||
if (coroutine.state == CoroutineInfoData.State.RUNNING && coroutine.activeThread is ThreadReference) {
|
if (coroutine.state == CoroutineInfoData.State.RUNNING && coroutine.activeThread is ThreadReference) {
|
||||||
val threadReferenceProxyImpl = runningThreadProxy(coroutine.activeThread)
|
val threadReferenceProxyImpl = runningThreadProxy(coroutine.activeThread)
|
||||||
val executionStack = JavaExecutionStack(threadReferenceProxyImpl, debugProcess, suspendedSameThread(coroutine.activeThread))
|
val executionStack = JavaExecutionStack(threadReferenceProxyImpl, debugProcess, suspendedSameThread(coroutine.activeThread))
|
||||||
|
|
||||||
val frames = threadReferenceProxyImpl.forceFrames()
|
val frames = threadReferenceProxyImpl.forceFrames()
|
||||||
var resumeMethodIndex = findResumeMethodIndex(frames)
|
var coroutineStackInserted = false
|
||||||
for (frameIndex in 0..frames.lastIndex) {
|
for (runningStackFrameProxy in frames) {
|
||||||
val runningStackFrameProxy = frames[frameIndex]
|
val jStackFrame = executionStack.createStackFrame(runningStackFrameProxy)
|
||||||
if (frameIndex == resumeMethodIndex) {
|
val coroutineStack = coroutineStackFrameProvider.getAsyncStackTraceSafe(runningStackFrameProxy, suspendContext)
|
||||||
val previousFrame = frames[resumeMethodIndex - 1]
|
if (coroutineStack.isNotEmpty()) {
|
||||||
val previousJavaFrame = JavaStackFrame(StackFrameDescriptorImpl(previousFrame, methodsTracker), true)
|
// clue coroutine stack into the thread's real stack
|
||||||
val asyncStackTrace = coroutineStackFrameProvider
|
|
||||||
.getAsyncStackTrace(previousJavaFrame, suspendContext)
|
val firstMergedFrame = mergeFrameVars(coroutineStack.first(), runningStackFrameProxy, jStackFrame)
|
||||||
asyncStackTrace?.forEach { asyncFrame ->
|
coroutineStackFrameList.add(firstMergedFrame)
|
||||||
val xStackFrame = JavaStackFrame(StackFrameDescriptorImpl(previousFrame, methodsTracker), true)
|
|
||||||
coroutineStackFrameList.add(AsyncCoroutineStackFrameItem(runningStackFrameProxy, asyncFrame, xStackFrame))
|
for (asyncFrame in coroutineStack.drop(1)) {
|
||||||
|
coroutineStackFrameList.add(
|
||||||
|
RestoredCoroutineStackFrameItem(
|
||||||
|
runningStackFrameProxy,
|
||||||
|
asyncFrame.location,
|
||||||
|
asyncFrame.spilledVariables
|
||||||
|
)
|
||||||
|
)
|
||||||
|
coroutineStackInserted = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val xStackFrame = executionStack.createStackFrame(runningStackFrameProxy)
|
if (coroutineStackInserted && isInvokeSuspendNegativeLineMethodFrame(runningStackFrameProxy)) {
|
||||||
coroutineStackFrameList.add(RunningCoroutineStackFrameItem(runningStackFrameProxy, xStackFrame))
|
coroutineStackInserted = false
|
||||||
|
} else
|
||||||
|
coroutineStackFrameList.add(RunningCoroutineStackFrameItem(runningStackFrameProxy, jStackFrame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((coroutine.state == CoroutineInfoData.State.SUSPENDED || coroutine.activeThread == null) && coroutine.lastObservedFrameFieldRef is ObjectReference) {
|
} else if ((coroutine.state == CoroutineInfoData.State
|
||||||
|
.SUSPENDED || coroutine.activeThread == null) && coroutine.lastObservedFrameFieldRef is ObjectReference
|
||||||
|
) {
|
||||||
// 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
|
||||||
val suspendedStackTrace = coroutine.stackTrace.take(creationFrameSeparatorIndex)
|
val suspendedStackTrace = coroutine.stackTrace.take(creationFrameSeparatorIndex)
|
||||||
for (suspendedFrame in suspendedStackTrace) {
|
for (suspendedFrame in suspendedStackTrace) {
|
||||||
val suspendedXStackFrame = stackFrame(positionManager, firstSuspendedStackFrameProxyImpl, suspendedFrame)
|
val location = createLocation(suspendedFrame)
|
||||||
|
|
||||||
coroutineStackFrameList.add(
|
coroutineStackFrameList.add(
|
||||||
SuspendCoroutineStackFrameItem(firstSuspendedStackFrameProxyImpl, suspendedFrame, suspendedXStackFrame, coroutine.lastObservedFrameFieldRef)
|
SuspendCoroutineStackFrameItem(
|
||||||
|
firstSuspendedStackFrameProxyImpl,
|
||||||
|
suspendedFrame,
|
||||||
|
coroutine.lastObservedFrameFieldRef,
|
||||||
|
location
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val executionStack = JavaExecutionStack(suspendedThreadProxy(), debugProcess, false)
|
|
||||||
val xStackFrame = executionStack.createStackFrame(firstSuspendedStackFrameProxyImpl)
|
|
||||||
|
|
||||||
coroutine.stackTrace.subList(creationFrameSeparatorIndex + 1, coroutine.stackTrace.size).forEach {
|
coroutine.stackTrace.subList(creationFrameSeparatorIndex + 1, coroutine.stackTrace.size).forEach {
|
||||||
var location = createLocation(it)
|
val location = createLocation(it)
|
||||||
coroutineStackFrameList.add(CreationCoroutineStackFrameItem(firstSuspendedStackFrameProxyImpl, it, xStackFrame, location))
|
coroutineStackFrameList.add(CreationCoroutineStackFrameItem(firstSuspendedStackFrameProxyImpl, it, location))
|
||||||
}
|
}
|
||||||
coroutine.stackFrameList.addAll(coroutineStackFrameList)
|
coroutine.stackFrameList.addAll(coroutineStackFrameList)
|
||||||
return coroutineStackFrameList
|
return coroutineStackFrameList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First frames need to be merged as real frame has accurate line number but lacks local variables from coroutine-restored frame.
|
||||||
|
*/
|
||||||
|
private fun mergeFrameVars(
|
||||||
|
restoredFrame: CoroutineStackFrameItem,
|
||||||
|
runningStackFrameProxy: StackFrameProxyImpl,
|
||||||
|
jStackFrame: XStackFrame,
|
||||||
|
): RunningCoroutineStackFrameItem {
|
||||||
|
if (restoredFrame.location is GeneratedLocation) {
|
||||||
|
val restoredMethod = restoredFrame.location.method()
|
||||||
|
val realMethod = runningStackFrameProxy.location().method()
|
||||||
|
// if refers to the same method - proceed with merge, otherwise do nothing
|
||||||
|
if (restoredMethod == realMethod) {
|
||||||
|
return RunningCoroutineStackFrameItem(runningStackFrameProxy, jStackFrame, restoredFrame.spilledVariables)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RunningCoroutineStackFrameItem(runningStackFrameProxy, jStackFrame)
|
||||||
|
}
|
||||||
|
|
||||||
private fun suspendedSameThread(activeThread: ThreadReference) =
|
private fun suspendedSameThread(activeThread: ThreadReference) =
|
||||||
activeThread == suspendedThreadProxy().threadReference
|
activeThread == suspendedThreadProxy().threadReference
|
||||||
|
|
||||||
@@ -123,20 +145,11 @@ class CoroutineBuilder(val suspendContext: XSuspendContext) {
|
|||||||
return GeneratedLocation(debugProcess, type, methodName, line)
|
return GeneratedLocation(debugProcess, type, methodName, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stackFrame(positionManager: CompoundPositionManager, runningStackFrameProxy: StackFrameProxyImpl, location: Location): XStackFrame {
|
|
||||||
return positionManager.createStackFrame(runningStackFrameProxy, debugProcess, location)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stackFrame(positionManager: CompoundPositionManager, runningStackFrameProxy: StackFrameProxyImpl, stackTraceElement: StackTraceElement) : XStackFrame {
|
|
||||||
val location = createLocation(stackTraceElement)
|
|
||||||
return positionManager.createStackFrame(runningStackFrameProxy, debugProcess, location)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to find creation frame separator if any, returns last index if none found
|
* Tries to find creation frame separator if any, returns last index if none found
|
||||||
*/
|
*/
|
||||||
private fun findCreationFrameIndex(frames: List<StackTraceElement>): Int {
|
private fun findCreationFrameIndex(frames: List<StackTraceElement>): Int {
|
||||||
var index = frames.indexOfFirst { isCreationSeparatorFrame(it) }
|
val index = frames.indexOfFirst { isCreationSeparatorFrame(it) }
|
||||||
return if (index < 0)
|
return if (index < 0)
|
||||||
frames.lastIndex
|
frames.lastIndex
|
||||||
else
|
else
|
||||||
@@ -157,88 +170,9 @@ class CoroutineBuilder(val suspendContext: XSuspendContext) {
|
|||||||
private fun suspendedThreadProxy(): ThreadReferenceProxyImpl =
|
private fun suspendedThreadProxy(): ThreadReferenceProxyImpl =
|
||||||
(suspendContext as SuspendContextImpl).thread!! // @TODO hash replace !!
|
(suspendContext as SuspendContextImpl).thread!! // @TODO hash replace !!
|
||||||
|
|
||||||
private fun findResumeMethodIndex(frames: List<StackFrameProxyImpl>): Int {
|
private fun isInvokeSuspendNegativeLineMethodFrame(frame: StackFrameProxyImpl) =
|
||||||
for (i: Int in frames.lastIndex downTo 0)
|
frame.safeLocation()?.safeMethod()?.name() == "invokeSuspend" &&
|
||||||
if (isResumeMethodFrame(frames[i])) {
|
frame.safeLocation()?.safeMethod()?.safeSignature() ?: "" == "(Ljava/lang/Object;)Ljava/lang/Object;" &&
|
||||||
return i
|
frame.safeLocation()?.safeLineNumber() ?: 0 < 0
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isResumeMethodFrame(frame: StackFrameProxyImpl) =
|
|
||||||
frame.safeLocation()?.safeMethod()?.name() == "resumeWith"
|
|
||||||
|
|
||||||
private fun createSyntheticStackFrame(
|
|
||||||
descriptor: SuspendStackFrameDescriptor,
|
|
||||||
pos: XSourcePosition,
|
|
||||||
project: Project
|
|
||||||
): 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreationCoroutineStackFrameItem(
|
|
||||||
frame: StackFrameProxyImpl,
|
|
||||||
val stackTraceElement: StackTraceElement,
|
|
||||||
stackFrame: XStackFrame,
|
|
||||||
location: Location
|
|
||||||
) : CoroutineStackFrameItem(frame, stackFrame, location) {
|
|
||||||
fun emptyDescriptor() =
|
|
||||||
EmptyStackFrameDescriptor(stackTraceElement, frame)
|
|
||||||
}
|
|
||||||
|
|
||||||
class SuspendCoroutineStackFrameItem(
|
|
||||||
frame: StackFrameProxyImpl,
|
|
||||||
val stackTraceElement: StackTraceElement,
|
|
||||||
stackFrame: XStackFrame,
|
|
||||||
val lastObservedFrameFieldRef: ObjectReference
|
|
||||||
) : CoroutineStackFrameItem(frame, stackFrame, frame.location()) {
|
|
||||||
fun emptyDescriptor() =
|
|
||||||
EmptyStackFrameDescriptor(stackTraceElement, frame)
|
|
||||||
}
|
|
||||||
|
|
||||||
class AsyncCoroutineStackFrameItem(
|
|
||||||
frame: StackFrameProxyImpl,
|
|
||||||
val frameItem: StackFrameItem,
|
|
||||||
stackFrame: XStackFrame
|
|
||||||
) : CoroutineStackFrameItem(frame, stackFrame, frame.location())
|
|
||||||
|
|
||||||
class RunningCoroutineStackFrameItem(
|
|
||||||
frame: StackFrameProxyImpl,
|
|
||||||
stackFrame: XStackFrame
|
|
||||||
) : CoroutineStackFrameItem(frame, stackFrame, frame.location())
|
|
||||||
|
|
||||||
abstract class CoroutineStackFrameItem(val frame: StackFrameProxyImpl, val stackFrame: XStackFrame, val location: Location) {
|
|
||||||
val log by logger
|
|
||||||
|
|
||||||
fun sourcePosition() : XSourcePosition? = stackFrame.sourcePosition
|
|
||||||
|
|
||||||
fun uniqueId(): String {
|
|
||||||
try {
|
|
||||||
return location.safeSourceName() + ":" + location.safeMethod().toString() + ":" +
|
|
||||||
location.safeLineNumber() + ":" + location.safeSourceLineNumber()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
log.error(e)
|
|
||||||
return location.method().toString() + ":" + location.lineNumber()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+58
-4
@@ -5,11 +5,65 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutine.data
|
package org.jetbrains.kotlin.idea.debugger.coroutine.data
|
||||||
|
|
||||||
import com.intellij.debugger.jdi.GeneratedLocation
|
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||||
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.StackFrameDescriptorImpl
|
||||||
import com.intellij.xdebugger.frame.XNamedValue
|
import com.intellij.xdebugger.frame.XNamedValue
|
||||||
|
import com.intellij.xdebugger.frame.XStackFrame
|
||||||
|
import com.sun.jdi.Location
|
||||||
|
import com.sun.jdi.ObjectReference
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.EmptyStackFrameDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.safeLineNumber
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.safeMethod
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.safeSourceLineNumber
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.safeSourceName
|
||||||
|
|
||||||
class CoroutineAsyncStackFrameItem(
|
class CreationCoroutineStackFrameItem(
|
||||||
val location: GeneratedLocation,
|
val frame: StackFrameProxyImpl,
|
||||||
|
val stackTraceElement: StackTraceElement,
|
||||||
|
location: Location
|
||||||
|
) : CoroutineStackFrameItem(location, emptyList()) {
|
||||||
|
fun emptyDescriptor() =
|
||||||
|
EmptyStackFrameDescriptor(stackTraceElement, frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
class SuspendCoroutineStackFrameItem(
|
||||||
|
val frame: StackFrameProxyImpl,
|
||||||
|
val stackTraceElement: StackTraceElement,
|
||||||
|
val lastObservedFrameFieldRef: ObjectReference,
|
||||||
|
location: Location,
|
||||||
|
spilledVariables: List<XNamedValue> = emptyList()
|
||||||
|
) : CoroutineStackFrameItem(location, spilledVariables) {
|
||||||
|
fun emptyDescriptor() =
|
||||||
|
EmptyStackFrameDescriptor(stackTraceElement, frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
class RunningCoroutineStackFrameItem(
|
||||||
|
val frame: StackFrameProxyImpl,
|
||||||
|
val stackFrame: XStackFrame,
|
||||||
|
spilledVariables: List<XNamedValue> = emptyList()
|
||||||
|
) : CoroutineStackFrameItem(frame.location(), spilledVariables)
|
||||||
|
|
||||||
|
class RestoredCoroutineStackFrameItem(
|
||||||
|
val frame: StackFrameProxyImpl,
|
||||||
|
location: Location,
|
||||||
spilledVariables: List<XNamedValue>
|
spilledVariables: List<XNamedValue>
|
||||||
) : StackFrameItem(location, spilledVariables)
|
) : CoroutineStackFrameItem(location, spilledVariables) {
|
||||||
|
fun emptyDescriptor() =
|
||||||
|
StackFrameDescriptorImpl(frame, MethodsTracker())
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultCoroutineStackFrameItem(location: Location, spilledVariables: List<XNamedValue>) :
|
||||||
|
CoroutineStackFrameItem(location, spilledVariables)
|
||||||
|
|
||||||
|
sealed class CoroutineStackFrameItem(val location: Location, val spilledVariables: List<XNamedValue>) :
|
||||||
|
StackFrameItem(location, spilledVariables) {
|
||||||
|
val log by logger
|
||||||
|
|
||||||
|
fun uniqueId(): String {
|
||||||
|
return location.safeSourceName() + ":" + location.safeMethod().toString() + ":" +
|
||||||
|
location.safeLineNumber() + ":" + location.safeSourceLineNumber()
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-2
@@ -6,7 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.idea.debugger.coroutine.data
|
package org.jetbrains.kotlin.idea.debugger.coroutine.data
|
||||||
|
|
||||||
import com.sun.jdi.*
|
import com.sun.jdi.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.command.CoroutineStackFrameItem
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents state of a coroutine.
|
* Represents state of a coroutine.
|
||||||
@@ -23,7 +22,7 @@ data class CoroutineInfoData(
|
|||||||
) {
|
) {
|
||||||
var stackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
var stackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
||||||
|
|
||||||
// @TODO for refactoring/removal
|
// @TODO for refactoring/removal along with DumpPanel
|
||||||
val stringStackTrace: String by lazy {
|
val stringStackTrace: String by lazy {
|
||||||
buildString {
|
buildString {
|
||||||
appendln("\"$name\", state: $state")
|
appendln("\"$name\", state: $state")
|
||||||
|
|||||||
-87
@@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.coroutine.data
|
|
||||||
|
|
||||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
|
||||||
import com.intellij.debugger.impl.descriptors.data.DescriptorData
|
|
||||||
import com.intellij.debugger.impl.descriptors.data.DisplayKey
|
|
||||||
import com.intellij.debugger.impl.descriptors.data.SimpleDisplayKey
|
|
||||||
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
|
||||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
|
||||||
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.sun.jdi.*
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.LookupContinuation
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
|
||||||
|
|
||||||
@Deprecated("moved to XCoroutineView")
|
|
||||||
class CoroutineStackTraceData(
|
|
||||||
infoData: CoroutineInfoData,
|
|
||||||
proxy: StackFrameProxyImpl,
|
|
||||||
evalContext: EvaluationContextImpl,
|
|
||||||
val frame: StackTraceElement
|
|
||||||
) : CoroutineStackDescriptorData(infoData, proxy, evalContext) {
|
|
||||||
|
|
||||||
override fun hashCode() = frame.hashCode()
|
|
||||||
|
|
||||||
override fun equals(other: Any?) =
|
|
||||||
other is CoroutineStackTraceData && frame == other.frame
|
|
||||||
|
|
||||||
override fun createDescriptorImpl(project: Project): NodeDescriptorImpl {
|
|
||||||
val context = ExecutionContext(evalContext, proxy)
|
|
||||||
val lookupContinuation = LookupContinuation(context, frame)
|
|
||||||
|
|
||||||
// retrieve continuation only if suspend method
|
|
||||||
val continuation = lookupContinuation.findContinuation(infoData.lastObservedFrameFieldRef)
|
|
||||||
|
|
||||||
return if (continuation is ObjectReference)
|
|
||||||
SuspendStackFrameDescriptor(infoData, frame, proxy, continuation)
|
|
||||||
else
|
|
||||||
CoroutineCreatedStackFrameDescriptor(frame, proxy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("moved to XCoroutineView")
|
|
||||||
class CoroutineStackFrameData(
|
|
||||||
infoData: CoroutineInfoData,
|
|
||||||
proxy: StackFrameProxyImpl,
|
|
||||||
evalContext: EvaluationContextImpl,
|
|
||||||
val frame: StackFrameItem
|
|
||||||
) :
|
|
||||||
CoroutineStackDescriptorData(infoData, proxy, evalContext) {
|
|
||||||
override fun createDescriptorImpl(project: Project): NodeDescriptorImpl =
|
|
||||||
AsyncStackFrameDescriptor(infoData, frame, proxy)
|
|
||||||
|
|
||||||
override fun hashCode() = frame.hashCode()
|
|
||||||
|
|
||||||
override fun equals(other: Any?) = other is CoroutineStackFrameData && frame == other.frame
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("moved to XCoroutineView")
|
|
||||||
abstract class CoroutineStackDescriptorData(
|
|
||||||
val infoData: CoroutineInfoData,
|
|
||||||
val proxy: StackFrameProxyImpl,
|
|
||||||
val evalContext: EvaluationContextImpl
|
|
||||||
) : DescriptorData<NodeDescriptorImpl>() {
|
|
||||||
|
|
||||||
override fun getDisplayKey(): DisplayKey<NodeDescriptorImpl> = SimpleDisplayKey(infoData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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>() {
|
|
||||||
|
|
||||||
override fun createDescriptorImpl(project: Project) =
|
|
||||||
CoroutineDescriptorImpl(infoData)
|
|
||||||
|
|
||||||
override fun equals(other: Any?) = if (other !is CoroutineDescriptorData) false else infoData.name == other.infoData.name
|
|
||||||
|
|
||||||
override fun hashCode() = infoData.name.hashCode()
|
|
||||||
|
|
||||||
override fun getDisplayKey(): DisplayKey<CoroutineDescriptorImpl> = SimpleDisplayKey(infoData.name)
|
|
||||||
}
|
|
||||||
+18
-11
@@ -17,13 +17,15 @@ import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_VARIABLE_NAME
|
|||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
|
||||||
import org.jetbrains.kotlin.idea.debugger.SUSPEND_LAMBDA_CLASSES
|
import org.jetbrains.kotlin.idea.debugger.SUSPEND_LAMBDA_CLASSES
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineAsyncStackFrameItem
|
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineStackFrameItem
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutine.data.DefaultCoroutineStackFrameItem
|
||||||
import org.jetbrains.kotlin.idea.debugger.isSubtype
|
import org.jetbrains.kotlin.idea.debugger.isSubtype
|
||||||
import org.jetbrains.kotlin.idea.debugger.safeVisibleVariableByName
|
import org.jetbrains.kotlin.idea.debugger.safeVisibleVariableByName
|
||||||
|
|
||||||
class AsyncStackTraceContext(
|
class AsyncStackTraceContext(
|
||||||
val context: ExecutionContext,
|
val context: ExecutionContext,
|
||||||
val method: Method) {
|
val method: Method
|
||||||
|
) {
|
||||||
val log by logger
|
val log by logger
|
||||||
val debugMetadataKtType = context.findClassSafe(
|
val debugMetadataKtType = context.findClassSafe(
|
||||||
DEBUG_METADATA_KT
|
DEBUG_METADATA_KT
|
||||||
@@ -33,9 +35,9 @@ class AsyncStackTraceContext(
|
|||||||
const val DEBUG_METADATA_KT = "kotlin.coroutines.jvm.internal.DebugMetadataKt"
|
const val DEBUG_METADATA_KT = "kotlin.coroutines.jvm.internal.DebugMetadataKt"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAsyncStackTraceIfAny() : List<CoroutineAsyncStackFrameItem> {
|
fun getAsyncStackTraceIfAny(): List<CoroutineStackFrameItem> {
|
||||||
val continuation = locateContinuation() ?: return emptyList()
|
val continuation = locateContinuation() ?: return emptyList()
|
||||||
val frames = mutableListOf<CoroutineAsyncStackFrameItem>()
|
val frames = mutableListOf<CoroutineStackFrameItem>()
|
||||||
try {
|
try {
|
||||||
collectFramesRecursively(continuation, frames)
|
collectFramesRecursively(continuation, frames)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -44,8 +46,8 @@ class AsyncStackTraceContext(
|
|||||||
return frames
|
return frames
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun locateContinuation() : ObjectReference? {
|
private fun locateContinuation(): ObjectReference? {
|
||||||
val continuation : ObjectReference?
|
val continuation: ObjectReference?
|
||||||
if (isInvokeSuspendMethod(method)) {
|
if (isInvokeSuspendMethod(method)) {
|
||||||
continuation = context.frameProxy.thisObject() ?: return null
|
continuation = context.frameProxy.thisObject() ?: return null
|
||||||
if (!isSuspendLambda(continuation.referenceType()))
|
if (!isSuspendLambda(continuation.referenceType()))
|
||||||
@@ -70,7 +72,7 @@ class AsyncStackTraceContext(
|
|||||||
private fun isSuspendLambda(referenceType: ReferenceType): Boolean =
|
private fun isSuspendLambda(referenceType: ReferenceType): Boolean =
|
||||||
SUSPEND_LAMBDA_CLASSES.any { referenceType.isSubtype(it) }
|
SUSPEND_LAMBDA_CLASSES.any { referenceType.isSubtype(it) }
|
||||||
|
|
||||||
private fun collectFramesRecursively(continuation: ObjectReference, consumer: MutableList<CoroutineAsyncStackFrameItem>) {
|
private fun collectFramesRecursively(continuation: ObjectReference, consumer: MutableList<CoroutineStackFrameItem>) {
|
||||||
val continuationType = continuation.referenceType() as? ClassType ?: return
|
val continuationType = continuation.referenceType() as? ClassType ?: return
|
||||||
val baseContinuationSupertype = findBaseContinuationSuperSupertype(continuationType) ?: return
|
val baseContinuationSupertype = findBaseContinuationSuperSupertype(continuationType) ?: return
|
||||||
|
|
||||||
@@ -78,7 +80,7 @@ class AsyncStackTraceContext(
|
|||||||
|
|
||||||
location?.let {
|
location?.let {
|
||||||
val spilledVariables = getSpilledVariables(continuation) ?: emptyList()
|
val spilledVariables = getSpilledVariables(continuation) ?: emptyList()
|
||||||
consumer.add(CoroutineAsyncStackFrameItem(location, spilledVariables))
|
consumer.add(DefaultCoroutineStackFrameItem(location, spilledVariables))
|
||||||
}
|
}
|
||||||
|
|
||||||
val completionField = baseContinuationSupertype.fieldByName("completion") ?: return
|
val completionField = baseContinuationSupertype.fieldByName("completion") ?: return
|
||||||
@@ -86,11 +88,11 @@ class AsyncStackTraceContext(
|
|||||||
collectFramesRecursively(completion, consumer)
|
collectFramesRecursively(completion, consumer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createLocation(continuation: ObjectReference) : GeneratedLocation? {
|
private fun createLocation(continuation: ObjectReference): GeneratedLocation? {
|
||||||
val instance = invokeGetStackTraceElement(continuation) ?: return null
|
val instance = invokeGetStackTraceElement(continuation) ?: return null
|
||||||
val className = context.invokeMethodAsString(instance, "getClassName") ?: return null
|
val className = context.invokeMethodAsString(instance, "getClassName") ?: return null
|
||||||
val methodName = context.invokeMethodAsString(instance, "getMethodName") ?: return null
|
val methodName = context.invokeMethodAsString(instance, "getMethodName") ?: return null
|
||||||
val lineNumber = context.invokeMethodAsInt(instance,"getLineNumber")?.takeIf {
|
val lineNumber = context.invokeMethodAsInt(instance, "getLineNumber")?.takeIf {
|
||||||
it >= 0
|
it >= 0
|
||||||
} ?: return null
|
} ?: return null
|
||||||
val locationClass = context.findClassSafe(className) ?: return null
|
val locationClass = context.findClassSafe(className) ?: return null
|
||||||
@@ -110,7 +112,12 @@ 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) ?: return null
|
context.invokeMethodAsArray(
|
||||||
|
debugMetadataKtType,
|
||||||
|
"getSpilledVariableFieldMapping",
|
||||||
|
"(Lkotlin/coroutines/jvm/internal/BaseContinuationImpl;)[Ljava/lang/String;",
|
||||||
|
continuation
|
||||||
|
) ?: return null
|
||||||
|
|
||||||
context.keepReference(rawSpilledVariables)
|
context.keepReference(rawSpilledVariables)
|
||||||
|
|
||||||
|
|||||||
+15
-10
@@ -12,6 +12,7 @@ import com.intellij.debugger.engine.SuspendContextImpl
|
|||||||
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.jdi.ThreadReferenceProxyImpl
|
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
||||||
|
import com.intellij.debugger.memory.utils.StackFrameItem
|
||||||
import com.intellij.ide.CommonActionsManager
|
import com.intellij.ide.CommonActionsManager
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
import com.intellij.openapi.actionSystem.ActionManager
|
import com.intellij.openapi.actionSystem.ActionManager
|
||||||
@@ -47,8 +48,7 @@ import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineDebuggerContentInfo
|
|||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineDebuggerContentInfo.Companion.XCOROUTINE_POPUP_ACTION_GROUP
|
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineDebuggerContentInfo.Companion.XCOROUTINE_POPUP_ACTION_GROUP
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.VersionedImplementationProvider
|
import org.jetbrains.kotlin.idea.debugger.coroutine.VersionedImplementationProvider
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.command.*
|
import org.jetbrains.kotlin.idea.debugger.coroutine.command.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
|
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.SyntheticStackFrame
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.*
|
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParams
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParams
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParamsProvider
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParamsProvider
|
||||||
@@ -207,7 +207,7 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
infoData.stackFrameList.forEach {
|
infoData.stackFrameList.forEach {
|
||||||
if (it is CreationCoroutineStackFrameItem)
|
if (it is CreationCoroutineStackFrameItem)
|
||||||
creationStack.add(it)
|
creationStack.add(it)
|
||||||
else
|
else if (it is CoroutineStackFrameItem)
|
||||||
children.add(CoroutineFrameValue(it))
|
children.add(CoroutineFrameValue(it))
|
||||||
}
|
}
|
||||||
children.add(CreationFramesContainer(infoData, creationStack))
|
children.add(CreationFramesContainer(infoData, creationStack))
|
||||||
@@ -281,12 +281,12 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
val threadSuspendContext = session.suspendContext as SuspendContextImpl
|
val threadSuspendContext = session.suspendContext as SuspendContextImpl
|
||||||
when (frame) {
|
when (frame) {
|
||||||
is RunningCoroutineStackFrameItem -> {
|
is RunningCoroutineStackFrameItem -> {
|
||||||
val threadProxy = valueContainer.frame.frame.threadProxy()
|
val threadProxy = frame.frame.threadProxy()
|
||||||
val isCurrentContext = threadSuspendContext.thread == threadProxy
|
val isCurrentContext = threadSuspendContext.thread == threadProxy
|
||||||
createStackAndSetFrame(threadProxy, { frame.stackFrame }, isCurrentContext)
|
createStackAndSetFrame(threadProxy, { frame.stackFrame }, isCurrentContext)
|
||||||
}
|
}
|
||||||
is CreationCoroutineStackFrameItem -> {
|
is CreationCoroutineStackFrameItem -> {
|
||||||
val position = getPosition(frame.stackTraceElement) ?: return false
|
val position = getPosition(frame.stackTraceElement.className, frame.stackTraceElement.lineNumber) ?: return false
|
||||||
val threadProxy = threadSuspendContext.thread as ThreadReferenceProxyImpl
|
val threadProxy = threadSuspendContext.thread as ThreadReferenceProxyImpl
|
||||||
createStackAndSetFrame(threadProxy, { SyntheticStackFrame(frame.emptyDescriptor(), emptyList(), position) })
|
createStackAndSetFrame(threadProxy, { SyntheticStackFrame(frame.emptyDescriptor(), emptyList(), position) })
|
||||||
}
|
}
|
||||||
@@ -295,7 +295,11 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
val executionContext = executionContext(threadSuspendContext, frame.frame)
|
val executionContext = executionContext(threadSuspendContext, frame.frame)
|
||||||
createStackAndSetFrame(threadProxy, { createSyntheticStackFrame(executionContext, frame) })
|
createStackAndSetFrame(threadProxy, { createSyntheticStackFrame(executionContext, frame) })
|
||||||
}
|
}
|
||||||
is AsyncCoroutineStackFrameItem -> {
|
is RestoredCoroutineStackFrameItem -> {
|
||||||
|
val threadProxy = frame.frame.threadProxy()
|
||||||
|
val position = getPosition(frame.location.declaringType().name(), frame.location.lineNumber()) ?: return false
|
||||||
|
createStackAndSetFrame(threadProxy, { SyntheticStackFrame(frame.emptyDescriptor(), frame.spilledVariables, position) })
|
||||||
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
}
|
}
|
||||||
@@ -331,15 +335,15 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
JavaExecutionStack(threadReferenceProxy, debugProcess, isCurrentContext)
|
JavaExecutionStack(threadReferenceProxy, debugProcess, isCurrentContext)
|
||||||
|
|
||||||
|
|
||||||
private fun getPosition(frame: StackTraceElement): XSourcePosition? {
|
private fun getPosition(className: String, lineNumber: Int): XSourcePosition? {
|
||||||
val psiFacade = JavaPsiFacade.getInstance(project)
|
val psiFacade = JavaPsiFacade.getInstance(project)
|
||||||
val psiClass = psiFacade.findClass(
|
val psiClass = psiFacade.findClass(
|
||||||
frame.className.substringBefore("$"), // find outer class, for which psi exists TODO
|
className.substringBefore("$"), // find outer class, for which psi exists TODO
|
||||||
GlobalSearchScope.everythingScope(project)
|
GlobalSearchScope.everythingScope(project)
|
||||||
)
|
)
|
||||||
val classFile = psiClass?.containingFile?.virtualFile
|
val classFile = psiClass?.containingFile?.virtualFile
|
||||||
// to convert to 0-based line number or '-1' to do not move
|
// 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
|
val lineNumber = if (lineNumber > 0) lineNumber - 1 else return null
|
||||||
return XDebuggerUtil.getInstance().createPosition(classFile, lineNumber)
|
return XDebuggerUtil.getInstance().createPosition(classFile, lineNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +356,8 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
executionContext: ExecutionContext,
|
executionContext: ExecutionContext,
|
||||||
frame: SuspendCoroutineStackFrameItem
|
frame: SuspendCoroutineStackFrameItem
|
||||||
): SyntheticStackFrame? {
|
): SyntheticStackFrame? {
|
||||||
val position = getPosition(frame.stackTraceElement) ?: return null
|
|
||||||
|
val position = applicationThreadExecutor.readAction { getPosition(frame.stackTraceElement.className, frame.stackTraceElement.lineNumber) } ?: return null
|
||||||
val lookupContinuation = LookupContinuation(executionContext, frame.stackTraceElement)
|
val lookupContinuation = LookupContinuation(executionContext, frame.stackTraceElement)
|
||||||
val continuation = lookupContinuation.findContinuation(frame.lastObservedFrameFieldRef) ?: return null
|
val continuation = lookupContinuation.findContinuation(frame.lastObservedFrameFieldRef) ?: return null
|
||||||
|
|
||||||
|
|||||||
+12
@@ -89,6 +89,10 @@ fun Field.safeType(): Type? {
|
|||||||
return wrapClassNotLoadedException { type() }
|
return wrapClassNotLoadedException { type() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Method.safeSignature() {
|
||||||
|
wrapIllegalArgumentExceptionException { signature() }
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun <T> wrapAbsentInformationException(block: () -> T): T? {
|
private inline fun <T> wrapAbsentInformationException(block: () -> T): T? {
|
||||||
return try {
|
return try {
|
||||||
block()
|
block()
|
||||||
@@ -101,6 +105,14 @@ private inline fun <T> wrapAbsentInformationException(block: () -> T): T? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun <T> wrapIllegalArgumentExceptionException(block: () -> T): T? {
|
||||||
|
return try {
|
||||||
|
block()
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun <T> wrapClassNotLoadedException(block: () -> T): T? {
|
private inline fun <T> wrapClassNotLoadedException(block: () -> T): T? {
|
||||||
return try {
|
return try {
|
||||||
block()
|
block()
|
||||||
|
|||||||
Reference in New Issue
Block a user