From 3d343e7d219374d338743334d1dd67b6eca7fcc5 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 5 Jul 2022 15:06:21 +0200 Subject: [PATCH 1/7] Fix debug module publication with shadow plugin Fixes #3345 Fixes #3334 --- gradle/publish.gradle | 6 +----- kotlinx-coroutines-debug/build.gradle | 18 +++++++++--------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/gradle/publish.gradle b/gradle/publish.gradle index 00034bfa87..f3b1561dde 100644 --- a/gradle/publish.gradle +++ b/gradle/publish.gradle @@ -45,11 +45,7 @@ publishing { // Configure java publications for regular non-MPP modules publications { maven(MavenPublication) { - if (project.name == "kotlinx-coroutines-debug") { - project.shadow.component(it) - } else { - from components.java - } + from components.java artifact sourcesJar } } diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index a420004801..2e10308696 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -8,14 +8,6 @@ configurations { shadowDeps // shaded dependencies, not included into the resulting .pom file compileOnly.extendsFrom(shadowDeps) runtimeOnly.extendsFrom(shadowDeps) - - /* - * It is possible to extend a particular configuration with shadow, - * but in that case it changes dependency type to "runtime" and resolves it - * (so it cannot be further modified). Otherwise, shadow just ignores all dependencies. - */ - shadow.extendsFrom(api) // shadow - resulting configuration with shaded jar file - configureKotlinJvmPlatform(shadow) } dependencies { @@ -39,19 +31,27 @@ java { } jar { + setEnabled(false) manifest { attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain" attributes "Can-Redefine-Classes": "true" } } -shadowJar { +def shadowJarTask = shadowJar { classifier null // Shadow only byte buddy, do not package kotlin stdlib configurations = [project.configurations.shadowDeps] relocate('net.bytebuddy', 'kotlinx.coroutines.repackaged.net.bytebuddy') } +configurations { + artifacts { + add("apiElements", shadowJarTask) + add("runtimeElements", shadowJarTask) + } +} + def commonKoverExcludes = // Never used, safety mechanism ["kotlinx.coroutines.debug.internal.NoOpProbesKt"] From 0289836ca5ee37ca3d3aea559d06f11d2829b99d Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 8 Jul 2022 11:27:23 +0200 Subject: [PATCH 2/7] ~tweak conf --- kotlinx-coroutines-debug/build.gradle | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index 2e10308696..ded13b7b5a 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -32,10 +32,6 @@ java { jar { setEnabled(false) - manifest { - attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain" - attributes "Can-Redefine-Classes": "true" - } } def shadowJarTask = shadowJar { @@ -43,6 +39,11 @@ def shadowJarTask = shadowJar { // Shadow only byte buddy, do not package kotlin stdlib configurations = [project.configurations.shadowDeps] relocate('net.bytebuddy', 'kotlinx.coroutines.repackaged.net.bytebuddy') + + manifest { + attributes "Premain-Class": "kotlinx.coroutines.debug.AgentPremain" + attributes "Can-Redefine-Classes": "true" + } } configurations { From ca8ec3483638f27db1a625b16a8738f82197e334 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 8 Jul 2022 12:44:48 +0200 Subject: [PATCH 3/7] ~restore manifest compatibility --- kotlinx-coroutines-debug/build.gradle | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index ded13b7b5a..9165a41f9b 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -34,6 +34,18 @@ jar { setEnabled(false) } +// This is a rough estimation of what shadow plugin has been doing with our default configuration prior to +// 1.6.2: https://github.com/johnrengelman/shadow/blob/1ff12fc816629ae5bc331fa3889c8ecfcaee7b27/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy#L72-L82 +// We just emulate it here for backwards compatibility +shadowJar.configure { + def classpath = project.objects.fileCollection().from { -> + project.configurations.findByName('runtimeClasspath') + } + doFirst { + manifest.attributes 'Class-Path': classpath.collect { "${it.name}" }.findAll { it }.join(' ') + } +} + def shadowJarTask = shadowJar { classifier null // Shadow only byte buddy, do not package kotlin stdlib From 633eafb732c4f1a90fe1864993232aa915c4d723 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 8 Jul 2022 12:57:05 +0200 Subject: [PATCH 4/7] Add integration test --- integration-testing/build.gradle | 22 +++++++++++++- .../kotlin/DynamicAttachDebugTest.kt | 29 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 integration-testing/src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle index c23d35fbe7..fb0f19e1ad 100644 --- a/integration-testing/build.gradle +++ b/integration-testing/build.gradle @@ -53,6 +53,18 @@ sourceSets { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-debug:$coroutines_version" } } + + debugDynamicAgentTest { + kotlin + compileClasspath += sourceSets.test.runtimeClasspath + runtimeClasspath += sourceSets.test.runtimeClasspath + + dependencies { + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-debug:$coroutines_version" + } + } + coreAgentTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath @@ -93,6 +105,14 @@ task debugAgentTest(type: Test) { systemProperties project.properties.subMap(["overwrite.probes"]) } +task debugDynamicAgentTest(type: Test) { + environment "version", coroutines_version + def sourceSet = sourceSets.debugDynamicAgentTest + testClassesDirs = sourceSet.output.classesDirs + classpath = sourceSet.runtimeClasspath +} + + task coreAgentTest(type: Test) { def sourceSet = sourceSets.coreAgentTest def coroutinesCoreJar = sourceSet.runtimeClasspath.filter {it.name == "kotlinx-coroutines-core-jvm-${coroutines_version}.jar" }.singleFile @@ -106,5 +126,5 @@ compileTestKotlin { } check { - dependsOn([withGuavaTest, mavenTest, debugAgentTest, coreAgentTest, 'smokeTest:build']) + dependsOn([withGuavaTest, debugDynamicAgentTest, mavenTest, debugAgentTest, coreAgentTest, 'smokeTest:build']) } diff --git a/integration-testing/src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt b/integration-testing/src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt new file mode 100644 index 0000000000..9d74019f5b --- /dev/null +++ b/integration-testing/src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +import org.junit.* +import kotlinx.coroutines.* +import kotlinx.coroutines.debug.* +import org.junit.Test +import java.io.* +import java.lang.IllegalStateException + +class DynamicAttachDebugTest { + + @Test + fun testAgentDumpsCoroutines() = + DebugProbes.withDebugProbes { + runBlocking { + val baos = ByteArrayOutputStream() + DebugProbes.dumpCoroutines(PrintStream(baos)) + // if the agent works, then dumps should contain something, + // at least the fact that this test is running. + Assert.assertTrue(baos.toString().contains("testAgentDumpsCoroutines")) + } + } + + @Test(expected = IllegalStateException::class) + fun testAgentIsNotInstalled() { + DebugProbes.dumpCoroutines(PrintStream(ByteArrayOutputStream())) + } +} From f090b09f9f65d2d9e668471e6a3ced2d2aff8310 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 8 Jul 2022 16:43:32 +0200 Subject: [PATCH 5/7] ~comments --- integration-testing/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle index fb0f19e1ad..57a27d9c2a 100644 --- a/integration-testing/build.gradle +++ b/integration-testing/build.gradle @@ -23,6 +23,7 @@ dependencies { } sourceSets { + // Test that relies on Guava to reflectively check all Throwable subclasses in coroutines withGuavaTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath @@ -33,6 +34,7 @@ sourceSets { implementation 'com.google.guava:guava:31.1-jre' } } + // Checks correctness of Maven publication (JAR resources) and absence of atomicfu symbols mavenTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath @@ -43,6 +45,7 @@ sourceSets { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" } } + // Checks that kotlinx-coroutines-debug can be used as -javaagent parameter debugAgentTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath @@ -54,6 +57,7 @@ sourceSets { } } + // Checks that kotlinx-coroutines-debug agent can self-attach dynamically to JVM as standalone dependency debugDynamicAgentTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath @@ -65,6 +69,7 @@ sourceSets { } } + // Checks that kotlinx-coroutines-core can be used as -javaagent parameter coreAgentTest { kotlin compileClasspath += sourceSets.test.runtimeClasspath From 6da448204afcb6f0e1bd55af282f3b9aa6c024f4 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 8 Jul 2022 17:53:20 +0200 Subject: [PATCH 6/7] ~remove unnecessary env --- integration-testing/build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle index 57a27d9c2a..985a40ed96 100644 --- a/integration-testing/build.gradle +++ b/integration-testing/build.gradle @@ -111,13 +111,11 @@ task debugAgentTest(type: Test) { } task debugDynamicAgentTest(type: Test) { - environment "version", coroutines_version def sourceSet = sourceSets.debugDynamicAgentTest testClassesDirs = sourceSet.output.classesDirs classpath = sourceSet.runtimeClasspath } - task coreAgentTest(type: Test) { def sourceSet = sourceSets.coreAgentTest def coroutinesCoreJar = sourceSet.runtimeClasspath.filter {it.name == "kotlinx-coroutines-core-jvm-${coroutines_version}.jar" }.singleFile From e7e8aec96d48da5d45b0f4fd26f786ab6be59fe5 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 11 Jul 2022 11:48:30 +0300 Subject: [PATCH 7/7] Update integration-testing/src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt Co-authored-by: dkhalanskyjb <52952525+dkhalanskyjb@users.noreply.github.com> --- .../src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-testing/src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt b/integration-testing/src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt index 9d74019f5b..ff9cac847c 100644 --- a/integration-testing/src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt +++ b/integration-testing/src/debugDynamicAgentTest/kotlin/DynamicAttachDebugTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ import org.junit.* import kotlinx.coroutines.*