(CoroutineDebugger) Stack printing logic extracted to the single place
for DebugMetadata and CoroutinePanel, some corner cases fixed, test cases added. Added: - external maven dependencies in test scenarios - comparing stack traces + variables on breakpoint
This commit is contained in:
+7
-28
@@ -9,10 +9,9 @@ import com.intellij.debugger.engine.AsyncStackTraceProvider
|
||||
import com.intellij.debugger.engine.JavaValue
|
||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineAsyncStackTraceProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CoroutineFrameBuilder
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isPreFlight
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.getSafe
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
@@ -26,18 +25,15 @@ abstract class AbstractAsyncStackTraceTest : KotlinDescriptorTestCaseWithSteppin
|
||||
}
|
||||
|
||||
override fun doMultiFileTest(files: TestFiles, preferences: DebuggerPreferences) {
|
||||
val asyncStackTraceProvider = getAsyncStackTraceProvider()
|
||||
if (asyncStackTraceProvider == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
doOnBreakpoint {
|
||||
val frameProxy = this.frameProxy
|
||||
if (frameProxy != null) {
|
||||
try {
|
||||
val coroutineInfoData =
|
||||
asyncStackTraceProvider.lookupForResumeContinuation(frameProxy, this, emptyList())?.coroutineInfoData
|
||||
val sem = frameProxy.location().isPreFlight()
|
||||
val coroutineInfoData = if (sem.isCoroutineFound())
|
||||
CoroutineFrameBuilder.lookupContinuation(this, frameProxy, sem)?.coroutineInfoData
|
||||
else
|
||||
null
|
||||
if (coroutineInfoData != null && coroutineInfoData.stackTrace.isNotEmpty()) {
|
||||
print(renderAsyncStackTrace(coroutineInfoData.stackTrace), ProcessOutputTypes.SYSTEM)
|
||||
} else {
|
||||
@@ -56,23 +52,6 @@ abstract class AbstractAsyncStackTraceTest : KotlinDescriptorTestCaseWithSteppin
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAsyncStackTraceProvider(): CoroutineAsyncStackTraceProvider? {
|
||||
val area = Extensions.getArea(null)
|
||||
if (!area.hasExtensionPoint(ASYNC_STACKTRACE_EP_NAME)) {
|
||||
System.err.println("$ASYNC_STACKTRACE_EP_NAME extension point is not found (probably old IDE version)")
|
||||
return null
|
||||
}
|
||||
|
||||
val extensionPoint = area.getExtensionPoint<Any>(ASYNC_STACKTRACE_EP_NAME)
|
||||
val provider = extensionPoint.extensions.firstIsInstanceOrNull<CoroutineAsyncStackTraceProvider>()
|
||||
|
||||
if (provider == null) {
|
||||
System.err.println("Kotlin coroutine async stack trace provider is not found")
|
||||
}
|
||||
|
||||
return provider
|
||||
}
|
||||
|
||||
private fun Throwable.stackTraceAsString(): String {
|
||||
val writer = StringWriter()
|
||||
printStackTrace(PrintWriter(writer))
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.test
|
||||
|
||||
import com.intellij.debugger.engine.AsyncStackTraceProvider
|
||||
import com.intellij.debugger.engine.JavaStackFrame
|
||||
import com.intellij.debugger.engine.SuspendContextImpl
|
||||
import com.intellij.debugger.impl.OutputChecker
|
||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.xdebugger.frame.*
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineAsyncStackTraceProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.format
|
||||
import org.jetbrains.kotlin.idea.debugger.invokeInManagerThread
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.KotlinOutputChecker
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.XDebuggerTestUtil
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
abstract class AbstractContinuationStackTraceTest : KotlinDescriptorTestCaseWithStackFrames() {
|
||||
override fun doMultiFileTest(files: TestFiles, preferences: DebuggerPreferences) {
|
||||
val asyncStackTraceProvider = getAsyncStackTraceProvider()
|
||||
|
||||
doWhenXSessionPausedThenResume {
|
||||
printContext(debugProcess.debuggerContext)
|
||||
val suspendContext = debuggerSession.xDebugSession?.getSuspendContext()
|
||||
var executionStack = suspendContext?.getActiveExecutionStack()
|
||||
if (executionStack != null) {
|
||||
try {
|
||||
out("Thread stack trace:")
|
||||
val stackFrames: List<XStackFrame> = XDebuggerTestUtil.collectFrames(executionStack)
|
||||
for (frame in stackFrames) {
|
||||
if (frame is JavaStackFrame) {
|
||||
out(frame)
|
||||
asyncStackTraceProvider?.getAsyncStackTrace(frame, suspendContext as SuspendContextImpl)?.let {
|
||||
for (frameItem in it)
|
||||
out(frameItem)
|
||||
return@doWhenXSessionPausedThenResume
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
val stackTrace = e.stackTraceAsString()
|
||||
System.err.println("Exception occurred on calculating async stack traces: $stackTrace")
|
||||
throw e
|
||||
}
|
||||
} else {
|
||||
println("FrameProxy is 'null', can't calculate async stack trace", ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test
|
||||
|
||||
import com.intellij.debugger.engine.JavaStackFrame
|
||||
import com.intellij.debugger.engine.SuspendContextImpl
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.xdebugger.frame.XStackFrame
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.XDebuggerTestUtil
|
||||
|
||||
abstract class AbstractXCoroutinesStackTraceTest : KotlinDescriptorTestCaseWithStackFrames() {
|
||||
override fun doMultiFileTest(files: TestFiles, preferences: DebuggerPreferences) {
|
||||
val asyncStackTraceProvider = getAsyncStackTraceProvider()
|
||||
|
||||
doWhenXSessionPausedThenResume {
|
||||
printContext(debugProcess.debuggerContext)
|
||||
val suspendContext = debuggerSession.xDebugSession?.getSuspendContext()
|
||||
var executionStack = suspendContext?.getActiveExecutionStack()
|
||||
if (executionStack != null) {
|
||||
try {
|
||||
out("Thread stack trace:")
|
||||
val stackFrames: List<XStackFrame> = XDebuggerTestUtil.collectFrames(executionStack)
|
||||
for (frame in stackFrames) {
|
||||
if (frame is JavaStackFrame) {
|
||||
out(frame)
|
||||
asyncStackTraceProvider?.getAsyncStackTrace(frame, suspendContext as SuspendContextImpl)?.let {
|
||||
for (frameItem in it)
|
||||
out(frameItem)
|
||||
return@doWhenXSessionPausedThenResume
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
val stackTrace = e.stackTraceAsString()
|
||||
System.err.println("Exception occurred on calculating async stack traces: $stackTrace")
|
||||
throw e
|
||||
}
|
||||
} else {
|
||||
println("FrameProxy is 'null', can't calculate async stack trace", ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/continuation")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class ContinuationStackTraceTestGenerated extends AbstractContinuationStackTraceTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInContinuation() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/continuation"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendFun.kt")
|
||||
public void testSuspendFun() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/continuation/suspendFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("suspendLambda.kt")
|
||||
public void testSuspendLambda() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/continuation/suspendLambda.kt");
|
||||
}
|
||||
}
|
||||
+16
-2
@@ -10,6 +10,7 @@ import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.libraries.ui.OrderRoot
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
@@ -39,6 +40,7 @@ class DebuggerTestCompilerFacility(files: List<TestFile>, private val jvmTarget:
|
||||
|
||||
private val mainFiles: TestFilesByLanguage
|
||||
private val libraryFiles: TestFilesByLanguage
|
||||
private val mavenArtifacts = mutableListOf<String>()
|
||||
|
||||
init {
|
||||
val splitFiles = splitByTarget(files)
|
||||
@@ -61,6 +63,15 @@ class DebuggerTestCompilerFacility(files: List<TestFile>, private val jvmTarget:
|
||||
compileLibrary(libraryFiles, srcDir, classesDir)
|
||||
}
|
||||
|
||||
fun addDependencies(libraryPaths: List<String>) {
|
||||
for (libraryPath in libraryPaths) {
|
||||
mavenArtifacts.add(libraryPath)
|
||||
}
|
||||
}
|
||||
|
||||
fun kotlinStdlibInMavenArtifacts() =
|
||||
mavenArtifacts.find { it.contains(Regex("""kotlin-stdlib-\d+\.\d+\.\d+(\-\w+)?""")) }
|
||||
|
||||
fun compileLibrary(srcDir: File, classesDir: File) {
|
||||
compileLibrary(this.libraryFiles, srcDir, classesDir)
|
||||
|
||||
@@ -72,19 +83,22 @@ class DebuggerTestCompilerFacility(files: List<TestFile>, private val jvmTarget:
|
||||
resources.copy(classesDir)
|
||||
(kotlin + java).copy(srcDir)
|
||||
|
||||
if (kotlinStdlibInMavenArtifacts() == null)
|
||||
mavenArtifacts.add(kotlinStdlibPath)
|
||||
|
||||
if (kotlin.isNotEmpty()) {
|
||||
MockLibraryUtil.compileKotlin(
|
||||
srcDir.absolutePath,
|
||||
classesDir,
|
||||
listOf("-jvm-target", jvmTarget.description),
|
||||
kotlinStdlibPath
|
||||
*(mavenArtifacts.toTypedArray())
|
||||
)
|
||||
}
|
||||
|
||||
if (java.isNotEmpty()) {
|
||||
CodegenTestUtil.compileJava(
|
||||
java.map { File(srcDir, it.name).absolutePath },
|
||||
listOf(kotlinStdlibPath, classesDir.absolutePath),
|
||||
mavenArtifacts + classesDir.absolutePath,
|
||||
listOf("-g"),
|
||||
classesDir
|
||||
)
|
||||
|
||||
+12
-3
@@ -9,6 +9,8 @@ import com.intellij.debugger.impl.DescriptorTestCase
|
||||
import com.intellij.debugger.impl.OutputChecker
|
||||
import com.intellij.execution.configurations.JavaParameters
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.jarRepository.JarRepositoryManager
|
||||
import com.intellij.jarRepository.RemoteRepositoryDescription
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
@@ -21,6 +23,7 @@ import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testFramework.EdtTestUtil
|
||||
import com.intellij.xdebugger.XDebugSession
|
||||
import org.jetbrains.idea.maven.aether.ArtifactKind
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
|
||||
@@ -38,6 +41,7 @@ import org.jetbrains.kotlin.test.isIgnoredInDatabaseWithLog
|
||||
import org.jetbrains.kotlin.test.testFramework.runWriteAction
|
||||
import org.junit.ComparisonFailure
|
||||
import java.io.File
|
||||
import org.jetbrains.jps.model.library.JpsMavenRepositoryLibraryDescriptor as JpsMavenRepositoryLibraryDescriptor
|
||||
|
||||
internal const val KOTLIN_LIBRARY_NAME = "KotlinLibrary"
|
||||
internal const val TEST_LIBRARY_NAME = "TestLibrary"
|
||||
@@ -109,7 +113,10 @@ abstract class KotlinDescriptorTestCase : DescriptorTestCase() {
|
||||
val compilerFacility = DebuggerTestCompilerFacility(testFiles, jvmTarget)
|
||||
|
||||
for (library in preferences[DebuggerPreferenceKeys.ATTACH_LIBRARY]) {
|
||||
compilerFacility.compileExternalLibrary(library, librarySrcDirectory, libraryOutputDirectory)
|
||||
if (library.startsWith("maven("))
|
||||
addMavenDependency(compilerFacility, library)
|
||||
else
|
||||
compilerFacility.compileExternalLibrary(library, librarySrcDirectory, libraryOutputDirectory)
|
||||
}
|
||||
|
||||
compilerFacility.compileLibrary(librarySrcDirectory, libraryOutputDirectory)
|
||||
@@ -125,6 +132,9 @@ abstract class KotlinDescriptorTestCase : DescriptorTestCase() {
|
||||
doMultiFileTest(testFiles, preferences)
|
||||
}
|
||||
|
||||
open fun addMavenDependency(compilerFacility: DebuggerTestCompilerFacility, library: String) {
|
||||
}
|
||||
|
||||
private fun createTestFiles(wholeFile: File, wholeFileContents: String): TestFiles {
|
||||
val testFiles = org.jetbrains.kotlin.test.TestFiles.createTestFiles(
|
||||
wholeFile.name,
|
||||
@@ -227,6 +237,5 @@ abstract class KotlinDescriptorTestCase : DescriptorTestCase() {
|
||||
return super.shouldRunTest() && !isIgnoredInDatabaseWithLog(this)
|
||||
}
|
||||
|
||||
private fun getTestDirectoryPath(): String = javaClass.getAnnotation(TestMetadata::class.java).value
|
||||
protected fun getTestDirectoryPath(): String = javaClass.getAnnotation(TestMetadata::class.java).value
|
||||
}
|
||||
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test
|
||||
|
||||
import com.intellij.debugger.engine.AsyncStackTraceProvider
|
||||
import com.intellij.debugger.engine.JavaStackFrame
|
||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
||||
import com.intellij.execution.configurations.JavaParameters
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.jarRepository.JarRepositoryManager
|
||||
import com.intellij.jarRepository.RemoteRepositoryDescription
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.libraries.ui.OrderRoot
|
||||
import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor
|
||||
import com.intellij.testFramework.EdtTestUtil
|
||||
import com.intellij.xdebugger.frame.XNamedValue
|
||||
import com.intellij.xdebugger.frame.XStackFrame
|
||||
import org.jetbrains.idea.maven.aether.ArtifactKind
|
||||
import org.jetbrains.jps.model.library.JpsMavenRepositoryLibraryDescriptor
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineAsyncStackTraceProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CreationCoroutineStackFrameItem
|
||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.format
|
||||
import org.jetbrains.kotlin.idea.debugger.invokeInManagerThread
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.XDebuggerTestUtil
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.test.testFramework.runWriteAction
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
abstract class KotlinDescriptorTestCaseWithStackFrames() : KotlinDescriptorTestCaseWithStepping() {
|
||||
private companion object {
|
||||
val ASYNC_STACKTRACE_EP_NAME = AsyncStackTraceProvider.EP.name
|
||||
val INDENT_FRAME = 1
|
||||
val INDENT_VARIABLES = 2
|
||||
}
|
||||
|
||||
val agentList = mutableListOf<JpsMavenRepositoryLibraryDescriptor>()
|
||||
val classPath = mutableListOf<String>()
|
||||
|
||||
protected fun out(stackFrame: StackFrameItem) {
|
||||
if (stackFrame is CreationCoroutineStackFrameItem && stackFrame.first)
|
||||
out(0, "Creation stack frame")
|
||||
out(INDENT_FRAME, stackFrame.format())
|
||||
outVariables(debugProcess.invokeInManagerThread { stackFrame.createFrame(debugProcess) } ?: return)
|
||||
}
|
||||
|
||||
protected fun out(frame: JavaStackFrame) {
|
||||
out(INDENT_FRAME, XDebuggerTestUtil.getFramePresentation(frame))
|
||||
outVariables(frame)
|
||||
}
|
||||
|
||||
private fun outVariables(stackFrame: XStackFrame) {
|
||||
val variables = XDebuggerTestUtil.collectChildrenWithError(stackFrame)
|
||||
val sorted = mutableListOf<String>()
|
||||
sorted.addAll(variables.first.mapNotNull { if (it is XNamedValue) it.name else null })
|
||||
sorted.sort()
|
||||
val varString = sorted.joinToString()
|
||||
out(INDENT_VARIABLES, "($varString)")
|
||||
}
|
||||
|
||||
protected fun out(text: String) {
|
||||
println(text, ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
|
||||
protected fun out(indent: Int, text: String) {
|
||||
println("\t".repeat(indent) + text, ProcessOutputTypes.SYSTEM)
|
||||
println(text)
|
||||
}
|
||||
|
||||
protected fun Throwable.stackTraceAsString(): String {
|
||||
val writer = StringWriter()
|
||||
printStackTrace(PrintWriter(writer))
|
||||
return writer.toString()
|
||||
}
|
||||
|
||||
protected fun getAsyncStackTraceProvider(): CoroutineAsyncStackTraceProvider? {
|
||||
val area = Extensions.getArea(null)
|
||||
if (!area.hasExtensionPoint(ASYNC_STACKTRACE_EP_NAME)) {
|
||||
System.err.println("${ASYNC_STACKTRACE_EP_NAME} extension point is not found (probably old IDE version)")
|
||||
return null
|
||||
}
|
||||
|
||||
val extensionPoint = area.getExtensionPoint<Any>(ASYNC_STACKTRACE_EP_NAME)
|
||||
val provider = extensionPoint.extensions.firstIsInstanceOrNull<CoroutineAsyncStackTraceProvider>()
|
||||
|
||||
if (provider == null) {
|
||||
System.err.println("Kotlin coroutine async stack trace provider is not found")
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
override fun addMavenDependency(compilerFacility: DebuggerTestCompilerFacility, library: String) {
|
||||
val regex = Regex(pattern = """maven\(([a-zA-Z0-9_\-\.]+)\:([a-zA-Z0-9_\-\.]+):([a-zA-Z0-9_\-\.]+)\)(\-javaagent)?""")
|
||||
val result = regex.matchEntire(library) ?: return
|
||||
val (_, groupId: String, artifactId: String, version: String, agent: String) = result.groupValues
|
||||
if ("-javaagent" == agent)
|
||||
agentList.add(JpsMavenRepositoryLibraryDescriptor(groupId, artifactId, version, false))
|
||||
val description = JpsMavenRepositoryLibraryDescriptor(groupId, artifactId, version)
|
||||
val artifacts = loadDependencies(description)
|
||||
compilerFacility.addDependencies(artifacts.map { it.file.presentableUrl })
|
||||
addLibraries(artifacts)
|
||||
}
|
||||
|
||||
override fun createJavaParameters(mainClass: String?): JavaParameters {
|
||||
val params = super.createJavaParameters(mainClass)
|
||||
for (entry in classPath) {
|
||||
params.classPath.add(entry)
|
||||
}
|
||||
for (agent in agentList) {
|
||||
val dependencies = loadDependencies(agent)
|
||||
for (dependency in dependencies) {
|
||||
params.vmParametersList.add("-javaagent:${dependency.file.presentableUrl}")
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
private fun addLibraries(artifacts: MutableList<OrderRoot>) =
|
||||
EdtTestUtil.runInEdtAndWait {
|
||||
runWriteAction {
|
||||
val model = ModuleRootManager.getInstance(myModule).modifiableModel
|
||||
val customLibEditor = NewLibraryEditor().apply {
|
||||
for (artifact in artifacts) {
|
||||
classPath.add(artifact.file.presentableUrl) // for sandbox jvm
|
||||
addRoot(artifact.file, artifact.type)
|
||||
}
|
||||
}
|
||||
ConfigLibraryUtil.addLibrary(customLibEditor, model, null) // for kotlin compiler
|
||||
model.commit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadDependencies(
|
||||
description: JpsMavenRepositoryLibraryDescriptor
|
||||
): MutableList<OrderRoot> {
|
||||
return JarRepositoryManager.loadDependenciesSync(
|
||||
project, description, setOf(ArtifactKind.ARTIFACT),
|
||||
RemoteRepositoryDescription.DEFAULT_REPOSITORIES, null
|
||||
) ?: throw AssertionError("Maven Dependency not found: $description")
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/xcoroutines")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class XCoroutinesStackTraceTestGenerated extends AbstractXCoroutinesStackTraceTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInXcoroutines() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/xcoroutines"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("coroutineSuspendFun.kt")
|
||||
public void testCoroutineSuspendFun() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/xcoroutines/coroutineSuspendFun.kt");
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test.util;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.ui.TextTransferable;
|
||||
import com.intellij.xdebugger.frame.XExecutionStack;
|
||||
import com.intellij.xdebugger.frame.XStackFrame;
|
||||
import com.intellij.xdebugger.frame.XValue;
|
||||
import com.intellij.xdebugger.frame.XValueContainer;
|
||||
import com.intellij.xdebugger.impl.frame.XStackFrameContainerEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* Kotlin clone of com.intellij.xdebugger.XDebuggerTestUtil
|
||||
*/
|
||||
public class XDebuggerTestUtil {
|
||||
public static final int TIMEOUT_MS = 25_000;
|
||||
|
||||
public static List<XStackFrame> collectFrames(@NotNull XExecutionStack thread) {
|
||||
return collectFrames(thread, TIMEOUT_MS * 2);
|
||||
}
|
||||
|
||||
public static List<XStackFrame> collectFrames(XExecutionStack thread, long timeout) {
|
||||
return collectFrames(thread, timeout, XDebuggerTestUtil::waitFor);
|
||||
}
|
||||
|
||||
public static List<XStackFrame> collectFrames(XExecutionStack thread, long timeout, BiFunction<Semaphore, Long, Boolean> waitFunction) {
|
||||
return collectFramesWithError(thread, timeout, waitFunction).first;
|
||||
}
|
||||
|
||||
public static String getFramePresentation(XStackFrame frame) {
|
||||
TextTransferable.ColoredStringBuilder builder = new TextTransferable.ColoredStringBuilder();
|
||||
frame.customizePresentation(builder);
|
||||
return builder.getBuilder().toString();
|
||||
}
|
||||
|
||||
public static Pair<List<XStackFrame>, String> collectFramesWithError(XExecutionStack thread, long timeout, BiFunction<Semaphore, Long, Boolean> waitFunction) {
|
||||
XTestStackFrameContainer container = new XTestStackFrameContainer();
|
||||
thread.computeStackFrames(0, container);
|
||||
return container.waitFor(timeout, waitFunction);
|
||||
}
|
||||
|
||||
public static boolean waitFor(Semaphore semaphore, long timeoutInMillis) {
|
||||
long end = System.currentTimeMillis() + timeoutInMillis;
|
||||
long remaining = timeoutInMillis;
|
||||
do {
|
||||
try {
|
||||
return semaphore.tryAcquire(remaining, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (InterruptedException ignored) {
|
||||
remaining = end - System.currentTimeMillis();
|
||||
}
|
||||
} while (remaining > 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class XTestStackFrameContainer extends XTestContainer<XStackFrame> implements XStackFrameContainerEx {
|
||||
public volatile XStackFrame frameToSelect;
|
||||
|
||||
@Override
|
||||
public void addStackFrames(@NotNull List<? extends XStackFrame> stackFrames, boolean last) {
|
||||
addChildren(stackFrames, last);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addStackFrames(@NotNull List<? extends XStackFrame> stackFrames, @Nullable XStackFrame toSelect, boolean last) {
|
||||
if (toSelect != null) frameToSelect = toSelect;
|
||||
addChildren(stackFrames, last);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void errorOccurred(@NotNull String errorMessage) {
|
||||
setErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<XValue> collectChildren(XValueContainer value) {
|
||||
return collectChildren(value, XDebuggerTestUtil::waitFor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<XValue> collectChildren(XValueContainer value, BiFunction<Semaphore, Long, Boolean> waitFunction) {
|
||||
final Pair<List<XValue>, String> childrenWithError = collectChildrenWithError(value, waitFunction);
|
||||
final String error = childrenWithError.second;
|
||||
assertNull("Error getting children: " + error, error);
|
||||
return childrenWithError.first;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Pair<List<XValue>, String> collectChildrenWithError(XValueContainer value) {
|
||||
return collectChildrenWithError(value, XDebuggerTestUtil::waitFor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Pair<List<XValue>, String> collectChildrenWithError(XValueContainer value,
|
||||
BiFunction<Semaphore, Long, Boolean> waitFunction) {
|
||||
XTestCompositeNode container = new XTestCompositeNode();
|
||||
value.computeChildren(container);
|
||||
|
||||
return container.waitFor(TIMEOUT_MS, waitFunction);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test.util;
|
||||
|
||||
import com.intellij.xdebugger.frame.XCompositeNode;
|
||||
import com.intellij.xdebugger.frame.XValue;
|
||||
import com.intellij.xdebugger.frame.XValueChildrenList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class XTestCompositeNode extends XTestContainer<XValue> implements XCompositeNode {
|
||||
@Override
|
||||
public void addChildren(@NotNull XValueChildrenList children, boolean last) {
|
||||
final List<XValue> list = new ArrayList<>();
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
list.add(children.getValue(i));
|
||||
}
|
||||
addChildren(list, last);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlreadySorted(boolean alreadySorted) {
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test.util;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class XTestContainer<T> {
|
||||
private final List<T> myChildren = new SmartList<>();
|
||||
private String myErrorMessage;
|
||||
private final Semaphore myFinished = new Semaphore(0);
|
||||
|
||||
public void addChildren(List<? extends T> children, boolean last) {
|
||||
myChildren.addAll(children);
|
||||
if (last) myFinished.release();
|
||||
}
|
||||
|
||||
public void tooManyChildren(int remaining) {
|
||||
myFinished.release();
|
||||
}
|
||||
|
||||
public void setMessage(@NotNull String message, Icon icon, @NotNull final SimpleTextAttributes attributes, @Nullable XDebuggerTreeNodeHyperlink link) {
|
||||
}
|
||||
|
||||
public void setErrorMessage(@NotNull String message, @Nullable XDebuggerTreeNodeHyperlink link) {
|
||||
setErrorMessage(message);
|
||||
}
|
||||
|
||||
public void setErrorMessage(@NotNull String errorMessage) {
|
||||
myErrorMessage = errorMessage;
|
||||
myFinished.release();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Pair<List<T>, String> waitFor(long timeoutMs) {
|
||||
return waitFor(timeoutMs, (semaphore, timeout) -> XDebuggerTestUtil.waitFor(myFinished, timeout));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Pair<List<T>, String> waitFor(long timeoutMs, BiFunction<? super Semaphore, ? super Long, Boolean> waitFunction) {
|
||||
if (!waitFunction.apply(myFinished, timeoutMs)) {
|
||||
throw new AssertionError("Waiting timed out");
|
||||
}
|
||||
|
||||
return Pair.create(myChildren, myErrorMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package coroutine1
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.coroutines.startCoroutine
|
||||
|
||||
fun main() {
|
||||
val cnt = Continuation<Int>(EmptyCoroutineContext) { }
|
||||
val result = ::test1.startCoroutine(1, cnt)
|
||||
println(result)
|
||||
}
|
||||
|
||||
suspend fun test1(i: Int): Int {
|
||||
val test1 = "a"
|
||||
a(test1)
|
||||
println(test1)
|
||||
return i
|
||||
}
|
||||
|
||||
suspend fun a(aParam: String) {
|
||||
val a = "a"
|
||||
b(a)
|
||||
println(a)
|
||||
}
|
||||
|
||||
suspend fun b(bParam: String) {
|
||||
val b = "b"
|
||||
// Breakpoint!
|
||||
println(b)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package continuation
|
||||
|
||||
fun main() {
|
||||
val a = "a"
|
||||
fibonacci().take(10).toList()
|
||||
}
|
||||
|
||||
fun nextSequence(terms: Pair<Int, Int>): Pair<Int, Int> {
|
||||
val terms1 = Pair(terms.second, terms.first + terms.second)
|
||||
if (terms1.first == 8) {
|
||||
//Breakpoint!
|
||||
return terms1
|
||||
} else
|
||||
return terms1
|
||||
}
|
||||
|
||||
fun fibonacci() = sequence {
|
||||
var terms = Pair(0, 1)
|
||||
var step = 0
|
||||
|
||||
while (true) {
|
||||
yield(terms.first)
|
||||
terms = nextSequence(terms)
|
||||
step++
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
LineBreakpoint created at sequence.kt:14
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
sequence.kt:12
|
||||
Thread stack trace:
|
||||
nextSequence:12, SequenceKt (continuation)
|
||||
(terms, terms1)
|
||||
invokeSuspend:23, SequenceKt$fibonacci$1 (continuation)
|
||||
($result, $this$sequence, step, terms, this)
|
||||
resumeWith:33, kotlin.coroutines.jvm.internal.BaseContinuationImpl
|
||||
($i$a$-with-BaseContinuationImpl$resumeWith$1, $this$with, completion, current, param, result, this)
|
||||
hasNext:140, kotlin.sequences.SequenceBuilderIterator
|
||||
(step, this)
|
||||
hasNext:374, kotlin.sequences.TakeSequence$iterator$1
|
||||
(this)
|
||||
toCollection:722, kotlin.sequences.SequencesKt___SequencesKt
|
||||
($this$toCollection, destination)
|
||||
toMutableList:752, kotlin.sequences.SequencesKt___SequencesKt
|
||||
($this$toMutableList)
|
||||
toList:743, kotlin.sequences.SequencesKt___SequencesKt
|
||||
($this$toList)
|
||||
main:5, continuation.SequenceKt
|
||||
(a)
|
||||
main:-1, continuation.SequenceKt
|
||||
()
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package continuation
|
||||
// ATTACH_LIBRARY: coroutines
|
||||
// ATTACH_LIBRARY: maven(org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.3.4)-javaagent
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.yield
|
||||
|
||||
fun main() {
|
||||
val main = "main"
|
||||
runBlocking {
|
||||
a()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun a() {
|
||||
val a = "a"
|
||||
b(a)
|
||||
val aLate = "a" // to prevent stackFrame to collapse
|
||||
}
|
||||
|
||||
suspend fun b(paramA: String) {
|
||||
yield()
|
||||
val b = "b"
|
||||
c(b)
|
||||
}
|
||||
|
||||
suspend fun c(paramB: String) {
|
||||
val c = "c"
|
||||
//Breakpoint!
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
LineBreakpoint created at coroutineSuspendFun.kt:30
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
coroutineSuspendFun.kt:30
|
||||
Thread stack trace:
|
||||
c:30, CoroutineSuspendFunKt (continuation)
|
||||
($completion, c, paramB)
|
||||
b:24, CoroutineSuspendFunKt (continuation)
|
||||
($completion, $continuation, $result, b, paramA)
|
||||
a:17, continuation.CoroutineSuspendFunKt
|
||||
(a)
|
||||
invokeSuspend:11, continuation.CoroutineSuspendFunKt$main$1
|
||||
($this$runBlocking)
|
||||
resumeWith:33, kotlin.coroutines.jvm.internal.BaseContinuationImpl
|
||||
($i$a$-with-BaseContinuationImpl$resumeWith$1, $this$with, completion, current, param, result, this)
|
||||
run:56, kotlinx.coroutines.DispatchedTask
|
||||
($i$a$-withCoroutineContext-DispatchedTask$run$1, $i$f$withCoroutineContext, context, continuation, countOrElement$iv, delegate, exception, fatalException, job, oldValue$iv, state, taskContext, this)
|
||||
processNextEvent:272, kotlinx.coroutines.EventLoopImplBase
|
||||
(delayed, this)
|
||||
joinBlocking:79, kotlinx.coroutines.BlockingCoroutine
|
||||
(this)
|
||||
runBlocking:54, kotlinx.coroutines.BuildersKt__BuildersKt
|
||||
(block, context, contextInterceptor, coroutine, currentThread, eventLoop, newContext)
|
||||
runBlocking:1, kotlinx.coroutines.BuildersKt
|
||||
()
|
||||
runBlocking$default:36, kotlinx.coroutines.BuildersKt__BuildersKt
|
||||
()
|
||||
runBlocking$default:1, kotlinx.coroutines.BuildersKt
|
||||
()
|
||||
main:10, continuation.CoroutineSuspendFunKt
|
||||
(main)
|
||||
main:-1, continuation.CoroutineSuspendFunKt
|
||||
()
|
||||
Creation stack frame
|
||||
createCoroutineUnintercepted:116, kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt
|
||||
()
|
||||
startCoroutineCancellable:26, kotlinx.coroutines.intrinsics.CancellableKt
|
||||
()
|
||||
runBlocking$default:1, kotlinx.coroutines.BuildersKt
|
||||
()
|
||||
main:10, continuation.CoroutineSuspendFunKt
|
||||
()
|
||||
main:-1, continuation.CoroutineSuspendFunKt
|
||||
()
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
Reference in New Issue
Block a user