diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 360b69f..c699d4c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,10 +18,10 @@ jobs: fetch-depth: 0 - - name: Set up JDK 8 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: 'temurin' diff --git a/CHANGELOG.md b/CHANGELOG.md index bc6f753..31c0f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## Version: [v1.0.8](https://github.com/newrelic/newrelic-java-kotlin-coroutines/releases/tag/v1.0.8) | Created: 2025-09-04 + + ## Version: [v1.0.6](https://github.com/newrelic/newrelic-java-kotlin-coroutines/releases/tag/v1.0.6) | Created: 2025-03-17 diff --git a/Kotlin-Coroutines-Suspends/build.gradle b/Kotlin-Coroutines-Suspends/build.gradle index 149b584..e3d0e60 100644 --- a/Kotlin-Coroutines-Suspends/build.gradle +++ b/Kotlin-Coroutines-Suspends/build.gradle @@ -4,11 +4,9 @@ apply plugin: 'java' dependencies { - implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.4.0' + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0") // New Relic Java Agent dependencies - implementation 'com.newrelic.agent.java:newrelic-agent:6.4.1' - implementation 'com.newrelic.agent.java:newrelic-api:6.4.1' implementation fileTree(include: ['*.jar'], dir: '../libs') implementation fileTree(include: ['*.jar'], dir: '../test-lib') } @@ -24,9 +22,14 @@ jar { } verifyInstrumentation { - // Verifier plugin documentation: - // https://github.com/newrelic/newrelic-gradle-verify-instrumentation - // Example: - // passes 'javax.servlet:servlet-api:[2.2,2.5]' - // exclude 'javax.servlet:servlet-api:2.4.public_draft' + passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core:[1.4.0,)' + passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.4.0)' + excludeRegex '.*SNAPSHOT' + excludeRegex '.*alpha' + excludeRegex '.*Beta' + excludeRegex '.*-eap-.*' + excludeRegex '.*-native-.*' + excludeRegex '.*-M[0-9]' + excludeRegex '.*-rc' + excludeRegex '.*-RC' } \ No newline at end of file diff --git a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/SuspendIgnores.java b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/SuspendIgnores.java deleted file mode 100644 index 47d3409..0000000 --- a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/SuspendIgnores.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.newrelic.api.agent.Config; -import com.newrelic.api.agent.NewRelic; - -public class SuspendIgnores { - - private static final List ignoredSuspends = new ArrayList(); - private static final String SUSPENDSIGNORECONFIG = "Coroutines.ignores.suspends"; - private static final List ignoredPackages = new ArrayList<>(); - - static { - Config config = NewRelic.getAgent().getConfig(); - String value = config.getValue(SUSPENDSIGNORECONFIG); - init(value); - ignoredPackages.add("kotlin.coroutines"); - ignoredPackages.add("kotlinx.coroutines"); - } - - private static void init(String value) { - if(value != null && !value.isEmpty()) { - String[] ignores = value.split(","); - for(String ignore : ignores) { - addIgnore(ignore); - } - } - } - - public static void reset(Config config) { - ignoredSuspends.clear(); - String value = config.getValue(SUSPENDSIGNORECONFIG); - init(value); - } - - public static void addIgnore(String s) { - if(!ignoredSuspends.contains(s)) { - ignoredSuspends.add(s); - NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore suspends named {0}", s); - } - } - - public static boolean ignoreSuspend(Object obj) { - String objString = obj.toString(); - Class clazz = obj.getClass(); - String className = clazz.getName(); - String packageName = clazz.getPackage().getName(); - - for(String ignored : ignoredPackages) { - if(packageName.startsWith(ignored)) { - return true; - } - } - - boolean objStringMatch = ignoredSuspends.contains(objString); - boolean classNameMatch = ignoredSuspends.contains(className); - - if(objStringMatch || classNameMatch) { - return true; - } - - for(String s : ignoredSuspends) { - Pattern pattern = Pattern.compile(s); - Matcher matcher1 = pattern.matcher(objString); - Matcher matcher2 = pattern.matcher(className); - if(matcher1.matches() || matcher2.matches()) return true; - } - - return false; - } -} diff --git a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/Utils.java b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/Utils.java deleted file mode 100644 index da25bf9..0000000 --- a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/Utils.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines; - -import com.newrelic.agent.config.AgentConfig; -import com.newrelic.agent.config.AgentConfigListener; -import com.newrelic.agent.service.ServiceFactory; -import com.newrelic.api.agent.Config; -import com.newrelic.api.agent.NewRelic; - -public class Utils implements AgentConfigListener { - - private static final Utils INSTANCE = new Utils(); - public static final String CREATEMETHOD1 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4"; - public static final String CREATEMETHOD2 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3"; - public static String sub = "createCoroutineFromSuspendFunction"; - private static final String CONT_LOC = "Continuation at"; - - static { - ServiceFactory.getConfigService().addIAgentConfigListener(INSTANCE); - Config config = NewRelic.getAgent().getConfig(); - SuspendIgnores.reset(config); - } - - @Override - public void configChanged(String appName, AgentConfig agentConfig) { - SuspendIgnores.reset(agentConfig); - } - - public static String getSuspendString(String cont_string, Object obj) { - if(cont_string.equals(CREATEMETHOD1) || cont_string.equals(CREATEMETHOD2)) return sub; - if(cont_string.startsWith(CONT_LOC)) { - return cont_string; - } - - int index = cont_string.indexOf('@'); - if(index > -1) { - return cont_string.substring(0, index); - } - - return obj.getClass().getName(); - } -} diff --git a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/CoroutinesPreMain.java b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/CoroutinesPreMain.java deleted file mode 100644 index e1dfd9a..0000000 --- a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/CoroutinesPreMain.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines.tracing; - -import java.lang.instrument.Instrumentation; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; - -import com.newrelic.agent.TracerService; -import com.newrelic.agent.core.CoreService; -import com.newrelic.agent.instrumentation.ClassTransformerService; -import com.newrelic.agent.instrumentation.context.ClassMatchVisitorFactory; -import com.newrelic.agent.instrumentation.context.InstrumentationContextManager; -import com.newrelic.agent.service.ServiceFactory; -import com.newrelic.api.agent.NewRelic; - -public class CoroutinesPreMain { - - private static int max_retries = 20; - private static ScheduledExecutorService executor = null; - - public static void premain(String args, Instrumentation inst) { - boolean b = setup(); - if(!b) { - executor = Executors.newSingleThreadScheduledExecutor(); - executor.schedule(new Setup(), 100L, TimeUnit.MILLISECONDS); - } - - } - - public static boolean setup() { - - TracerService tracerService = ServiceFactory.getTracerService(); - ClassTransformerService classTransformerService = ServiceFactory.getClassTransformerService(); - CoreService coreService = ServiceFactory.getCoreService(); - - if(tracerService != null && classTransformerService != null && coreService != null) { - tracerService.registerTracerFactory(SuspendTracerFactory.TRACER_FACTORY_NAME, new SuspendTracerFactory()); - InstrumentationContextManager contextMgr = classTransformerService.getContextManager(); - - if(contextMgr != null) { - SuspendClassTransformer suspendTransformer = new SuspendClassTransformer(contextMgr); - SuspendClassAndMethod suspendMatcher = new SuspendClassAndMethod(); - ClassMatchVisitorFactory suspendMatchVistor = suspendTransformer.addMatcher(suspendMatcher); - - Set factories = new HashSet<>(); - factories.add(suspendMatchVistor); - Class[] allLoadedClasses = coreService.getInstrumentation().getAllLoadedClasses(); - - classTransformerService.retransformMatchingClassesImmediately(allLoadedClasses, factories); - - return true; - } - } - - return false; - } - - private static class Setup implements Runnable { - - private static int count = 0; - - @Override - public void run() { - count++; - NewRelic.getAgent().getLogger().log(Level.FINE, "Call {0} to attempt setting up Suspend ClassTransformer",count); - boolean b = setup(); - - if(!b) { - if(count < max_retries) { - executor.schedule(this, 2L, TimeUnit.SECONDS); - } else { - NewRelic.getAgent().getLogger().log(Level.FINE, "Failed to initiate Suspend Client Transformer after {0} tries", max_retries); - executor.shutdownNow(); - } - } - - } - - } - -} diff --git a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassAndMethod.java b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassAndMethod.java deleted file mode 100644 index bf392fd..0000000 --- a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassAndMethod.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines.tracing; - -import com.newrelic.agent.instrumentation.classmatchers.ClassAndMethodMatcher; -import com.newrelic.agent.instrumentation.classmatchers.ClassMatcher; -import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher; - -public class SuspendClassAndMethod implements ClassAndMethodMatcher { - - private ClassMatcher classMatcher; - private MethodMatcher methodMatcher; - - public SuspendClassAndMethod() { - classMatcher = new SuspendClassMatcher(); - methodMatcher = new SuspendMethodMatcher(); - } - - @Override - public ClassMatcher getClassMatcher() { - return classMatcher; - } - - @Override - public MethodMatcher getMethodMatcher() { - return methodMatcher; - } - -} diff --git a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassMatcher.java b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassMatcher.java deleted file mode 100644 index b9093ac..0000000 --- a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassMatcher.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines.tracing; - -import java.util.ArrayList; -import java.util.Collection; - -import com.newrelic.agent.deps.org.objectweb.asm.ClassReader; -import com.newrelic.agent.instrumentation.classmatchers.ChildClassMatcher; -import com.newrelic.agent.instrumentation.classmatchers.ClassMatcher; -import com.newrelic.agent.instrumentation.classmatchers.ExactClassMatcher; -import com.newrelic.agent.instrumentation.classmatchers.NotMatcher; - -public class SuspendClassMatcher extends ClassMatcher { - - ChildClassMatcher matcher; - NotMatcher nrMatcher; - - public SuspendClassMatcher() { - matcher = new ChildClassMatcher("kotlin.coroutines.jvm.internal.BaseContinuationImpl",false); - nrMatcher = new NotMatcher(new ExactClassMatcher("com.newrelic.instrumentation.kotlin.coroutines.NRWrappedSuspend")); - } - - @Override - public boolean isMatch(ClassLoader loader, ClassReader cr) { - return matcher.isMatch(loader, cr) && nrMatcher.isMatch(loader, cr); - } - - @Override - public boolean isMatch(Class clazz) { - return matcher.isMatch(clazz) && nrMatcher.isMatch(clazz); - } - - @Override - public Collection getClassNames() { - Collection childClasses = matcher.getClassNames(); - Collection nrClasses = nrMatcher.getClassNames(); - if(childClasses == null && nrClasses == null) return null; - - ArrayList list = new ArrayList<>(); - if(childClasses != null) { - list.addAll(childClasses); - } - if(nrClasses != null) { - list.addAll(nrClasses); - } - - return list; - } - -} diff --git a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassTransformer.java b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassTransformer.java deleted file mode 100644 index ee81be0..0000000 --- a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassTransformer.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines.tracing; - -import java.lang.instrument.IllegalClassFormatException; -import java.security.ProtectionDomain; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; - -import com.newrelic.agent.deps.org.objectweb.asm.commons.Method; -import com.newrelic.agent.instrumentation.InstrumentationType; -import com.newrelic.agent.instrumentation.classmatchers.ClassAndMethodMatcher; -import com.newrelic.agent.instrumentation.classmatchers.OptimizedClassMatcher.Match; -import com.newrelic.agent.instrumentation.classmatchers.OptimizedClassMatcherBuilder; -import com.newrelic.agent.instrumentation.context.ClassMatchVisitorFactory; -import com.newrelic.agent.instrumentation.context.ContextClassTransformer; -import com.newrelic.agent.instrumentation.context.InstrumentationContext; -import com.newrelic.agent.instrumentation.context.InstrumentationContextManager; -import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher; -import com.newrelic.agent.instrumentation.tracing.TraceDetailsBuilder; -import com.newrelic.api.agent.NewRelic; - -public class SuspendClassTransformer implements ContextClassTransformer { - - private Map matchers = null; - private final InstrumentationContextManager contextMgr; - - public SuspendClassTransformer(InstrumentationContextManager mgr) { - contextMgr = mgr; - matchers = new HashMap<>(); - } - - protected ClassMatchVisitorFactory addMatcher(ClassAndMethodMatcher matcher) { - NewRelic.getAgent().getLogger().log(Level.FINE, "Adding matcher {0} to service classtransformer", matcher); - OptimizedClassMatcherBuilder builder = OptimizedClassMatcherBuilder.newBuilder(); - builder.addClassMethodMatcher(matcher); - ClassMatchVisitorFactory matchVistor = builder.build(); - matchers.put(matcher.getClass().getSimpleName(), matchVistor); - contextMgr.addContextClassTransformer(matchVistor, this); - return matchVistor; - } - - protected void removeMatcher(ClassAndMethodMatcher matcher) { - ClassMatchVisitorFactory matchVisitor = matchers.remove(matcher.getClass().getSimpleName()); - if(matchVisitor != null) { - contextMgr.removeMatchVisitor(matchVisitor); - } - } - - @Override - public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, - ProtectionDomain protectionDomain, byte[] classfileBuffer, InstrumentationContext context, Match match) - throws IllegalClassFormatException { - for(Method method : match.getMethods()) { - for(ClassAndMethodMatcher matcher : match.getClassMatches().keySet()) { - if (matcher.getMethodMatcher().matches(MethodMatcher.UNSPECIFIED_ACCESS, method.getName(), - method.getDescriptor(), match.getMethodAnnotations(method))) { - context.putTraceAnnotation(method, TraceDetailsBuilder.newBuilder().setTracerFactoryName(SuspendTracerFactory.TRACER_FACTORY_NAME).setDispatcher(true).setInstrumentationSourceName("CoroutinesCore").setInstrumentationType(InstrumentationType.TraceAnnotation).build()); - } - - } - } - return null; - } - -} diff --git a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendMethodMatcher.java b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendMethodMatcher.java deleted file mode 100644 index be48c40..0000000 --- a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendMethodMatcher.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines.tracing; - -import java.util.Set; - -import com.newrelic.agent.deps.org.objectweb.asm.commons.Method; -import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher; -import com.newrelic.agent.instrumentation.methodmatchers.NameMethodMatcher; - -public class SuspendMethodMatcher implements MethodMatcher { - - NameMethodMatcher matcher = null; - - public SuspendMethodMatcher() { - matcher = new NameMethodMatcher("invokeSuspend"); - } - - @Override - public boolean matches(int access, String name, String desc, Set annotations) { - return matcher.matches(access, name, desc, annotations); - } - - @Override - public Method[] getExactMethods() { - return matcher.getExactMethods(); - } - -} diff --git a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendTracerFactory.java b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendTracerFactory.java deleted file mode 100644 index 570ad90..0000000 --- a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendTracerFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines.tracing; - -import com.newrelic.agent.Transaction; -import com.newrelic.agent.tracers.ClassMethodSignature; -import com.newrelic.agent.tracers.DefaultTracer; -import com.newrelic.agent.tracers.Tracer; -import com.newrelic.agent.tracers.TracerFactory; -import com.newrelic.agent.tracers.metricname.SimpleMetricNameFormat; -import com.newrelic.instrumentation.kotlin.coroutines.SuspendIgnores; -import com.newrelic.instrumentation.kotlin.coroutines.Utils; - -public class SuspendTracerFactory implements TracerFactory { - - protected static final String TRACER_FACTORY_NAME = "SUSPEND_TRACER_FACTORY"; - - @Override - public Tracer getTracer(Transaction transaction, ClassMethodSignature sig, Object object, Object[] args) { - - if(SuspendIgnores.ignoreSuspend(object)) { - return null; - } - return new DefaultTracer(transaction, sig, object, new SimpleMetricNameFormat("Custom/SuspendFunction/"+Utils.getSuspendString(object.toString(), object))); - } - -} diff --git a/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/suspends/SuspendsUtils.java b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/suspends/SuspendsUtils.java new file mode 100644 index 0000000..d0f89f3 --- /dev/null +++ b/Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/suspends/SuspendsUtils.java @@ -0,0 +1,93 @@ +package com.newrelic.instrumentation.kotlin.suspends; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.ExitTracer; +import com.newrelic.agent.config.AgentConfig; +import com.newrelic.agent.config.AgentConfigListener; +import com.newrelic.agent.service.ServiceFactory; +import com.newrelic.agent.tracers.*; +import kotlin.coroutines.Continuation; +import kotlinx.coroutines.AbstractCoroutine; + +import java.util.Arrays; +import java.util.HashSet; + +public class SuspendsUtils implements AgentConfigListener { + + private static final HashSet ignoredSuspends = new HashSet<>(); + public static final String CREATE_METHOD1 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4"; + public static final String CREATE_METHOD2 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3"; + private static final String CONT_LOC = "Continuation at"; + public static String sub = "createCoroutineFromSuspendFunction"; + public static final String KOTLIN_PACKAGE = "kotlin"; + private static final String SUSPEND_FUNCTION_METRIC_NAME_PREFIX = "Custom/Kotlin/Coroutines/SuspendFunction/"; + private static final String SUSPENDSIGNORECONFIG = "Coroutines.ignores.suspends"; + + static { + ServiceFactory.getConfigService().addIAgentConfigListener(new SuspendsUtils()); + } + + private SuspendsUtils() {} + + public static ExitTracer getSuspendTracer(Continuation continuation) { + Class clazz = continuation.getClass(); + String className = clazz.getName(); + // don't track suspend functions in internal Coroutines classes (i.e. starts with kotlin or kotlinx) + if(className.startsWith(KOTLIN_PACKAGE)) { return null; } + String continuationString = getContinuationString(continuation); + // ignore if can't determine continuation string + if(continuationString == null || continuationString.isEmpty()) { return null; } + + for(String ignoredSuspend : ignoredSuspends) { + if(continuationString.matches(ignoredSuspend) || className.matches(ignoredSuspend)) { return null; } + } + if (ignoredSuspends.contains(continuationString) || ignoredSuspends.contains(className)) { + return null; + } + ClassMethodSignature signature = new ClassMethodSignature(clazz.getName(), "invokeSuspend", "(Ljava.lang.Object;)Ljava.lang.Object;"); + int index = ClassMethodSignatures.get().getIndex(signature); + if(index == -1) { + index = ClassMethodSignatures.get().add(signature); + } + + if(index >= 0) { + String metricName = SUSPEND_FUNCTION_METRIC_NAME_PREFIX + continuationString; + return AgentBridge.instrumentation.createTracer(continuation, index, metricName, DefaultTracer.DEFAULT_TRACER_FLAGS); + } + return null; + } + + public static String getContinuationString(Continuation continuation) { + String contString = continuation.toString(); + + if(contString.equals(CREATE_METHOD1) || contString.equals(CREATE_METHOD2)) { + return sub; + } + + if(contString.startsWith(CONT_LOC)) { + return contString; + } + + if(continuation instanceof AbstractCoroutine) { + return ((AbstractCoroutine)continuation).nameString$kotlinx_coroutines_core(); + } + + int index = contString.indexOf('@'); + if(index > -1) { + return contString.substring(0, index); + } + + return null; + } + + @Override + public void configChanged(String s, AgentConfig agentConfig) { + String value = agentConfig.getValue(SUSPENDSIGNORECONFIG); + if(value != null) { + String[] split = value.split(","); + if(split.length > 0) { + ignoredSuspends.addAll(Arrays.asList(split)); + } + } + } +} diff --git a/Kotlin-Coroutines-Suspends/src/main/java/kotlin/coroutines/jvm/internal/BaseContinuationImpl_Instrumentation.java b/Kotlin-Coroutines-Suspends/src/main/java/kotlin/coroutines/jvm/internal/BaseContinuationImpl_Instrumentation.java new file mode 100644 index 0000000..b2c3594 --- /dev/null +++ b/Kotlin-Coroutines-Suspends/src/main/java/kotlin/coroutines/jvm/internal/BaseContinuationImpl_Instrumentation.java @@ -0,0 +1,30 @@ +package kotlin.coroutines.jvm.internal; + +import com.newrelic.agent.bridge.ExitTracer; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.suspends.SuspendsUtils; +import kotlin.coroutines.Continuation; + +@Weave(type = MatchType.BaseClass, originalName = "kotlin.coroutines.jvm.internal.BaseContinuationImpl") +public abstract class BaseContinuationImpl_Instrumentation implements Continuation { + + protected Object invokeSuspend(Object result) { + ExitTracer tracer = SuspendsUtils.getSuspendTracer(this); + Object value = null; + try { + value = Weaver.callOriginal(); + } catch (Exception e) { + if(tracer != null) { + tracer.finish(e); + } + throw e; + } + if (tracer != null) { + tracer.finish(0, value); + } + return value; + } + +} diff --git a/Kotlin-Coroutines_1.4/build.gradle b/Kotlin-Coroutines_1.4/build.gradle index 0ceaed0..ebcaf4c 100644 --- a/Kotlin-Coroutines_1.4/build.gradle +++ b/Kotlin-Coroutines_1.4/build.gradle @@ -1,14 +1,12 @@ -// Build.gradle generated for instrumentation module Kotlin-Coroutines_1.2 apply plugin: 'java' +apply plugin: 'org.jetbrains.kotlin.jvm' dependencies { - implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.4.0' + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0") // New Relic Java Agent dependencies - implementation 'com.newrelic.agent.java:newrelic-agent:6.0.0' - implementation 'com.newrelic.agent.java:newrelic-api:6.0.0' implementation fileTree(include: ['*.jar'], dir: '../libs') } @@ -22,8 +20,8 @@ jar { } verifyInstrumentation { - passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core:[1.4.0,1.5.0)' - passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.4.0,1.5.0)' + passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core:[1.4.0,1.7.0)' + passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.4.0,1.7.0)' excludeRegex '.*SNAPSHOT' excludeRegex '.*alpha' excludeRegex '.*-eap-.*' diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/DispatchedTaskIgnores.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/DispatchedTaskIgnores.java deleted file mode 100644 index de0d9f5..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/DispatchedTaskIgnores.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_14; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - -import com.newrelic.api.agent.Config; -import com.newrelic.api.agent.NewRelic; - -public class DispatchedTaskIgnores { - - private static List ignoredTasks = new ArrayList<>(); - private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; - - static { - Config config = NewRelic.getAgent().getConfig(); - String ignores = config.getValue(DISPATCHEDIGNORECONFIG); - configure(ignores); - - } - - - public static boolean ignoreDispatchedTask(String contString) { - return ignoredTasks.contains(contString); - } - - public static void addIgnore(String ignore) { - if(!ignoredTasks.contains(ignore)) { - ignoredTasks.add(ignore); - NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore DispatchedTasks with continuation string {0}", ignore); - } - } - - public static void reset() { - ignoredTasks.clear(); - } - - protected static void configure(String result) { - if(result == null || result.isEmpty()) return; - String[] ignores = result.split(","); - for(String ignore : ignores) { - addIgnore(ignore); - } - } - -} diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRContinuationWrapper.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRContinuationWrapper.java deleted file mode 100644 index f851c44..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRContinuationWrapper.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_14; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; - -public class NRContinuationWrapper implements Continuation { - - private Continuation delegate = null; - private String name = null; - private static boolean isTransformed = false; - - public NRContinuationWrapper(Continuation d, String n) { - delegate = d; - name = n; - if(!isTransformed) { - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - isTransformed = true; - } - } - - @Override - public CoroutineContext getContext() { - return delegate.getContext(); - } - - @Override - @Trace(async=true) - public void resumeWith(Object p0) { - String contString = Utils.getContinuationString(delegate); - if(contString != null && !contString.isEmpty()) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",contString); - } else if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",p0.getClass().getName()); - } - Token t = Utils.getToken(getContext()); - if(t != null) { - t.link(); - } - if(delegate != null) { - delegate.resumeWith(p0); - } - } - -} diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRCoroutineToken.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRCoroutineToken.java deleted file mode 100644 index 8284d6a..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRCoroutineToken.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_14; - -import com.newrelic.api.agent.Token; - -import kotlin.coroutines.AbstractCoroutineContextElement; -import kotlin.coroutines.CoroutineContext; - -public class NRCoroutineToken extends AbstractCoroutineContextElement - { - public static Key key = new Key(); - - public NRCoroutineToken(Token t) { - super(key); - token = t; - } - - private Token token = null; - - public static final class Key implements CoroutineContext.Key { - private Key() {} - } - - public Token getToken() { - return token; - } - - @Override - public int hashCode() { - return token.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(this != obj ) { - if(obj instanceof NRCoroutineToken) { - NRCoroutineToken t = (NRCoroutineToken)obj; - return t.token == token; - } - } else { - return true; - } - return false; - } - - @Override - public String toString() { - return "NRCoroutineToken"; - } - - -} diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction2Wrapper.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction2Wrapper.java deleted file mode 100644 index 875875e..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction2Wrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_14; - -import com.newrelic.agent.bridge.AgentBridge; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function2; - -public class NRFunction2Wrapper implements Function2 { - - private Function2 delegate = null; - private static boolean isTransformed = false; - - public NRFunction2Wrapper(Function2 d) { - delegate = d; - if(!isTransformed) { - isTransformed = true; - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public R invoke(P1 p1, P2 p2) { - - if(p2 instanceof Continuation && !(p2 instanceof SuspendFunction)) { - // wrap if needed - String cont_string = Utils.getContinuationString((Continuation)p2); - if(!(p2 instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(cont_string)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p2, cont_string); - p2 = (P2) wrapper; - } - } - if(delegate != null) { - return delegate.invoke(p1, p2); - } - return null; - } - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/DispatchedTaskIgnores.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/DispatchedTaskIgnores.java similarity index 56% rename from Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/DispatchedTaskIgnores.java rename to Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/DispatchedTaskIgnores.java index dc1b680..3fafbce 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/DispatchedTaskIgnores.java +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/DispatchedTaskIgnores.java @@ -1,29 +1,37 @@ -package com.newrelic.instrumentation.kotlin.coroutines_15; +package com.newrelic.instrumentation.kotlin.coroutines_17; + +import com.newrelic.api.agent.Config; +import com.newrelic.api.agent.NewRelic; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.logging.Level; -import com.newrelic.api.agent.Config; -import com.newrelic.api.agent.NewRelic; - +/* +* Used to ignore Dispatched Tasks that the user has configured to ignore +*/ public class DispatchedTaskIgnores { - private static List ignoredTasks = new ArrayList<>(); - private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; + private static final List ignoredTasks = new ArrayList<>(); + private static final String DISPATCHED_IGNORE_CONFIG = "Coroutines.ignores.dispatched"; static { Config config = NewRelic.getAgent().getConfig(); - String ignores = config.getValue(DISPATCHEDIGNORECONFIG); + String ignores = config.getValue(DISPATCHED_IGNORE_CONFIG); configure(ignores); } - public static boolean ignoreDispatchedTask(String contString) { - return ignoredTasks.contains(contString); + public static boolean ignoreDispatchedTask(String dispatchedTask) { + return ignoredTasks.contains(dispatchedTask); } - + + public static void addIgnoredTasks(Collection toIgnore) { + ignoredTasks.addAll(toIgnore); + } + public static void addIgnore(String ignore) { if(!ignoredTasks.contains(ignore)) { ignoredTasks.add(ignore); diff --git a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRContinuationWrapper.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java similarity index 81% rename from Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRContinuationWrapper.java rename to Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java index 3d3bf72..ab38e34 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRContinuationWrapper.java +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java @@ -1,19 +1,23 @@ -package com.newrelic.instrumentation.kotlin.coroutines_15; +package com.newrelic.instrumentation.kotlin.coroutines_17; import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; - import kotlin.coroutines.Continuation; import kotlin.coroutines.CoroutineContext; +import org.jetbrains.annotations.NotNull; +/* +* Used to wrap a Continuation instance. Necessary to control which Continuations +* are tracked. Tracking all Continuations can effect performance. +* */ public class NRContinuationWrapper implements Continuation { - + private Continuation delegate = null; private String name = null; private static boolean isTransformed = false; - + public NRContinuationWrapper(Continuation d, String n) { delegate = d; name = n; @@ -22,7 +26,8 @@ public NRContinuationWrapper(Continuation d, String n) { isTransformed = true; } } - + + @NotNull @Override public CoroutineContext getContext() { return delegate.getContext(); @@ -30,7 +35,7 @@ public CoroutineContext getContext() { @Override @Trace(async=true) - public void resumeWith(Object p0) { + public void resumeWith(@NotNull Object p0) { String contString = Utils.getContinuationString(delegate); if(contString != null && !contString.isEmpty()) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",contString); diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayCancellableContinuation.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayCancellableContinuation.java new file mode 100644 index 0000000..6b60a06 --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayCancellableContinuation.java @@ -0,0 +1,146 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import kotlin.Unit; +import kotlin.coroutines.CoroutineContext; +import kotlin.jvm.functions.Function1; +import kotlinx.coroutines.CancellableContinuation; +import kotlinx.coroutines.CoroutineDispatcher; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/* + * Used to wrap the tracking of a call to the Coroutine delay function if tracking is enabled + * Will report the delay as a segment + */ +public class NRDelayCancellableContinuation implements CancellableContinuation { + + private final CancellableContinuation delegate; + private Segment segment; + + public NRDelayCancellableContinuation(CancellableContinuation delegate, String type) { + this.delegate = delegate; + String name = Utils.getContinuationString(delegate); + segment = NewRelic.getAgent().getTransaction().startSegment(type); + segment.addCustomAttribute("CancellableContinuation", name); + } + + @Override + public void resumeWith(@NotNull Object o) { + if(segment != null) { + segment.end(); + segment = null; + } + if(delegate != null) { + delegate.resumeWith(o); + } + } + + @Override + public @NotNull CoroutineContext getContext() { + return delegate.getContext(); + } + + @Override + public boolean isActive() { + return delegate.isActive(); + } + + @Override + public boolean isCompleted() { + return delegate.isCompleted(); + } + + @Override + public boolean isCancelled() { + return delegate.isCancelled(); + } + + @Override + public @Nullable Object tryResume(T t, @Nullable Object o) { + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.tryResume(t,o); + } + + @Override + public @Nullable Object tryResume(T t, @Nullable Object o, @Nullable Function1 function1) { + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.tryResume(t,o, function1); + } + + @Override + public @Nullable Object tryResumeWithException(@NotNull Throwable throwable) { + NewRelic.noticeError(throwable); + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.tryResumeWithException(throwable); + } + + @Override + public void completeResume(@NotNull Object o) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.completeResume(o); + } + + @Override + public void initCancellability() { + delegate.initCancellability(); + } + + @Override + public boolean cancel(@Nullable Throwable throwable) { + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.cancel(throwable); + } + + @Override + public void invokeOnCancellation(@NotNull Function1 function1) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.invokeOnCancellation(function1); + } + + @Override + public void resumeUndispatched(@NotNull CoroutineDispatcher coroutineDispatcher, T t) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resumeUndispatched(coroutineDispatcher, t); + } + + @Override + public void resumeUndispatchedWithException(@NotNull CoroutineDispatcher coroutineDispatcher, @NotNull Throwable throwable) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resumeUndispatchedWithException(coroutineDispatcher, throwable); + } + + @Override + public void resume(T t, @Nullable Function1 function1) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resume(t,function1); + } +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayContinuation.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayContinuation.java new file mode 100644 index 0000000..ad0ad39 --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayContinuation.java @@ -0,0 +1,38 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; +import kotlinx.coroutines.CancellableContinuation; +import org.jetbrains.annotations.NotNull; + +public class NRDelayContinuation implements Continuation { + + private final Continuation delegate; + private final Segment segment; + + public NRDelayContinuation(Continuation delegate, String type) { + this.delegate = delegate; + String name = Utils.getContinuationString(delegate); + segment = NewRelic.getAgent().getTransaction().startSegment(type); + segment.addCustomAttribute("Continuation", name); + boolean isCancellable = delegate instanceof CancellableContinuation; + segment.addCustomAttribute("isCancellable", isCancellable); + } + + @Override + public void resumeWith(@NotNull Object o) { + if(segment != null) { + segment.end(); + } + if(delegate != null) { + delegate.resumeWith(o); + } + } + + @Override + public @NotNull CoroutineContext getContext() { + return delegate.getContext(); + } +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java new file mode 100644 index 0000000..480c33e --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java @@ -0,0 +1,41 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import kotlin.coroutines.Continuation; +import kotlin.jvm.functions.Function1; + +/* +* Used to wrap a suspend function that was used as an input as a parameter to the +* Coroutine functions runBlocking, async, invoke, launch and withContext +*/ +public class NRFunction1SuspendWrapper implements Function1 { + + private Function1 delegate = null; + private static boolean isTransformed = false; + + public NRFunction1SuspendWrapper(Function1 d) { + delegate = d; + if(!isTransformed) { + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + isTransformed = true; + } + } + + @Override + public R invoke(P1 p1) { + if(p1 instanceof Continuation) { + Continuation cont = (Continuation)p1; + + String cont_string = Utils.getContinuationString(cont); + if(cont_string != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",cont_string); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction","UnknownSource"); + + } + } + return delegate != null ? delegate.invoke(p1) : null; + } + +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction1Wrapper.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1Wrapper.java similarity index 76% rename from Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction1Wrapper.java rename to Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1Wrapper.java index fab3048..d0232a6 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction1Wrapper.java +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1Wrapper.java @@ -1,7 +1,6 @@ -package com.newrelic.instrumentation.kotlin.coroutines_14; +package com.newrelic.instrumentation.kotlin.coroutines_17; import com.newrelic.agent.bridge.AgentBridge; - import kotlin.coroutines.Continuation; import kotlin.coroutines.jvm.internal.SuspendFunction; import kotlin.jvm.functions.Function1; @@ -24,9 +23,9 @@ public NRFunction1Wrapper(Function1 d) { public R invoke(P1 p1) { if(p1 instanceof Continuation && !(p1 instanceof SuspendFunction)) { // wrap if needed - if(!(p1 instanceof NRContinuationWrapper)) { - String cont_string = Utils.getContinuationString((Continuation)p1); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p1, cont_string); + Continuation continuation = (Continuation) p1; + if(!(p1 instanceof NRContinuationWrapper) && Utils.continueWithContinuation(continuation)) { + NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation) p1, Utils.getContinuationString(continuation)); p1 = (P1) wrapper; } } diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java new file mode 100644 index 0000000..8130e00 --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java @@ -0,0 +1,66 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; +import kotlin.jvm.functions.Function2; +import kotlinx.coroutines.CoroutineScope; + +/* + * Used to wrap a suspend function that was used as an input as a parameter to the + * Coroutine functions runBlocking, async, invoke, launch and withContext + */ +public class NRFunction2SuspendWrapper implements Function2 { + + private Function2 delegate = null; + private static boolean isTransformed = false; + + public NRFunction2SuspendWrapper(Function2 d) { + delegate = d; + if(!isTransformed) { + isTransformed = true; + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + } + } + + @Override + public R invoke(S s, T t) { + + // set name + boolean name_set = false; + if(s instanceof CoroutineScope) { + CoroutineScope scope = (CoroutineScope)s; + CoroutineContext ctx = scope.getCoroutineContext(); + Token token = Utils.getToken(ctx); + if(token != null) { + token.link(); + } + CoroutineContext context = scope.getCoroutineContext(); + String coroutineName = Utils.getCoroutineName(context); + if(coroutineName != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",coroutineName); + name_set = true; + } + + } + + if(!name_set && t instanceof Continuation) { + Continuation cont = (Continuation)t; + + String cont_string = Utils.getContinuationString(cont); + if(cont_string != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",cont_string); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction","UnknownSource"); + + } + } + if(delegate != null) { + return delegate.invoke(s, t); + } + return null; + } + +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRRunnable.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRRunnable.java similarity index 75% rename from Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRRunnable.java rename to Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRRunnable.java index 36cf40b..5eee99b 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRRunnable.java +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRRunnable.java @@ -1,10 +1,9 @@ -package com.newrelic.instrumentation.kotlin.coroutines_14; +package com.newrelic.instrumentation.kotlin.coroutines_17; import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; - import kotlin.coroutines.Continuation; import kotlinx.coroutines.DispatchedTask; @@ -30,13 +29,11 @@ public void run() { if(delegate != null && delegate instanceof DispatchedTask) { DispatchedTask task = (DispatchedTask)delegate; Continuation cont_delegate = task.getDelegate$kotlinx_coroutines_core(); - if(cont_delegate != null) { - String cont_string = Utils.getContinuationString(cont_delegate); - if(cont_string == null) cont_string = cont_delegate.getClass().getName(); - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","DispatchedTask",Utils.getContinuationString(cont_delegate)); - nameSet = true; - } - } + String cont_string = Utils.getContinuationString(cont_delegate); + if(cont_string == null) cont_string = cont_delegate.getClass().getName(); + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","DispatchedTask",cont_string); + nameSet = true; + } if(!nameSet) { String delegateType = delegate != null ? delegate.getClass().getName() : "null"; NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AsyncRunnableWrapper",delegateType); diff --git a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java similarity index 76% rename from Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java rename to Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java index 52945da..5e2de63 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java +++ b/Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java @@ -1,4 +1,4 @@ -package com.newrelic.instrumentation.kotlin.coroutines_14; +package com.newrelic.instrumentation.kotlin.coroutines_17; import java.util.ArrayList; import java.util.List; @@ -15,7 +15,6 @@ import kotlin.coroutines.CoroutineContext; import kotlin.coroutines.jvm.internal.BaseContinuationImpl; import kotlinx.coroutines.AbstractCoroutine; -import kotlinx.coroutines.CoroutineName; import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.DispatchedTask; @@ -27,7 +26,7 @@ public class Utils implements AgentConfigListener { private static final String SCOPESIGNORECONFIG = "Coroutines.ignores.scopes"; private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; private static final String DELAYED_ENABLED_CONFIG = "Coroutines.delayed.enabled"; - + public static final String CREATEMETHOD1 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4"; public static final String CREATEMETHOD2 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3"; private static final Utils INSTANCE = new Utils(); @@ -48,9 +47,9 @@ public class Utils implements AgentConfigListener { DELAYED_ENABLED = Boolean.valueOf(value.toString()); } } - + } - + public static NRRunnable getRunnableWrapper(Runnable r) { if(r instanceof NRRunnable) { return null; @@ -65,7 +64,7 @@ public static NRRunnable getRunnableWrapper(Runnable r) { } } } - + Token t = NewRelic.getAgent().getTransaction().getToken(); if(t != null && t.isActive()) { return new NRRunnable(r, t); @@ -75,14 +74,14 @@ public static NRRunnable getRunnableWrapper(Runnable r) { } return null; } - + private static void loadConfig(Config config) { String ignores = config.getValue(CONTIGNORECONFIG); NewRelic.getAgent().getLogger().log(Level.FINE, "Value of {0}: {1}", CONTIGNORECONFIG, ignores); if (ignores != null && !ignores.isEmpty()) { ignoredContinuations.clear(); String[] ignoresList = ignores.split(","); - + for(String ignore : ignoresList) { if (!ignoredContinuations.contains(ignore)) { ignoredContinuations.add(ignore); @@ -102,7 +101,7 @@ private static void loadConfig(Config config) { if (ignores != null && !ignores.isEmpty()) { ignoredScopes.clear(); String[] ignoresList = ignores.split(","); - + for(String ignore : ignoresList) { if (!ignoredScopes.contains(ignore)) { ignoredScopes.add(ignore); @@ -112,61 +111,103 @@ private static void loadConfig(Config config) { } else if(!ignoredScopes.isEmpty()) { ignoredScopes.clear(); } - + } - + public static boolean ignoreScope(CoroutineScope scope) { CoroutineContext ctx = scope.getCoroutineContext(); String name = getCoroutineName(ctx); String className = scope.getClass().getName(); return ignoreScope(className) || ignoreScope(name); } - + public static boolean ignoreScope(String coroutineScope) { return ignoredScopes.contains(coroutineScope); } - + public static boolean ignoreContinuation(String cont_string) { return ignoredContinuations.contains(cont_string); } - + + /* + * Allows certain Coroutine scopes to be ignored + * coroutineScope can be a Coroutine name or CoroutineScope class name + */ + public static boolean continueWithScope(CoroutineScope scope) { + CoroutineContext ctx = scope.getCoroutineContext(); + String name = getCoroutineName(ctx); + String className = scope.getClass().getName(); + return continueWithScope(className) && continueWithScope(name); + } + + /* + * Allows certain Coroutine scopes to be ignored + * coroutineScope can be a Coroutine name or CoroutineScope class name + */ + public static boolean continueWithScope(String coroutineScope) { + for(String ignoredScope : ignoredScopes) { + if(coroutineScope.matches(ignoredScope)) { + return false; + } + } + return !ignoredScopes.contains(coroutineScope); + } + + public static boolean continueWithContinuation(Continuation continuation) { + /* + * Don't trace internal Coroutines Continuations + */ + String className = continuation.getClass().getName(); + if(className.startsWith("kotlin")) return false; + + /* + * Get the continuation string and check if it should be ignored + */ + String cont_string = getContinuationString(continuation); + if(cont_string == null) { return false; } + + for(String ignored : ignoredContinuations) { + if(cont_string.matches(ignored)) { + return false; + } + } + return !ignoredContinuations.contains(cont_string); + } + + + public static String sub = "createCoroutineFromSuspendFunction"; - public static NRCoroutineToken setToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken == null) { + public static void setToken(CoroutineContext context) { + TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); + if (tokenContext == null) { Token t = NewRelic.getAgent().getTransaction().getToken(); if(t != null && t.isActive()) { - coroutineToken = new NRCoroutineToken(t); - return coroutineToken; + NRTokenContextKt.addTokenContext(context, t); } else if(t != null) { t.expire(); t = null; } } - return null; } public static Token getToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - Token token = null; - if(coroutineToken != null) { - token = coroutineToken.getToken(); + TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); + if(tokenContext != null) { + return tokenContext.getToken(); } - - return token; + return null; } public static void expireToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken != null) { - Token token = coroutineToken.getToken(); + TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); + if(tokenContext != null) { + Token token = tokenContext.getToken(); token.expire(); - context.minusKey(NRCoroutineToken.key); + NRTokenContextKt.removeTokenContext(context); } } - - @SuppressWarnings("unchecked") + public static String getCoroutineName(CoroutineContext context, Continuation continuation) { if(continuation instanceof AbstractCoroutine) { return ((AbstractCoroutine)continuation).nameString$kotlinx_coroutines_core(); @@ -178,13 +219,7 @@ public static String getCoroutineName(CoroutineContext context, Continuation } public static String getCoroutineName(CoroutineContext context) { - CoroutineName cName = context.get(CoroutineName.Key); - if(cName != null) { - String name = cName.getName(); - if(name != null && !name.isEmpty()) return name; - } - - return null; + return CoroutineNameUtilsKt.getCoroutineName(context); } @Override @@ -199,27 +234,27 @@ public void configChanged(String appName, AgentConfig agentConfig) { } } } - + public static String getContinuationString(Continuation continuation) { String contString = continuation.toString(); - + if(contString.equals(CREATEMETHOD1) || contString.equals(CREATEMETHOD2)) { return sub; } - + if(contString.startsWith(CONT_LOC)) { return contString; } - + if(continuation instanceof AbstractCoroutine) { return ((AbstractCoroutine)continuation).nameString$kotlinx_coroutines_core(); } - + int index = contString.indexOf('@'); if(index > -1) { return contString.substring(0, index); } - + return null; } -} \ No newline at end of file +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlin/coroutines/ContinuationKt.java b/Kotlin-Coroutines_1.4/src/main/java/kotlin/coroutines/ContinuationKt.java deleted file mode 100644 index 7a4f41f..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlin/coroutines/ContinuationKt.java +++ /dev/null @@ -1,34 +0,0 @@ -package kotlin.coroutines; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRFunction2Wrapper; - -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; - -@Weave -public abstract class ContinuationKt { - - @Trace - public static void startCoroutine(Function1, ? extends Object> f, Continuation cont ) { - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper, ? extends Object> wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @Trace - public static void startCoroutine(Function2, ? extends Object> f, R receiver, Continuation cont) { - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - -} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java b/Kotlin-Coroutines_1.4/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java new file mode 100644 index 0000000..300c483 --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java @@ -0,0 +1,32 @@ +package kotlin.coroutines; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; + +import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function2; + +@Weave(originalName = "kotlin.coroutines.ContinuationKt") +public abstract class ContinuationKt_Instrumentation { + + @Trace + public static void startCoroutine(Function1, ?> f, Continuation cont ) { + if(!(f instanceof NRFunction1SuspendWrapper)) { + f = new NRFunction1SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + @Trace + public static void startCoroutine(Function2, ?> f, R receiver, Continuation cont) { + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine.java deleted file mode 100644 index ef4f781..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine.java +++ /dev/null @@ -1,83 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.TracedMethod; -import com.newrelic.api.agent.TransactionNamePriority; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.Utils; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; - -@Weave(type=MatchType.BaseClass) -public abstract class AbstractCoroutine { - - public abstract CoroutineContext getContext(); - public abstract String nameString$kotlinx_coroutines_core(); - - protected void onCompleted(T value) { - Utils.expireToken(getContext()); - Weaver.callOriginal(); - } - - protected void onCancelled(Throwable t, boolean b) { - Utils.expireToken(getContext()); - Weaver.callOriginal(); - } - - public void handleOnCompletionException$kotlinx_coroutines_core(Throwable t) { - //Utils.expireToken(getContext()); - Weaver.callOriginal(); - } - - @Trace - public void start(CoroutineStart start, Function1, ? extends Object> block) { - if(!(block instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper, ? extends Object> wrapper = new NRFunction1Wrapper<>(block); - block = wrapper; - } - String ctxName = Utils.getCoroutineName(getContext()); - String name = ctxName != null ? ctxName : nameString$kotlinx_coroutines_core(); - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Coroutine-Name", name); - traced.addCustomAttribute("Block", block.toString()); - if(name != null && !name.isEmpty()) { - NewRelic.getAgent().getTransaction().setTransactionName(TransactionNamePriority.FRAMEWORK_HIGH, false, "Coroutine", "Kotlin","Coroutine",name); - traced.setMetricName("Custom","Kotlin","Coroutines",getClass().getSimpleName(),"start",name); - } else { - NewRelic.getAgent().getTransaction().setTransactionName(TransactionNamePriority.FRAMEWORK_HIGH, false, "Coroutine", "Kotlin","Coroutine"); - } - - Weaver.callOriginal(); - } - - @Trace - public void start(CoroutineStart start, R receiver, Function2, ? extends Object> block) { - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper<>(block); - block = wrapper; - } - String ctxName = Utils.getCoroutineName(getContext()); - String name = ctxName != null ? ctxName : nameString$kotlinx_coroutines_core(); - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Coroutine-Name", name); - traced.addCustomAttribute("Block", block.toString()); - - if(name != null && !name.isEmpty()) { - NewRelic.getAgent().getTransaction().setTransactionName(TransactionNamePriority.FRAMEWORK_HIGH, false, "Coroutine", "Kotlin","Coroutine",name); - traced.setMetricName("Custom","Kotlin","Coroutines",getClass().getSimpleName(),"start",name); - } else { - NewRelic.getAgent().getTransaction().setTransactionName(TransactionNamePriority.FRAMEWORK_HIGH, false, "Coroutine", "Kotlin","Coroutine"); - } - - Weaver.callOriginal(); - } - -} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java similarity index 84% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java index a600fe7..a83b9b4 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java @@ -7,15 +7,15 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2Wrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; import kotlin.coroutines.Continuation; import kotlin.coroutines.CoroutineContext; import kotlin.jvm.functions.Function2; -@Weave(type=MatchType.BaseClass) -public abstract class AbstractCoroutine { +@Weave(type=MatchType.BaseClass, originalName = "kotlinx.coroutines.AbstractCoroutine") +public abstract class AbstractCoroutine_Instrumentation { public abstract CoroutineContext getContext(); public abstract String nameString$kotlinx_coroutines_core(); @@ -35,10 +35,9 @@ protected void onCancelled(Throwable t, boolean b) { } @Trace - public void start(CoroutineStart start, R receiver, Function2, ? extends Object> block) { - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper<>(block); - block = wrapper; + public void start(CoroutineStart start, R receiver, Function2, ?> block) { + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = new NRFunction2SuspendWrapper<>(block); } String ctxName = Utils.getCoroutineName(getContext()); String name = ctxName != null ? ctxName : nameString$kotlinx_coroutines_core(); diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AwaitAll.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java similarity index 76% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AwaitAll.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java index 81064be..9ef878e 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AwaitAll.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java @@ -9,14 +9,13 @@ import kotlin.coroutines.Continuation; -@Weave -abstract class AwaitAll { +@Weave(originalName = "kotlinx.coroutines.AwaitAll") +abstract class AwaitAll_Instrumentation { @Trace public Object await(Continuation> cont) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AwaitAll","await"); - Object result = Weaver.callOriginal(); - return result; + return Weaver.callOriginal(); } } diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java deleted file mode 100644 index 0c060d1..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt.java +++ /dev/null @@ -1,175 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRCoroutineToken; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.Utils; - -import kotlin.Unit; -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function2; - -@SuppressWarnings({ "unchecked", "rawtypes" }) -@Weave -public class BuildersKt { - - @Trace(dispatcher = true) - public static final T runBlocking(CoroutineContext context, Function2, ? extends Object> block) { - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - String name = Utils.getCoroutineName(context); - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - - if (!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - T t = Weaver.callOriginal(); - return t; - } - - @Trace(dispatcher = true) - public static final Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if (!Utils.ignoreScope(scope)) { - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); - } - - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - } else { - NewRelic.getAgent().getTransaction().ignore(); - } - return Weaver.callOriginal(); - } - - @Trace(dispatcher = true) - public static final Object invoke(CoroutineDispatcher dispatcher, Function2, ? extends Object> block, Continuation c) { - - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", c.toString()); - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - if(c != null && !Utils.ignoreContinuation(c.toString())) { - boolean isSuspend = c instanceof SuspendFunction; - if(!isSuspend) { - String cont_string = Utils.getContinuationString(c); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(c, cont_string); - c = wrapper; - } - } - Object t = Weaver.callOriginal(); - return t; - } - - @Trace(dispatcher = true) - public static final kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if (!Utils.ignoreScope(scope)) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); - - String name = Utils.getCoroutineName(context); - if (name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if (name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if (!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper( - block); - block = wrapper; - } - } else { - NewRelic.getAgent().getTransaction().ignore(); - } - Job j = Weaver.callOriginal(); - return j; - } - - @Trace(dispatcher = true) - public static final Object withContext(CoroutineContext context,Function2, ? extends Object> block, Continuation completion) { - String name = Utils.getCoroutineName(context,completion); - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext"); - } - if(completion != null) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Completion", completion.toString()); - } - - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - if(completion != null && !Utils.ignoreContinuation(completion.toString())) { - if(!(completion instanceof NRContinuationWrapper)) { - String cont_string = Utils.getContinuationString(completion); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(completion, cont_string); - completion = wrapper; - } - } - return Weaver.callOriginal(); - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java new file mode 100644 index 0000000..6a97c4c --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java @@ -0,0 +1,138 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRContinuationWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; + +import kotlin.Unit; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; +import kotlin.coroutines.jvm.internal.SuspendFunction; +import kotlin.jvm.functions.Function2; + +@SuppressWarnings({ "unchecked", "rawtypes" }) +@Weave(originalName = "kotlinx.coroutines.BuildersKt") +public class BuildersKt_Instrumentation { + + @Trace(dispatcher = true) + public static T runBlocking(CoroutineContext context, Function2, ?> block) { + String name = Utils.getCoroutineName(context); + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking",name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking"); + } + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); + + if (!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, + Function2, ?> block) { + if (Utils.continueWithScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-CoroutineName", Utils.getCoroutineName(scope.getCoroutineContext())); + + String name = Utils.getCoroutineName(context); + if(name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); + } + + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + } else { + NewRelic.getAgent().getTransaction().ignore(); + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static Object invoke(CoroutineDispatcher_Instrumentation dispatcher, + Function2, ?> block, Continuation cont) { + + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", cont.toString()); + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + if(Utils.continueWithContinuation(cont)) { + boolean isSuspend = cont instanceof SuspendFunction; + if(!isSuspend) { + String cont_string = Utils.getContinuationString(cont); + cont = new NRContinuationWrapper<>(cont, cont_string); + } + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, + Function2, ?> block) { + if (Utils.continueWithScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-CoroutineName", Utils.getCoroutineName(scope.getCoroutineContext())); + + String name = Utils.getCoroutineName(context); + if (name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if (name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); + } + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); + if (!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper( + block); + } + } else { + NewRelic.getAgent().getTransaction().ignore(); + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static Object withContext(CoroutineContext context, Function2, ?> block, + Continuation completion) { + String name = Utils.getCoroutineName(context,completion); + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext",name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext"); + } + if(completion != null) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Completion", completion.toString()); + } + + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + if(completion != null && Utils.continueWithContinuation(completion)) { + if(!(completion instanceof NRContinuationWrapper)) { + String cont_string = Utils.getContinuationString(completion); + completion = new NRContinuationWrapper<>(completion, cont_string); + } + } + return Weaver.callOriginal(); + } +} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java deleted file mode 100644 index 6912bef..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java +++ /dev/null @@ -1,22 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRRunnable; -import com.newrelic.instrumentation.kotlin.coroutines_14.Utils; - -import kotlin.coroutines.CoroutineContext; - -@Weave(type = MatchType.BaseClass) -public abstract class CoroutineDispatcher { - - public void dispatch(CoroutineContext ctx, Runnable r) { - NRRunnable wrapper = Utils.getRunnableWrapper(r); - if(wrapper != null) { - r = wrapper; - } - - Weaver.callOriginal(); - } -} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineDispatcher_Instrumentation.java similarity index 64% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineDispatcher_Instrumentation.java index a28b871..14b5365 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineDispatcher_Instrumentation.java @@ -8,8 +8,12 @@ import kotlin.coroutines.CoroutineContext; -@Weave(type = MatchType.BaseClass) -public abstract class CoroutineDispatcher { +/* + * Dispatchers are used to dispatch tasks to another thread. By wrapping the Runable + * we can track the Coroutine across threads + */ +@Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.CoroutineDispatcher") +public abstract class CoroutineDispatcher_Instrumentation { public void dispatch(CoroutineContext ctx, Runnable r) { NRRunnable wrapper = Utils.getRunnableWrapper(r); diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java deleted file mode 100644 index 97fc24b..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java +++ /dev/null @@ -1,14 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave -public abstract class CoroutineExceptionHandlerKt { - - @Trace - public static void handleCoroutineException(kotlin.coroutines.CoroutineContext ctx,Throwable t) { - Weaver.callOriginal(); - } -} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt_Instrumentation.java similarity index 67% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt_Instrumentation.java index 97fc24b..393f625 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt_Instrumentation.java @@ -4,8 +4,8 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -@Weave -public abstract class CoroutineExceptionHandlerKt { +@Weave(originalName = "kotlinx.coroutines.CoroutineExceptionHandlerKt") +public abstract class CoroutineExceptionHandlerKt_Instrumentation { @Trace public static void handleCoroutineException(kotlin.coroutines.CoroutineContext ctx,Throwable t) { diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler_Instrumentation.java similarity index 71% rename from Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler_Instrumentation.java index 6419a85..9aa2936 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler_Instrumentation.java @@ -6,8 +6,8 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -@Weave(type = MatchType.Interface) -public abstract class CoroutineExceptionHandler { +@Weave(type = MatchType.Interface, originalName = "kotlinx.coroutines.CoroutineExceptionHandler") +public abstract class CoroutineExceptionHandler_Instrumentation { @Trace public void handleException(kotlin.coroutines.CoroutineContext ctx, java.lang.Throwable t) { diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/DelayKt_Instrumentation.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/DelayKt_Instrumentation.java new file mode 100644 index 0000000..4a387d1 --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/DelayKt_Instrumentation.java @@ -0,0 +1,30 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRDelayContinuation; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; +import kotlin.Unit; +import kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.DelayKt") +public class DelayKt_Instrumentation { + + @Trace + public static Object awaitCancellation(Continuation cont) { + if(Utils.DELAYED_ENABLED && !(cont instanceof NRDelayContinuation)) { + cont = new NRDelayContinuation<>(cont,"AwaitCancellation"); + } + return Weaver.callOriginal(); + } + + @Trace + public static Object delay(long timeMillis, Continuation cont) { + if(Utils.DELAYED_ENABLED && !(cont instanceof NRDelayContinuation)) { + cont = new NRDelayContinuation<>(cont,"Delay"); + } + return Weaver.callOriginal(); + } + +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/Delay_Instrumentation.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/Delay_Instrumentation.java new file mode 100644 index 0000000..aeee82e --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/Delay_Instrumentation.java @@ -0,0 +1,37 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRDelayCancellableContinuation; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRDelayContinuation; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; +import kotlin.Unit; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; + +@Weave(type = MatchType.Interface, originalName = "kotlinx.coroutines.Delay") +public class Delay_Instrumentation { + + @Trace + public Object delay(long timeMills, Continuation continuation) { + if(Utils.DELAYED_ENABLED && !(continuation instanceof NRDelayContinuation)) { + continuation = new NRDelayContinuation<>(continuation,"Delay"); + } + return Weaver.callOriginal(); + } + + @Trace + public void scheduleResumeAfterDelay(long timeMills, CancellableContinuation continuation) { + if(Utils.DELAYED_ENABLED && !(continuation instanceof NRDelayContinuation)) { + continuation = new NRDelayCancellableContinuation<>(continuation,"scheduleResumeAfterDelay"); + } + Weaver.callOriginal(); + } + + @Trace + public DisposableHandle invokeOnTimeout(long timeMills, Runnable r, CoroutineContext context) { + return Weaver.callOriginal(); + } +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/DispatcherExecutor.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/DispatcherExecutor.java deleted file mode 100644 index d2b139c..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/DispatcherExecutor.java +++ /dev/null @@ -1,21 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRRunnable; -import com.newrelic.instrumentation.kotlin.coroutines_14.Utils; - -@Weave(type = MatchType.BaseClass) -abstract class DispatcherExecutor { - - public void execute(Runnable r) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","DispatcherExecutor","execute"); - NRRunnable wrapper = Utils.getRunnableWrapper(r); - if(wrapper != null) { - r = wrapper; - } - Weaver.callOriginal(); - } -} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/DispatcherExecutor_Instrumentation.java similarity index 82% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/DispatcherExecutor_Instrumentation.java index 034b646..eb823ba 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/DispatcherExecutor_Instrumentation.java @@ -8,8 +8,8 @@ import com.newrelic.instrumentation.kotlin.coroutines_17.NRRunnable; import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; -@Weave(type = MatchType.BaseClass) -abstract class DispatcherExecutor { +@Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.DispatcherExecutor") +abstract class DispatcherExecutor_Instrumentation { @Trace public void execute(Runnable r) { diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase.java deleted file mode 100644 index d92eeb1..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase.java +++ /dev/null @@ -1,80 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_14.Utils; - -@Weave -public abstract class EventLoopImplBase { - - @Weave(type = MatchType.BaseClass) - public static abstract class DelayedTask { - - public DelayedTask(long nanos) { - if(Utils.DELAYED_ENABLED) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public long nanoTime = Weaver.callOriginal(); - - @NewField - protected Token token = null; - - public void dispose() { - if(token != null) { - token.expire(); - token = null; - } - Weaver.callOriginal(); - } - } - - @Weave - static class DelayedResumeTask extends DelayedTask { - - - public DelayedResumeTask(long nanos, kotlinx.coroutines.CancellableContinuation cont) { - super(nanos); - } - - @Trace(async = true) - public void run() { - if(token != null) { - token.linkAndExpire(); - token = null; - } - - NewRelic.recordResponseTimeMetric("Custom/Kotlin/DelayedResumeTask", nanoTime); - Weaver.callOriginal(); - } - } - - @Weave - static class DelayedRunnableTask extends DelayedTask { - - public DelayedRunnableTask(long nanos, Runnable r) { - super(nanos); - } - - @Trace(async = true) - public void run() { - if(token != null) { - token.linkAndExpire(); - token = null; - } - NewRelic.recordResponseTimeMetric("Custom/Kotlin/DelayedResumeTask", nanoTime); - Weaver.callOriginal(); - } - } - - @Weave - public static class DelayedTaskQueue { - - } -} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java similarity index 55% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java index 3baf1dc..95033d1 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java @@ -9,13 +9,13 @@ import com.newrelic.api.agent.weaver.Weaver; import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; -@Weave -public abstract class EventLoopImplBase { +@Weave(originalName = "kotlinx.coroutines.EventLoopImplBase") +public abstract class EventLoopImplBase_Instrumentation { - @Weave(type = MatchType.BaseClass) - public static abstract class DelayedTask { + @Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedTask") + public static abstract class DelayedTask_Instrumentation { - public DelayedTask(long nanos) { + public DelayedTask_Instrumentation(long nanos) { if(Utils.DELAYED_ENABLED) { token = NewRelic.getAgent().getTransaction().getToken(); } @@ -35,11 +35,11 @@ public void dispose() { } } - @Weave - static class DelayedResumeTask extends DelayedTask { + @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask") + static class DelayedResumeTask_Instrumentation extends DelayedTask_Instrumentation { - public DelayedResumeTask(long nanos, kotlinx.coroutines.CancellableContinuation cont) { + public DelayedResumeTask_Instrumentation(long nanos, kotlinx.coroutines.CancellableContinuation cont) { super(nanos); } @@ -55,10 +55,10 @@ public void run() { } } - @Weave - static class DelayedRunnableTask extends DelayedTask { + @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask") + static class DelayedRunnableTask_Instrumentation extends DelayedTask_Instrumentation { - public DelayedRunnableTask(long nanos, Runnable r) { + public DelayedRunnableTask_Instrumentation(long nanos, Runnable r) { super(nanos); } @@ -73,8 +73,8 @@ public void run() { } } - @Weave - public static class DelayedTaskQueue { + @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedTaskQueue") + public static class DelayedTaskQueue_Instrumentation { } } diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java similarity index 69% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java index 0f9ec41..817a87d 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java @@ -7,13 +7,13 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -@Weave -abstract class ResumeUndispatchedRunnable { +@Weave(originalName = "kotlinx.coroutines.ResumeUndispatchedRunnable") +abstract class ResumeUndispatchedRunnable_Instrumentation { @NewField private Token token = null; - public ResumeUndispatchedRunnable(CoroutineDispatcher dispatcher, CancellableContinuation cont) { + public ResumeUndispatchedRunnable_Instrumentation(CoroutineDispatcher_Instrumentation dispatcher, CancellableContinuation cont) { Token t = NewRelic.getAgent().getTransaction().getToken(); if(t != null && t.isActive()) { token = t; diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/YieldKt.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/YieldKt_Instrumentation.java similarity index 87% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/YieldKt.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/YieldKt_Instrumentation.java index 9f35049..3f8e573 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/YieldKt.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/YieldKt_Instrumentation.java @@ -9,8 +9,8 @@ import kotlin.Unit; import kotlin.coroutines.Continuation; -@Weave -public class YieldKt { +@Weave(originalName = "kotlinx.coroutines.YieldKt") +public class YieldKt_Instrumentation { @Trace public static Object yield(Continuation cont) { diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java new file mode 100644 index 0000000..977c207 --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java @@ -0,0 +1,15 @@ +package kotlinx.coroutines.channels; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +/** + * Included to avoid loading in version 1.7.x + * One difference between 1.5.x and 1.7.x is the class BufferedChannel which was + * introduced in 1.7 + * + * @author dhilpipre + * + */ +@SkipIfPresent(originalName = "kotlinx.coroutines.channels.BufferedChannel") +public class BufferedChannel_Instrumentation { +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java new file mode 100644 index 0000000..51cacb5 --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java @@ -0,0 +1,35 @@ +package kotlinx.coroutines.channels; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +import kotlin.Unit; +import kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.channels.ChannelCoroutine") +public abstract class ChannelCoroutine_Instrumentation { + + @Trace(dispatcher = true) + public Object receive(Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"receive"); + + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public void cancel(java.util.concurrent.CancellationException ex) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"Cancel"); + + Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public Object send(E e, Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"send"); + return Weaver.callOriginal(); + } + + +} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java similarity index 56% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java rename to Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java index aa537cc..ead5823 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java @@ -6,8 +6,9 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.newrelic.instrumentation.kotlin.coroutines_17.NRContinuationWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1SuspendWrapper; import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2Wrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; import kotlin.coroutines.Continuation; @@ -15,39 +16,34 @@ import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; -@Weave -public abstract class CancellableKt { +@Weave(originalName = "kotlinx.coroutines.intrinsics.CancellableKt") +public abstract class CancellableKt_Instrumentation { - @SuppressWarnings({ "unchecked", "rawtypes" }) @Trace - public static void startCoroutineCancellable(Function1, ? extends java.lang.Object> f, Continuation cont) { + public static void startCoroutineCancellable(Function1, ?> f, Continuation cont) { String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; + if(!(cont instanceof SuspendFunction)) { + if(!(cont instanceof NRContinuationWrapper) && Utils.continueWithContinuation(cont)) { + cont = new NRContinuationWrapper<>(cont, continuationString); } } if(continuationString != null) { NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); } NewRelic.getAgent().getTracedMethod().addCustomAttribute("Suspend-Type", "Function1"); - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; + if(!(f instanceof NRFunction1SuspendWrapper)) { + f = new NRFunction1SuspendWrapper<>(f); } Weaver.callOriginal(); } - @SuppressWarnings({ "unchecked", "rawtypes" }) @Trace - public static void startCoroutineCancellable(Function2, ? extends java.lang.Object> f, R receiver, Continuation cont, Function1 onCancellation) { + public static void startCoroutineCancellable(Function2, ?> f, R receiver, Continuation cont, Function1 onCancellation) { String continuationString = Utils.getContinuationString(cont); if(!(cont instanceof SuspendFunction)) { // create continuation wrapper if needed - if(cont != null && !Utils.ignoreContinuation(continuationString) && !(cont instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; + if(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { + cont = new NRContinuationWrapper<>(cont, continuationString); } } if(continuationString != null) { @@ -57,13 +53,11 @@ public static void startCoroutineCancellable(Function2(onCancellation); - onCancellation = wrapper; + onCancellation = new NRFunction1Wrapper<>(onCancellation); } - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends java.lang.Object> wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); } Weaver.callOriginal(); } @@ -71,19 +65,17 @@ public static void startCoroutineCancellable(Function2 completion, Continuation cont) { String completionString = Utils.getContinuationString(completion); - if(completion != null && !(completion instanceof SuspendFunction)) { + if(!(completion instanceof SuspendFunction)) { // create continuation wrapper if needed - if(!Utils.ignoreContinuation(completionString) && !(completion instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(completion, completionString); - completion = wrapper; + if(Utils.continueWithContinuation(completion) && !(completion instanceof NRContinuationWrapper)) { + completion = new NRContinuationWrapper<>(completion, completionString); } } String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { + if(!(cont instanceof SuspendFunction)) { // create continuation wrapper if needed - if(cont != null && !Utils.ignoreContinuation(continuationString) && !(cont instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; + if(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { + cont = new NRContinuationWrapper<>(cont, continuationString); } } TracedMethod traced = NewRelic.getAgent().getTracedMethod(); diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java deleted file mode 100644 index 8c9da69..0000000 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java +++ /dev/null @@ -1,138 +0,0 @@ -package kotlinx.coroutines.intrinsics; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.TracedMethod; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.Utils; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; -import kotlinx.coroutines.internal.ScopeCoroutine; - -@Weave -public class UndispatchedKt { - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUnintercepted(Function1, ? extends Object> f, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function1"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUnintercepted(Function2, ? extends Object> f, R receiver, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUndispatched(Function1, ? extends Object> f, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function1"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUndispatched(Function2, ? extends Object> f, R receiver, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final Object startUndispatchedOrReturn(ScopeCoroutine scope, R receiver, Function2, ? extends Object> f) { - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - return Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutine scope, R receiver, Function2, ? extends Object> f) { - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - return Weaver.callOriginal(); - } - -} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java new file mode 100644 index 0000000..dcd4fd2 --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java @@ -0,0 +1,86 @@ +package kotlinx.coroutines.intrinsics; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.TracedMethod; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRContinuationWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; + +import kotlin.coroutines.Continuation; +import kotlin.coroutines.jvm.internal.SuspendFunction; +import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function2; +import kotlinx.coroutines.internal.ScopeCoroutine; + +@Weave(originalName = "kotlinx.coroutines.intrinsics.UndispatchedKt") +public class UndispatchedKt_Instrumentation { + + @Trace + public static void startCoroutineUnintercepted(Function1, ?> f, Continuation cont) { + String continuationString = Utils.getContinuationString(cont); + if(!(cont instanceof SuspendFunction)) { + if(!(cont instanceof NRContinuationWrapper) && Utils.continueWithContinuation(cont)) { + cont = new NRContinuationWrapper<>(cont, continuationString); + } + } + TracedMethod traced = NewRelic.getAgent().getTracedMethod(); + traced.addCustomAttribute("Suspend-Type", "Function1"); + if(continuationString != null) { + traced.addCustomAttribute("Continuation", continuationString); + } + if(!(f instanceof NRFunction1SuspendWrapper)) { + f = new NRFunction1SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + @Trace + public static void startCoroutineUndispatched(Function2, ?> f, R receiver, + Continuation cont) { + String continuationString = Utils.getContinuationString(cont); + if(!(cont instanceof SuspendFunction)) { + if(!(cont instanceof NRContinuationWrapper) && Utils.continueWithContinuation(cont)) { + cont = new NRContinuationWrapper<>(cont, continuationString); + } + } + TracedMethod traced = NewRelic.getAgent().getTracedMethod(); + traced.addCustomAttribute("Suspend-Type", "Function2"); + if(continuationString != null) { + traced.addCustomAttribute("Continuation", continuationString); + } + traced.addCustomAttribute("Receiver", receiver.getClass().getName()); + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + @Trace + public static Object startUndispatchedOrReturn(ScopeCoroutine scope, R receiver, + Function2, ?> f) { + TracedMethod traced = NewRelic.getAgent().getTracedMethod(); + traced.addCustomAttribute("Suspend-Type", "Function2"); + traced.addCustomAttribute("Receiver", receiver.getClass().getName()); + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); + } + return Weaver.callOriginal(); + } + + @Trace + public static Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutine scope, R receiver, + Function2, ?> f) { + TracedMethod traced = NewRelic.getAgent().getTracedMethod(); + traced.addCustomAttribute("Suspend-Type", "Function2"); + traced.addCustomAttribute("Receiver", receiver.getClass().getName()); + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); + } + return Weaver.callOriginal(); + } + +} diff --git a/Kotlin-Coroutines_1.4/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/CoroutineNameUtils.kt b/Kotlin-Coroutines_1.4/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/CoroutineNameUtils.kt new file mode 100644 index 0000000..355fcd8 --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/CoroutineNameUtils.kt @@ -0,0 +1,6 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17 + +import kotlinx.coroutines.CoroutineName +import kotlin.coroutines.CoroutineContext + +fun CoroutineContext.getCoroutineName(): String? = this[CoroutineName.Key]?.name diff --git a/Kotlin-Coroutines_1.4/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/NRTokenContext.kt b/Kotlin-Coroutines_1.4/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/NRTokenContext.kt new file mode 100644 index 0000000..3c1b1ad --- /dev/null +++ b/Kotlin-Coroutines_1.4/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/NRTokenContext.kt @@ -0,0 +1,34 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17 + +import kotlin.coroutines.* +import com.newrelic.api.agent.Token +import org.jetbrains.annotations.Nullable + +data class TokenContext(@Nullable var token: Token) + +class TokenContextElement(val context: TokenContext) : AbstractCoroutineContextElement(Key) { + companion object Key : CoroutineContext.Key +} + +fun CoroutineContext.getTokenContext(): TokenContext? = this[TokenContextElement]?.context + +fun CoroutineContext.getTokenContextOrNull(): TokenContext? = this[TokenContextElement]?.context + +fun addTokenContext(context : CoroutineContext, @Nullable token : Token) : CoroutineContext { + val tokenContext = TokenContext(token) + return context + TokenContextElement(tokenContext) +} + +fun removeTokenContext(context : CoroutineContext) : CoroutineContext { + val tokenContext = context.getTokenContextOrNull(); + if (tokenContext != null) { + @Nullable var token = tokenContext.token + token.expire() + + return context.minusKey(com.newrelic.instrumentation.kotlin.coroutines_17.TokenContextElement.Key) + } + return context +} + + + diff --git a/Kotlin-Coroutines_1.4/src/test/java/SampleTestTest.kt b/Kotlin-Coroutines_1.4/src/test/java/SampleTestTest.kt deleted file mode 100644 index 87a568a..0000000 --- a/Kotlin-Coroutines_1.4/src/test/java/SampleTestTest.kt +++ /dev/null @@ -1,3 +0,0 @@ -import junit.framework.TestCase - -class SampleTestTest : TestCase() \ No newline at end of file diff --git a/Kotlin-Coroutines_1.4/src/test/kotlin/Sample.kt b/Kotlin-Coroutines_1.4/src/test/kotlin/Sample.kt deleted file mode 100644 index c4e7255..0000000 --- a/Kotlin-Coroutines_1.4/src/test/kotlin/Sample.kt +++ /dev/null @@ -1,6 +0,0 @@ -class Sample() { - - fun sum(a: Int, b: Int) : Int { - return a + b - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.4/src/test/kotlin/SampleTest.kt b/Kotlin-Coroutines_1.4/src/test/kotlin/SampleTest.kt deleted file mode 100644 index a6ea6e9..0000000 --- a/Kotlin-Coroutines_1.4/src/test/kotlin/SampleTest.kt +++ /dev/null @@ -1,14 +0,0 @@ - -import kotlin.test.assertEquals -import org.junit.Test - -internal class SampleTest { - - private val testSample: Sample = Sample() - - @Test - fun testSum() { - val expected = 42 - assertEquals(expected, testSample.sum(40,2)) - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.5/build.gradle b/Kotlin-Coroutines_1.5/build.gradle deleted file mode 100644 index 384b565..0000000 --- a/Kotlin-Coroutines_1.5/build.gradle +++ /dev/null @@ -1,36 +0,0 @@ - -// Build.gradle generated for instrumentation module Kotlin-Coroutines_1.2 - -apply plugin: 'java' - -dependencies { - implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.5.0' - - // New Relic Java Agent dependencies - implementation 'com.newrelic.agent.java:newrelic-agent:6.5.1' - implementation 'com.newrelic.agent.java:newrelic-api:6.4.1' - implementation fileTree(include: ['*.jar'], dir: '../libs') -} - -jar { - manifest { - attributes 'Implementation-Title': 'com.newrelic.instrumentation.labs.Kotlin-Coroutines_1.5' - attributes 'Implementation-Vendor': 'New Relic Labs' - attributes 'Implementation-Vendor-Id': 'com.newrelic.labs' - attributes 'Implementation-Version': 2.0 - } -} - -verifyInstrumentation { - passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core:[1.5.0,1.7.0)' - passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.5.0,1.7.0)' - excludeRegex '.*SNAPSHOT' - excludeRegex '.*alpha' - excludeRegex '.*-eap-.*' - excludeRegex '.*-native-.*' - excludeRegex '.*-M[0-9]' - excludeRegex '.*-rc' - excludeRegex '.*-RC' - excludeRegex '.*-RC[0-9]' - excludeRegex '.*-Beta' -} diff --git a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRContinuationWrapper.java b/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRContinuationWrapper.java deleted file mode 100644 index 28ed281..0000000 --- a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRContinuationWrapper.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; - -public class NRContinuationWrapper implements Continuation { - - private Continuation delegate = null; - private String name = null; - private static boolean isTransformed = false; - - public NRContinuationWrapper(Continuation d, String n) { - delegate = d; - name = n; - if(!isTransformed) { - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - isTransformed = true; - } - } - - @Override - public CoroutineContext getContext() { - return delegate.getContext(); - } - - @Override - @Trace(async=true) - public void resumeWith(Object p0) { - - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",name != null ? name : Utils.getCoroutineName(getContext(), delegate.getClass())); - Token token = Utils.getToken(getContext()); - if(token != null) { - token.link(); - } - delegate.resumeWith(p0); - } - -} diff --git a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRCoroutineToken.java b/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRCoroutineToken.java deleted file mode 100644 index 57f1b6f..0000000 --- a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRCoroutineToken.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines; - -import com.newrelic.api.agent.Token; - -import kotlin.coroutines.AbstractCoroutineContextElement; -import kotlin.coroutines.CoroutineContext; - -public class NRCoroutineToken extends AbstractCoroutineContextElement - { - public static Key key = new Key(); - - public NRCoroutineToken(Token t) { - super(key); - token = t; - } - - private Token token = null; - - public static final class Key implements CoroutineContext.Key { - private Key() {} - } - - public Token getToken() { - return token; - } - - @Override - public int hashCode() { - return token.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(this != obj ) { - if(obj instanceof NRCoroutineToken) { - NRCoroutineToken t = (NRCoroutineToken)obj; - return t.token == token; - } - } else { - return true; - } - return false; - } - - @Override - public String toString() { - return "NRCoroutineToken"; - } - - -} diff --git a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRFunction1Wrapper.java b/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRFunction1Wrapper.java deleted file mode 100644 index c516b7c..0000000 --- a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRFunction1Wrapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; - -import kotlin.jvm.functions.Function1; - -public class NRFunction1Wrapper implements Function1 { - - private Function1 delegate = null; - private String name = null; - private static boolean isTransformed = false; - - public NRFunction1Wrapper(Function1 d, String n) { - delegate = d; - name = n; - if(!isTransformed) { - isTransformed = true; - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - } - } - - @Override - @Trace(dispatcher=true) - public R invoke(P1 p1) { - if(name != null) NewRelic.getAgent().getTracedMethod().setMetricName("Custom","WrappedSuspend",name); - if(delegate != null) { - return delegate.invoke(p1); - } - return null; - } - -} diff --git a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRFunction2Wrapper.java b/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRFunction2Wrapper.java deleted file mode 100644 index 967940e..0000000 --- a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/NRFunction2Wrapper.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; - -import kotlin.coroutines.Continuation; -import kotlin.jvm.functions.Function2; - -public class NRFunction2Wrapper implements Function2 { - - private Function2 delegate = null; - private String name = null; - private static boolean isTransformed = false; - public Token token = null; - - public NRFunction2Wrapper(Function2 d,String n) { - delegate = d; - name = n; - if(!isTransformed) { - isTransformed = true; - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - @Trace(async=true) - public R invoke(P1 p1, P2 p2) { - if(token != null) { - token.linkAndExpire(); - token = null; - } - String nameStr = null; - if(p2 instanceof Continuation) { - Continuation continuation = (Continuation)p2; - - if (!Utils.ignoreContinuation(continuation.getClass(), continuation.getContext())) { - NRContinuationWrapper wrapper = new NRContinuationWrapper(continuation, name); - p2 = (P2) wrapper; - } - } - if(nameStr == null) { - nameStr = name; - } - if(nameStr != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","WrappedSuspend",nameStr); - } - if(delegate != null) { - return delegate.invoke(p1, p2); - } - return null; - } - -} diff --git a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/Utils.java b/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/Utils.java deleted file mode 100644 index 6f36330..0000000 --- a/Kotlin-Coroutines_1.5/java/com/newrelic/instrumentation/kotlin/coroutines/Utils.java +++ /dev/null @@ -1,175 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines; - -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; -import java.util.logging.Level; - -import com.newrelic.agent.config.AgentConfig; -import com.newrelic.agent.config.AgentConfigListener; -import com.newrelic.agent.service.ServiceFactory; -import com.newrelic.api.agent.Config; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; - -import kotlin.coroutines.CoroutineContext; -import kotlinx.coroutines.CoroutineName; - -public class Utils implements AgentConfigListener { - - private static final List ignoredSuspends = new ArrayList(); - private static final List ignoredContinuations = new ArrayList(); - private static final List ignoredDispatchs = new ArrayList(); - private static final String SUSPENDSIGNORECONFIG = "Coroutines.ignores.suspends"; - private static final String CONTIGNORECONFIG = "Coroutines.ignores.continuations"; - private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; - - private static final Utils INSTANCE = new Utils(); - - static { - ServiceFactory.getConfigService().addIAgentConfigListener(INSTANCE); - Config config = NewRelic.getAgent().getConfig(); - String ignores = config.getValue(SUSPENDSIGNORECONFIG); - if (ignores != null && !ignores.isEmpty()) { - StringTokenizer st = new StringTokenizer(ignores, ","); - while (st.hasMoreTokens()) { - String token = st.nextToken(); - if (token != null && !token.isEmpty()) { - NewRelic.getAgent().getLogger().log(Level.INFO, "will ignore suspend: {0}", token); - ignoredSuspends.add(token); - } - } - } - ignores = config.getValue(CONTIGNORECONFIG); - if (ignores != null && !ignores.isEmpty()) { - StringTokenizer st = new StringTokenizer(ignores, ","); - while (st.hasMoreTokens()) { - String token = st.nextToken(); - if (token != null && !token.isEmpty()) { - NewRelic.getAgent().getLogger().log(Level.INFO, "will ignore continuation: {0}", token); - ignoredContinuations.add(token); - } - } - } - ignores = config.getValue(DISPATCHEDIGNORECONFIG); - if (ignores != null && !ignores.isEmpty()) { - StringTokenizer st = new StringTokenizer(ignores, ","); - while (st.hasMoreTokens()) { - String token = st.nextToken(); - if (token != null && !token.isEmpty()) { - NewRelic.getAgent().getLogger().log(Level.INFO, "will ignore dispatched continuation: {0}", token); - ignoredDispatchs.add(token); - } - } - } - - } - - public static boolean ignoreContinuation(String name) { - return ignoredContinuations.contains(name); - } - - public static boolean ignoreContinuation(Class continuation, CoroutineContext context) { - - String classname = continuation.getName(); - if(ignoredContinuations.contains(classname)) return true; - - if(context == null) return false; - - String name = getCoroutineName(context); - - if(ignoredContinuations.contains(name)) return true; - - return false; - } - - public static boolean ignoreDispatched(Class dispatched, CoroutineContext context) { - String classname = dispatched.getName(); - if(ignoredDispatchs.contains(classname)) return true; - - if(context == null) return false; - - String name = getCoroutineName(context); - - if(ignoredDispatchs.contains(name)) return true; - - return false; - } - - public static boolean ignoreSuspend(Class suspend, CoroutineContext context) { - - String classname = suspend.getName(); - - if(ignoredSuspends.contains(classname)) return true; - - if(context == null) return false; - - String name = getCoroutineName(context); - - if(ignoredSuspends.contains(name)) { - return true; - } - - return false; - } - - public static String sub = "createCoroutineFromSuspendFunction"; - - public static NRCoroutineToken setToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken == null) { - Token t = NewRelic.getAgent().getTransaction().getToken(); - if(t != null && t.isActive()) { - coroutineToken = new NRCoroutineToken(t); - return coroutineToken; - } else if(t != null) { - t.expire(); - t = null; - } - } - return null; - } - - public static Token getToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - Token token = null; - if(coroutineToken != null) { - token = coroutineToken.getToken(); - } - - return token; - } - - public static void expireToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken != null) { - Token token = coroutineToken.getToken(); - token.expire(); - context.minusKey(NRCoroutineToken.key); - } - } - - public static String getCoroutineName(CoroutineContext context, Class clazz) { - String name = getCoroutineName(context); - if(name != null) { - return name; - } - return clazz.getSimpleName(); - } - - public static String getCoroutineName(CoroutineContext context) { - CoroutineName cName = context.get(CoroutineName.Key); - if(cName != null) { - String name = cName.getName(); - if(name != null && !name.isEmpty()) return name; - } - - return null; - } - - @Override - public void configChanged(String appName, AgentConfig agentConfig) { - // TODO Auto-generated method stub - - } -} diff --git a/Kotlin-Coroutines_1.5/java/kotlin/coroutines/intrinsics/IntrinsicsKt__IntrinsicsJvmKt.java b/Kotlin-Coroutines_1.5/java/kotlin/coroutines/intrinsics/IntrinsicsKt__IntrinsicsJvmKt.java deleted file mode 100644 index 56219f6..0000000 --- a/Kotlin-Coroutines_1.5/java/kotlin/coroutines/intrinsics/IntrinsicsKt__IntrinsicsJvmKt.java +++ /dev/null @@ -1,38 +0,0 @@ -package kotlin.coroutines.intrinsics; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines.Utils; - -import kotlin.Unit; -import kotlin.coroutines.Continuation; -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; - -@Weave -abstract class IntrinsicsKt__IntrinsicsJvmKt { - - public static final Continuation createCoroutineUnintercepted(Function1, ? extends Object> f, Continuation c) { - String name = Utils.getCoroutineName(c.getContext(), c.getClass()); - Continuation result = Weaver.callOriginal(); - if(!Utils.ignoreContinuation(name)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper(result, name); - result = wrapper; - } - return result; - } - - @Trace - public static final Continuation createCoroutineUnintercepted(Function2, ? extends Object> f, R r, Continuation c) { - String name = Utils.getCoroutineName(c.getContext(), c.getClass()); - Continuation result = Weaver.callOriginal(); - if(!Utils.ignoreContinuation(name)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper(result, name); - result = wrapper; - } - return result; - } - -} diff --git a/Kotlin-Coroutines_1.5/java/kotlinx/coroutines/AbstractCoroutine.java b/Kotlin-Coroutines_1.5/java/kotlinx/coroutines/AbstractCoroutine.java deleted file mode 100644 index 8e95487..0000000 --- a/Kotlin-Coroutines_1.5/java/kotlinx/coroutines/AbstractCoroutine.java +++ /dev/null @@ -1,30 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines.Utils; - -import kotlin.coroutines.CoroutineContext; - -@Weave(type=MatchType.BaseClass) -public abstract class AbstractCoroutine { - - public abstract CoroutineContext getContext(); - - protected void onCompleted(T value) { - Utils.expireToken(getContext()); - Weaver.callOriginal(); - } - - protected void onCancelled(Throwable t, boolean b) { - Utils.expireToken(getContext()); - Weaver.callOriginal(); - } - - public void handleOnCompletionException$kotlinx_coroutines_core(Throwable t) { - Utils.expireToken(getContext()); - Weaver.callOriginal(); - } - -} diff --git a/Kotlin-Coroutines_1.5/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.5/java/kotlinx/coroutines/BuildersKt.java deleted file mode 100644 index 0edda99..0000000 --- a/Kotlin-Coroutines_1.5/java/kotlinx/coroutines/BuildersKt.java +++ /dev/null @@ -1,106 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines.NRCoroutineToken; -import com.newrelic.instrumentation.kotlin.coroutines.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines.Utils; - -import kotlin.Unit; -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; -import kotlin.jvm.functions.Function2; - -@SuppressWarnings({ "unchecked", "rawtypes" }) -@Weave -public class BuildersKt { - - @Trace - public static final T runBlocking(CoroutineContext context, Function2, ? extends Object> block) { - - if (!Utils.ignoreSuspend(block.getClass(), context)) { - if (!(block instanceof NRFunction2Wrapper)) { - String name = Utils.getCoroutineName(context); - if (name == null) - name = block.getClass().getName(); - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper( - block, name); - block = wrapper; - } - } - T t = Weaver.callOriginal(); - return t; - } - - @Trace - public static final Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if(!Utils.ignoreSuspend(block.getClass(),context) && !Utils.ignoreSuspend(block.getClass(), scope.getCoroutineContext())) { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - if(!(block instanceof NRFunction2Wrapper)) { - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name == null) name = block.getClass().getName(); - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block,name); - block = wrapper; - } - } - return Weaver.callOriginal(); - } - - @Trace - public static final Object invoke(CoroutineDispatcher dispatcher, Function2, ? extends Object> block, Continuation c) { - if(!Utils.ignoreSuspend(block.getClass(), null)) { - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block,block.getClass().getName()); - block = wrapper; - } - } - Object t = Weaver.callOriginal(); - return t; - } - - @Trace - public static final kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if(!Utils.ignoreSuspend(block.getClass(), context) && !Utils.ignoreSuspend(block.getClass(), scope.getCoroutineContext())) { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - if(!(block instanceof NRFunction2Wrapper)) { - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name == null) name = block.getClass().getName(); - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block,name); - block = wrapper; - } - } - Job j = Weaver.callOriginal(); - return j; - } - - @Trace - public static final Object withContext(CoroutineContext context,Function2, ? extends Object> block, Continuation completion) { - if(!Utils.ignoreSuspend(block.getClass(),context)) { - - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - if(!(block instanceof NRFunction2Wrapper)) { - String name = Utils.getCoroutineName(context); - if(name == null) name = block.getClass().getName(); - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block,name); - block = wrapper; - } - } - return Weaver.callOriginal(); - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.5/java/kotlinx/coroutines/DispatchedTask.java b/Kotlin-Coroutines_1.5/java/kotlinx/coroutines/DispatchedTask.java deleted file mode 100644 index c561587..0000000 --- a/Kotlin-Coroutines_1.5/java/kotlinx/coroutines/DispatchedTask.java +++ /dev/null @@ -1,40 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines.Utils; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; - -@Weave(type=MatchType.BaseClass) -public abstract class DispatchedTask { - - public abstract Continuation getDelegate$kotlinx_coroutines_core(); - - @Trace(async=true) - public void run() { - Continuation continuation = getDelegate$kotlinx_coroutines_core(); - if(continuation != null) { - CoroutineContext context = continuation.getContext(); - if(continuation instanceof DispatchedContinuation) { - DispatchedContinuation dispatched = (DispatchedContinuation)continuation; - continuation = dispatched.continuation; - } - - if(context != null && !Utils.ignoreDispatched(continuation.getClass(), context)) { - Token t = Utils.getToken(context); - if(t != null) t.link(); - String cName = Utils.getCoroutineName(context, continuation.getClass()); - if(cName != null) - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","DispatchedTask",cName); - } - } - Weaver.callOriginal(); - } - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRCoroutineToken.java b/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRCoroutineToken.java deleted file mode 100644 index 35c8d30..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRCoroutineToken.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_15; - -import com.newrelic.api.agent.Token; - -import kotlin.coroutines.AbstractCoroutineContextElement; -import kotlin.coroutines.CoroutineContext; - -public class NRCoroutineToken extends AbstractCoroutineContextElement - { - public static Key key = new Key(); - - public NRCoroutineToken(Token t) { - super(key); - token = t; - } - - private Token token = null; - - public static final class Key implements CoroutineContext.Key { - private Key() {} - } - - public Token getToken() { - return token; - } - - @Override - public int hashCode() { - return token.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(this != obj ) { - if(obj instanceof NRCoroutineToken) { - NRCoroutineToken t = (NRCoroutineToken)obj; - return t.token == token; - } - } else { - return true; - } - return false; - } - - @Override - public String toString() { - return "NRCoroutineToken"; - } - - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRFunction1Wrapper.java b/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRFunction1Wrapper.java deleted file mode 100644 index 5ffa6b7..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRFunction1Wrapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_15; - -import com.newrelic.agent.bridge.AgentBridge; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function1; - -public class NRFunction1Wrapper implements Function1 { - - private Function1 delegate = null; - private static boolean isTransformed = false; - - public NRFunction1Wrapper(Function1 d) { - delegate = d; - if(!isTransformed) { - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - isTransformed = true; - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public R invoke(P1 p1) { - if(p1 instanceof Continuation && !(p1 instanceof SuspendFunction)) { - // wrap if needed - if(!(p1 instanceof NRContinuationWrapper)) { - String cont_string = Utils.getContinuationString((Continuation)p1); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p1, cont_string); - p1 = (P1) wrapper; - } - } - return delegate != null ? delegate.invoke(p1) : null; - } - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRFunction2Wrapper.java b/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRFunction2Wrapper.java deleted file mode 100644 index cac0c1c..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRFunction2Wrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_15; - -import com.newrelic.agent.bridge.AgentBridge; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function2; - -public class NRFunction2Wrapper implements Function2 { - - private Function2 delegate = null; - private static boolean isTransformed = false; - - public NRFunction2Wrapper(Function2 d) { - delegate = d; - if(!isTransformed) { - isTransformed = true; - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public R invoke(P1 p1, P2 p2) { - - if(p2 instanceof Continuation && !(p2 instanceof SuspendFunction)) { - // wrap if needed - String cont_string = Utils.getContinuationString((Continuation)p2); - if(!(p2 instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(cont_string)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p2, cont_string); - p2 = (P2) wrapper; - } - } - if(delegate != null) { - return delegate.invoke(p1, p2); - } - return null; - } - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRRunnable.java b/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRRunnable.java deleted file mode 100644 index 56a7a61..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/NRRunnable.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_15; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; - -import kotlin.coroutines.Continuation; -import kotlinx.coroutines.DispatchedTask; - -public class NRRunnable implements Runnable { - - private Runnable delegate = null; - private Token token = null; - private static boolean isTransformed = false; - - public NRRunnable(Runnable r,Token t) { - if(!isTransformed) { - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - isTransformed = true; - } - delegate = r; - token = t; - } - - @Override - @Trace(async = true) - public void run() { - boolean nameSet = false; - if(delegate != null && delegate instanceof DispatchedTask) { - DispatchedTask task = (DispatchedTask)delegate; - Continuation cont_delegate = task.getDelegate$kotlinx_coroutines_core(); - if(cont_delegate != null) { - String cont_string = Utils.getContinuationString(cont_delegate); - if(cont_string == null) cont_string = cont_delegate.getClass().getName(); - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","DispatchedTask",Utils.getContinuationString(cont_delegate)); - nameSet = true; - } - } - if(!nameSet) { - String delegateType = delegate != null ? delegate.getClass().getName() : "null"; - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AsyncRunnableWrapper",delegateType); - } - if(token != null) { - token.linkAndExpire(); - token = null; - } - if(delegate != null) { - delegate.run(); - } - } - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/Utils.java b/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/Utils.java deleted file mode 100644 index 796f371..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_15/Utils.java +++ /dev/null @@ -1,225 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_15; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - -import com.newrelic.agent.config.AgentConfig; -import com.newrelic.agent.config.AgentConfigListener; -import com.newrelic.agent.service.ServiceFactory; -import com.newrelic.api.agent.Config; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; -import kotlin.coroutines.jvm.internal.BaseContinuationImpl; -import kotlinx.coroutines.AbstractCoroutine; -import kotlinx.coroutines.CoroutineName; -import kotlinx.coroutines.CoroutineScope; -import kotlinx.coroutines.DispatchedTask; - -public class Utils implements AgentConfigListener { - - private static final List ignoredContinuations = new ArrayList(); - private static final List ignoredScopes = new ArrayList<>(); - private static final String CONTIGNORECONFIG = "Coroutines.ignores.continuations"; - private static final String SCOPESIGNORECONFIG = "Coroutines.ignores.scopes"; - private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; - private static final String DELAYED_ENABLED_CONFIG = "Coroutines.delayed.enabled"; - - public static final String CREATEMETHOD1 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4"; - public static final String CREATEMETHOD2 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3"; - private static final Utils INSTANCE = new Utils(); - private static final String CONT_LOC = "Continuation at"; - public static boolean DELAYED_ENABLED = true; - - static { - ServiceFactory.getConfigService().addIAgentConfigListener(INSTANCE); - Config config = NewRelic.getAgent().getConfig(); - loadConfig(config); - ignoredContinuations.add(CREATEMETHOD1); - ignoredContinuations.add(CREATEMETHOD2); - Object value = config.getValue(DELAYED_ENABLED_CONFIG); - if(value != null) { - if(value instanceof Boolean) { - DELAYED_ENABLED = (Boolean)value; - } else { - DELAYED_ENABLED = Boolean.valueOf(value.toString()); - } - } - - } - - public static NRRunnable getRunnableWrapper(Runnable r) { - if(r instanceof NRRunnable) { - return null; - } - if(r instanceof DispatchedTask) { - DispatchedTask task = (DispatchedTask)r; - Continuation cont = task.getDelegate$kotlinx_coroutines_core(); - if(cont != null) { - String cont_string = getContinuationString(cont); - if(cont_string != null && DispatchedTaskIgnores.ignoreDispatchedTask(cont_string)) { - return null; - } - } - } - - Token t = NewRelic.getAgent().getTransaction().getToken(); - if(t != null && t.isActive()) { - return new NRRunnable(r, t); - } else if(t != null) { - t.expire(); - t = null; - } - return null; - } - - private static void loadConfig(Config config) { - String ignores = config.getValue(CONTIGNORECONFIG); - NewRelic.getAgent().getLogger().log(Level.FINE, "Value of {0}: {1}", CONTIGNORECONFIG, ignores); - if (ignores != null && !ignores.isEmpty()) { - ignoredContinuations.clear(); - String[] ignoresList = ignores.split(","); - - for(String ignore : ignoresList) { - if (!ignoredContinuations.contains(ignore)) { - ignoredContinuations.add(ignore); - NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore Continuations named {0}", ignore); - } - } - } else if(!ignoredContinuations.isEmpty()) { - ignoredContinuations.clear(); - } - ignores = config.getValue(DISPATCHEDIGNORECONFIG); - NewRelic.getAgent().getLogger().log(Level.FINE, "Value of {0}: {1}", DISPATCHEDIGNORECONFIG, ignores); - DispatchedTaskIgnores.reset(); - if (ignores != null && !ignores.isEmpty()) { - DispatchedTaskIgnores.configure(ignores); - } - ignores = config.getValue(SCOPESIGNORECONFIG); - if (ignores != null && !ignores.isEmpty()) { - ignoredScopes.clear(); - String[] ignoresList = ignores.split(","); - - for(String ignore : ignoresList) { - if (!ignoredScopes.contains(ignore)) { - ignoredScopes.add(ignore); - NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore CoroutineScopes named {0}", ignore); - } - } - } else if(!ignoredScopes.isEmpty()) { - ignoredScopes.clear(); - } - - } - - public static boolean ignoreScope(CoroutineScope scope) { - CoroutineContext ctx = scope.getCoroutineContext(); - String name = getCoroutineName(ctx); - String className = scope.getClass().getName(); - return ignoreScope(className) || ignoreScope(name); - } - - public static boolean ignoreScope(String coroutineScope) { - return ignoredScopes.contains(coroutineScope); - } - - public static boolean ignoreContinuation(String cont_string) { - return ignoredContinuations.contains(cont_string); - } - - public static String sub = "createCoroutineFromSuspendFunction"; - - public static NRCoroutineToken setToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken == null) { - Token t = NewRelic.getAgent().getTransaction().getToken(); - if(t != null && t.isActive()) { - coroutineToken = new NRCoroutineToken(t); - return coroutineToken; - } else if(t != null) { - t.expire(); - t = null; - } - } - return null; - } - - public static Token getToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - Token token = null; - if(coroutineToken != null) { - token = coroutineToken.getToken(); - } - - return token; - } - - public static void expireToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken != null) { - Token token = coroutineToken.getToken(); - token.expire(); - context.minusKey(NRCoroutineToken.key); - } - } - - @SuppressWarnings("unchecked") - public static String getCoroutineName(CoroutineContext context, Continuation continuation) { - if(continuation instanceof AbstractCoroutine) { - return ((AbstractCoroutine)continuation).nameString$kotlinx_coroutines_core(); - } - if(continuation instanceof BaseContinuationImpl) { - return ((BaseContinuationImpl)continuation).toString(); - } - return null; - } - - public static String getCoroutineName(CoroutineContext context) { - CoroutineName cName = context.get(CoroutineName.Key); - if(cName != null) { - String name = cName.getName(); - if(name != null && !name.isEmpty()) return name; - } - - return null; - } - - @Override - public void configChanged(String appName, AgentConfig agentConfig) { - loadConfig(agentConfig); - Object value = agentConfig.getValue(DELAYED_ENABLED_CONFIG); - if(value != null) { - if(value instanceof Boolean) { - DELAYED_ENABLED = (Boolean)value; - } else { - DELAYED_ENABLED = Boolean.valueOf(value.toString()); - } - } - } - - public static String getContinuationString(Continuation continuation) { - String contString = continuation.toString(); - - if(contString.equals(CREATEMETHOD1) || contString.equals(CREATEMETHOD2)) { - return sub; - } - - if(contString.startsWith(CONT_LOC)) { - return contString; - } - - if(continuation instanceof AbstractCoroutine) { - return ((AbstractCoroutine)continuation).nameString$kotlinx_coroutines_core(); - } - - int index = contString.indexOf('@'); - if(index > -1) { - return contString.substring(0, index); - } - - return null; - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlin/coroutines/ContinuationKt.java b/Kotlin-Coroutines_1.5/src/main/java/kotlin/coroutines/ContinuationKt.java deleted file mode 100644 index ebfb1c1..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlin/coroutines/ContinuationKt.java +++ /dev/null @@ -1,34 +0,0 @@ -package kotlin.coroutines; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRFunction2Wrapper; - -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; - -@Weave -public abstract class ContinuationKt { - - @Trace - public static void startCoroutine(Function1, ? extends Object> f, Continuation cont ) { - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper, ? extends Object> wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @Trace - public static void startCoroutine(Function2, ? extends Object> f, R receiver, Continuation cont) { - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/AwaitAll.java b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/AwaitAll.java deleted file mode 100644 index 81064be..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/AwaitAll.java +++ /dev/null @@ -1,22 +0,0 @@ -package kotlinx.coroutines; - -import java.util.List; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -import kotlin.coroutines.Continuation; - -@Weave -abstract class AwaitAll { - - @Trace - public Object await(Continuation> cont) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AwaitAll","await"); - Object result = Weaver.callOriginal(); - return result; - } - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java deleted file mode 100644 index 0efc143..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/BuildersKt.java +++ /dev/null @@ -1,181 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRCoroutineToken; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.Utils; - -import kotlin.Unit; -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function2; - -@SuppressWarnings({ "unchecked", "rawtypes" }) -@Weave -public class BuildersKt { - - @Trace(dispatcher = true) - public static final T runBlocking(CoroutineContext context, Function2, ? extends Object> block) { - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - String name = Utils.getCoroutineName(context); - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking",name); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking"); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - - if (!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - T t = Weaver.callOriginal(); - return t; - } - - @Trace(dispatcher = true) - public static final Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if (!Utils.ignoreScope(scope)) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); - } - - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - } else { - NewRelic.getAgent().getTransaction().ignore(); - } - return Weaver.callOriginal(); - } - - @Trace(dispatcher = true) - public static final Object invoke(CoroutineDispatcher dispatcher, Function2, ? extends Object> block, Continuation c) { - - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", c.toString()); - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - if(c != null && !Utils.ignoreContinuation(c.toString())) { - boolean isSuspend = c instanceof SuspendFunction; - if(!isSuspend) { - String cont_string = Utils.getContinuationString(c); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(c, cont_string); - c = wrapper; - } - } - Object t = Weaver.callOriginal(); - return t; - } - - @Trace(dispatcher = true) - public static final kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if (!Utils.ignoreScope(scope)) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); - - String name = Utils.getCoroutineName(context); - if (name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if (name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if (!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper( - block); - block = wrapper; - } - } else { - NewRelic.getAgent().getTransaction().ignore(); - } - Job j = Weaver.callOriginal(); - return j; - } - - @Trace(dispatcher = true) - public static final Object withContext(CoroutineContext context,Function2, ? extends Object> block, Continuation completion) { - String name = Utils.getCoroutineName(context,completion); - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext"); - } - if(completion != null) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Completion", completion.toString()); - } - - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - if(completion != null && !Utils.ignoreContinuation(completion.toString())) { - if(!(completion instanceof NRContinuationWrapper)) { - String cont_string = Utils.getContinuationString(completion); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(completion, cont_string); - completion = wrapper; - } - } - return Weaver.callOriginal(); - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java deleted file mode 100644 index 3bfcbd4..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java +++ /dev/null @@ -1,22 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRRunnable; -import com.newrelic.instrumentation.kotlin.coroutines_15.Utils; - -import kotlin.coroutines.CoroutineContext; - -@Weave(type = MatchType.BaseClass) -public abstract class CoroutineDispatcher { - - public void dispatch(CoroutineContext ctx, Runnable r) { - NRRunnable wrapper = Utils.getRunnableWrapper(r); - if(wrapper != null) { - r = wrapper; - } - - Weaver.callOriginal(); - } -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/YieldKt.java b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/YieldKt.java deleted file mode 100644 index 9eaad07..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/YieldKt.java +++ /dev/null @@ -1,23 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_15.Utils; - -import kotlin.Unit; -import kotlin.coroutines.Continuation; - -@Weave -public class YieldKt { - - @Trace - public static Object yield(Continuation cont) { - String cont_string = Utils.getContinuationString(cont); - if(cont_string != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Yield","yield",cont_string); - } - return Weaver.callOriginal(); - } -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java deleted file mode 100644 index 21f8130..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java +++ /dev/null @@ -1,100 +0,0 @@ -package kotlinx.coroutines.intrinsics; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.TracedMethod; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.Utils; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; - -@Weave -public abstract class CancellableKt { - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static void startCoroutineCancellable(Function1, ? extends java.lang.Object> f, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - if(continuationString != null) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Suspend-Type", "Function1"); - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static void startCoroutineCancellable(Function2, ? extends java.lang.Object> f, R receiver, Continuation cont, Function1 onCancellation) { - String continuationString = Utils.getContinuationString(cont); - if(!(cont instanceof SuspendFunction)) { - // create continuation wrapper if needed - if(cont != null && !Utils.ignoreContinuation(continuationString) && !(cont instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - if(continuationString != null) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Suspend-Type", "Function2"); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Receiver", receiver.getClass().getName()); - - if(onCancellation != null && !(onCancellation instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(onCancellation); - onCancellation = wrapper; - - } - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends java.lang.Object> wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @Trace - public static void startCoroutineCancellable(Continuation completion, Continuation cont) { - String completionString = Utils.getContinuationString(completion); - if(completion != null && !(completion instanceof SuspendFunction)) { - // create continuation wrapper if needed - if(!Utils.ignoreContinuation(completionString) && !(completion instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(completion, completionString); - completion = wrapper; - } - } - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - // create continuation wrapper if needed - if(cont != null && !Utils.ignoreContinuation(continuationString) && !(cont instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - if(completionString != null) { - traced.addCustomAttribute("Completion", completionString); - } - if(continuationString != null) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); - } - traced.addCustomAttribute("Suspend-Type", "None"); - Weaver.callOriginal(); - } - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java b/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java deleted file mode 100644 index a038514..0000000 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java +++ /dev/null @@ -1,138 +0,0 @@ -package kotlinx.coroutines.intrinsics; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.TracedMethod; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.Utils; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; -import kotlinx.coroutines.internal.ScopeCoroutine; - -@Weave -public class UndispatchedKt { - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUnintercepted(Function1, ? extends Object> f, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function1"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUnintercepted(Function2, ? extends Object> f, R receiver, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUndispatched(Function1, ? extends Object> f, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function1"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUndispatched(Function2, ? extends Object> f, R receiver, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final Object startUndispatchedOrReturn(ScopeCoroutine scope, R receiver, Function2, ? extends Object> f) { - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - return Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutine scope, R receiver, Function2, ? extends Object> f) { - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - return Weaver.callOriginal(); - } - -} diff --git a/Kotlin-Coroutines_1.5/src/test/java/SampleTestTest.kt b/Kotlin-Coroutines_1.5/src/test/java/SampleTestTest.kt deleted file mode 100644 index 87a568a..0000000 --- a/Kotlin-Coroutines_1.5/src/test/java/SampleTestTest.kt +++ /dev/null @@ -1,3 +0,0 @@ -import junit.framework.TestCase - -class SampleTestTest : TestCase() \ No newline at end of file diff --git a/Kotlin-Coroutines_1.5/src/test/kotlin/Sample.kt b/Kotlin-Coroutines_1.5/src/test/kotlin/Sample.kt deleted file mode 100644 index c4e7255..0000000 --- a/Kotlin-Coroutines_1.5/src/test/kotlin/Sample.kt +++ /dev/null @@ -1,6 +0,0 @@ -class Sample() { - - fun sum(a: Int, b: Int) : Int { - return a + b - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.5/src/test/kotlin/SampleTest.kt b/Kotlin-Coroutines_1.5/src/test/kotlin/SampleTest.kt deleted file mode 100644 index a6ea6e9..0000000 --- a/Kotlin-Coroutines_1.5/src/test/kotlin/SampleTest.kt +++ /dev/null @@ -1,14 +0,0 @@ - -import kotlin.test.assertEquals -import org.junit.Test - -internal class SampleTest { - - private val testSample: Sample = Sample() - - @Test - fun testSum() { - val expected = 42 - assertEquals(expected, testSample.sum(40,2)) - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.7/build.gradle b/Kotlin-Coroutines_1.7/build.gradle index cac13f8..c020436 100644 --- a/Kotlin-Coroutines_1.7/build.gradle +++ b/Kotlin-Coroutines_1.7/build.gradle @@ -1,16 +1,14 @@ -// Build.gradle generated for instrumentation module Kotlin-Coroutines_1.2 apply plugin: 'java' +apply plugin: 'org.jetbrains.kotlin.jvm' targetCompatibility = JavaVersion.VERSION_1_9 dependencies { - implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.7.3' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0' // New Relic Java Agent dependencies - implementation 'com.newrelic.agent.java:newrelic-agent:6.0.0' - implementation 'com.newrelic.agent.java:newrelic-api:6.0.0' implementation fileTree(include: ['*.jar'], dir: '../libs') } diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/DispatchedTaskIgnores.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/DispatchedTaskIgnores.java index 5d239d3..3fafbce 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/DispatchedTaskIgnores.java +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/DispatchedTaskIgnores.java @@ -1,29 +1,37 @@ package com.newrelic.instrumentation.kotlin.coroutines_17; +import com.newrelic.api.agent.Config; +import com.newrelic.api.agent.NewRelic; + import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.logging.Level; -import com.newrelic.api.agent.Config; -import com.newrelic.api.agent.NewRelic; - +/* +* Used to ignore Dispatched Tasks that the user has configured to ignore +*/ public class DispatchedTaskIgnores { - private static List ignoredTasks = new ArrayList<>(); - private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; + private static final List ignoredTasks = new ArrayList<>(); + private static final String DISPATCHED_IGNORE_CONFIG = "Coroutines.ignores.dispatched"; static { Config config = NewRelic.getAgent().getConfig(); - String ignores = config.getValue(DISPATCHEDIGNORECONFIG); + String ignores = config.getValue(DISPATCHED_IGNORE_CONFIG); configure(ignores); } - public static boolean ignoreDispatchedTask(String contString) { - return ignoredTasks.contains(contString); + public static boolean ignoreDispatchedTask(String dispatchedTask) { + return ignoredTasks.contains(dispatchedTask); } - + + public static void addIgnoredTasks(Collection toIgnore) { + ignoredTasks.addAll(toIgnore); + } + public static void addIgnore(String ignore) { if(!ignoredTasks.contains(ignore)) { ignoredTasks.add(ignore); diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java index 5a016fa..ab38e34 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java @@ -4,10 +4,14 @@ import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; - import kotlin.coroutines.Continuation; import kotlin.coroutines.CoroutineContext; +import org.jetbrains.annotations.NotNull; +/* +* Used to wrap a Continuation instance. Necessary to control which Continuations +* are tracked. Tracking all Continuations can effect performance. +* */ public class NRContinuationWrapper implements Continuation { private Continuation delegate = null; @@ -23,6 +27,7 @@ public NRContinuationWrapper(Continuation d, String n) { } } + @NotNull @Override public CoroutineContext getContext() { return delegate.getContext(); @@ -30,7 +35,7 @@ public CoroutineContext getContext() { @Override @Trace(async=true) - public void resumeWith(Object p0) { + public void resumeWith(@NotNull Object p0) { String contString = Utils.getContinuationString(delegate); if(contString != null && !contString.isEmpty()) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",contString); diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRCoroutineToken.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRCoroutineToken.java deleted file mode 100644 index d6a63da..0000000 --- a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRCoroutineToken.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_17; - -import com.newrelic.api.agent.Token; - -import kotlin.coroutines.AbstractCoroutineContextElement; -import kotlin.coroutines.CoroutineContext; - -public class NRCoroutineToken extends AbstractCoroutineContextElement - { - public static Key key = new Key(); - - public NRCoroutineToken(Token t) { - super(key); - token = t; - } - - private Token token = null; - - public static final class Key implements CoroutineContext.Key { - private Key() {} - } - - public Token getToken() { - return token; - } - - @Override - public int hashCode() { - return token.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(this != obj ) { - if(obj instanceof NRCoroutineToken) { - NRCoroutineToken t = (NRCoroutineToken)obj; - return t.token == token; - } - } else { - return true; - } - return false; - } - - @Override - public String toString() { - return "NRCoroutineToken"; - } - - -} diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayCancellableContinuation.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayCancellableContinuation.java new file mode 100644 index 0000000..6b60a06 --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayCancellableContinuation.java @@ -0,0 +1,146 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import kotlin.Unit; +import kotlin.coroutines.CoroutineContext; +import kotlin.jvm.functions.Function1; +import kotlinx.coroutines.CancellableContinuation; +import kotlinx.coroutines.CoroutineDispatcher; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/* + * Used to wrap the tracking of a call to the Coroutine delay function if tracking is enabled + * Will report the delay as a segment + */ +public class NRDelayCancellableContinuation implements CancellableContinuation { + + private final CancellableContinuation delegate; + private Segment segment; + + public NRDelayCancellableContinuation(CancellableContinuation delegate, String type) { + this.delegate = delegate; + String name = Utils.getContinuationString(delegate); + segment = NewRelic.getAgent().getTransaction().startSegment(type); + segment.addCustomAttribute("CancellableContinuation", name); + } + + @Override + public void resumeWith(@NotNull Object o) { + if(segment != null) { + segment.end(); + segment = null; + } + if(delegate != null) { + delegate.resumeWith(o); + } + } + + @Override + public @NotNull CoroutineContext getContext() { + return delegate.getContext(); + } + + @Override + public boolean isActive() { + return delegate.isActive(); + } + + @Override + public boolean isCompleted() { + return delegate.isCompleted(); + } + + @Override + public boolean isCancelled() { + return delegate.isCancelled(); + } + + @Override + public @Nullable Object tryResume(T t, @Nullable Object o) { + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.tryResume(t,o); + } + + @Override + public @Nullable Object tryResume(T t, @Nullable Object o, @Nullable Function1 function1) { + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.tryResume(t,o, function1); + } + + @Override + public @Nullable Object tryResumeWithException(@NotNull Throwable throwable) { + NewRelic.noticeError(throwable); + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.tryResumeWithException(throwable); + } + + @Override + public void completeResume(@NotNull Object o) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.completeResume(o); + } + + @Override + public void initCancellability() { + delegate.initCancellability(); + } + + @Override + public boolean cancel(@Nullable Throwable throwable) { + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.cancel(throwable); + } + + @Override + public void invokeOnCancellation(@NotNull Function1 function1) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.invokeOnCancellation(function1); + } + + @Override + public void resumeUndispatched(@NotNull CoroutineDispatcher coroutineDispatcher, T t) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resumeUndispatched(coroutineDispatcher, t); + } + + @Override + public void resumeUndispatchedWithException(@NotNull CoroutineDispatcher coroutineDispatcher, @NotNull Throwable throwable) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resumeUndispatchedWithException(coroutineDispatcher, throwable); + } + + @Override + public void resume(T t, @Nullable Function1 function1) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resume(t,function1); + } +} diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayContinuation.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayContinuation.java new file mode 100644 index 0000000..ad0ad39 --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRDelayContinuation.java @@ -0,0 +1,38 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; +import kotlinx.coroutines.CancellableContinuation; +import org.jetbrains.annotations.NotNull; + +public class NRDelayContinuation implements Continuation { + + private final Continuation delegate; + private final Segment segment; + + public NRDelayContinuation(Continuation delegate, String type) { + this.delegate = delegate; + String name = Utils.getContinuationString(delegate); + segment = NewRelic.getAgent().getTransaction().startSegment(type); + segment.addCustomAttribute("Continuation", name); + boolean isCancellable = delegate instanceof CancellableContinuation; + segment.addCustomAttribute("isCancellable", isCancellable); + } + + @Override + public void resumeWith(@NotNull Object o) { + if(segment != null) { + segment.end(); + } + if(delegate != null) { + delegate.resumeWith(o); + } + } + + @Override + public @NotNull CoroutineContext getContext() { + return delegate.getContext(); + } +} diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java new file mode 100644 index 0000000..480c33e --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java @@ -0,0 +1,41 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import kotlin.coroutines.Continuation; +import kotlin.jvm.functions.Function1; + +/* +* Used to wrap a suspend function that was used as an input as a parameter to the +* Coroutine functions runBlocking, async, invoke, launch and withContext +*/ +public class NRFunction1SuspendWrapper implements Function1 { + + private Function1 delegate = null; + private static boolean isTransformed = false; + + public NRFunction1SuspendWrapper(Function1 d) { + delegate = d; + if(!isTransformed) { + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + isTransformed = true; + } + } + + @Override + public R invoke(P1 p1) { + if(p1 instanceof Continuation) { + Continuation cont = (Continuation)p1; + + String cont_string = Utils.getContinuationString(cont); + if(cont_string != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",cont_string); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction","UnknownSource"); + + } + } + return delegate != null ? delegate.invoke(p1) : null; + } + +} diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1Wrapper.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1Wrapper.java index 37bc3bc..d0232a6 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1Wrapper.java +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1Wrapper.java @@ -1,7 +1,6 @@ package com.newrelic.instrumentation.kotlin.coroutines_17; import com.newrelic.agent.bridge.AgentBridge; - import kotlin.coroutines.Continuation; import kotlin.coroutines.jvm.internal.SuspendFunction; import kotlin.jvm.functions.Function1; @@ -24,9 +23,9 @@ public NRFunction1Wrapper(Function1 d) { public R invoke(P1 p1) { if(p1 instanceof Continuation && !(p1 instanceof SuspendFunction)) { // wrap if needed - if(!(p1 instanceof NRContinuationWrapper)) { - String cont_string = Utils.getContinuationString((Continuation)p1); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p1, cont_string); + Continuation continuation = (Continuation) p1; + if(!(p1 instanceof NRContinuationWrapper) && Utils.continueWithContinuation(continuation)) { + NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation) p1, Utils.getContinuationString(continuation)); p1 = (P1) wrapper; } } diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java new file mode 100644 index 0000000..8130e00 --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java @@ -0,0 +1,66 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; +import kotlin.jvm.functions.Function2; +import kotlinx.coroutines.CoroutineScope; + +/* + * Used to wrap a suspend function that was used as an input as a parameter to the + * Coroutine functions runBlocking, async, invoke, launch and withContext + */ +public class NRFunction2SuspendWrapper implements Function2 { + + private Function2 delegate = null; + private static boolean isTransformed = false; + + public NRFunction2SuspendWrapper(Function2 d) { + delegate = d; + if(!isTransformed) { + isTransformed = true; + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + } + } + + @Override + public R invoke(S s, T t) { + + // set name + boolean name_set = false; + if(s instanceof CoroutineScope) { + CoroutineScope scope = (CoroutineScope)s; + CoroutineContext ctx = scope.getCoroutineContext(); + Token token = Utils.getToken(ctx); + if(token != null) { + token.link(); + } + CoroutineContext context = scope.getCoroutineContext(); + String coroutineName = Utils.getCoroutineName(context); + if(coroutineName != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",coroutineName); + name_set = true; + } + + } + + if(!name_set && t instanceof Continuation) { + Continuation cont = (Continuation)t; + + String cont_string = Utils.getContinuationString(cont); + if(cont_string != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",cont_string); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction","UnknownSource"); + + } + } + if(delegate != null) { + return delegate.invoke(s, t); + } + return null; + } + +} diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2Wrapper.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2Wrapper.java deleted file mode 100644 index d81eb8e..0000000 --- a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2Wrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_17; - -import com.newrelic.agent.bridge.AgentBridge; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function2; - -public class NRFunction2Wrapper implements Function2 { - - private Function2 delegate = null; - private static boolean isTransformed = false; - - public NRFunction2Wrapper(Function2 d) { - delegate = d; - if(!isTransformed) { - isTransformed = true; - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public R invoke(P1 p1, P2 p2) { - - if(p2 instanceof Continuation && !(p2 instanceof SuspendFunction)) { - // wrap if needed - String cont_string = Utils.getContinuationString((Continuation)p2); - if(!(p2 instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(cont_string)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p2, cont_string); - p2 = (P2) wrapper; - } - } - if(delegate != null) { - return delegate.invoke(p1, p2); - } - return null; - } - -} diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRRunnable.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRRunnable.java index 7d9cdb3..5eee99b 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRRunnable.java +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRRunnable.java @@ -4,7 +4,6 @@ import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; - import kotlin.coroutines.Continuation; import kotlinx.coroutines.DispatchedTask; @@ -30,13 +29,11 @@ public void run() { if(delegate != null && delegate instanceof DispatchedTask) { DispatchedTask task = (DispatchedTask)delegate; Continuation cont_delegate = task.getDelegate$kotlinx_coroutines_core(); - if(cont_delegate != null) { - String cont_string = Utils.getContinuationString(cont_delegate); - if(cont_string == null) cont_string = cont_delegate.getClass().getName(); - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","DispatchedTask",Utils.getContinuationString(cont_delegate)); - nameSet = true; - } - } + String cont_string = Utils.getContinuationString(cont_delegate); + if(cont_string == null) cont_string = cont_delegate.getClass().getName(); + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","DispatchedTask",cont_string); + nameSet = true; + } if(!nameSet) { String delegateType = delegate != null ? delegate.getClass().getName() : "null"; NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AsyncRunnableWrapper",delegateType); diff --git a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java index 94850ff..5e2de63 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java +++ b/Kotlin-Coroutines_1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java @@ -15,7 +15,6 @@ import kotlin.coroutines.CoroutineContext; import kotlin.coroutines.jvm.internal.BaseContinuationImpl; import kotlinx.coroutines.AbstractCoroutine; -import kotlinx.coroutines.CoroutineName; import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.DispatchedTask; @@ -27,7 +26,7 @@ public class Utils implements AgentConfigListener { private static final String SCOPESIGNORECONFIG = "Coroutines.ignores.scopes"; private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; private static final String DELAYED_ENABLED_CONFIG = "Coroutines.delayed.enabled"; - + public static final String CREATEMETHOD1 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4"; public static final String CREATEMETHOD2 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3"; private static final Utils INSTANCE = new Utils(); @@ -48,9 +47,9 @@ public class Utils implements AgentConfigListener { DELAYED_ENABLED = Boolean.valueOf(value.toString()); } } - + } - + public static NRRunnable getRunnableWrapper(Runnable r) { if(r instanceof NRRunnable) { return null; @@ -65,7 +64,7 @@ public static NRRunnable getRunnableWrapper(Runnable r) { } } } - + Token t = NewRelic.getAgent().getTransaction().getToken(); if(t != null && t.isActive()) { return new NRRunnable(r, t); @@ -75,14 +74,14 @@ public static NRRunnable getRunnableWrapper(Runnable r) { } return null; } - + private static void loadConfig(Config config) { String ignores = config.getValue(CONTIGNORECONFIG); NewRelic.getAgent().getLogger().log(Level.FINE, "Value of {0}: {1}", CONTIGNORECONFIG, ignores); if (ignores != null && !ignores.isEmpty()) { ignoredContinuations.clear(); String[] ignoresList = ignores.split(","); - + for(String ignore : ignoresList) { if (!ignoredContinuations.contains(ignore)) { ignoredContinuations.add(ignore); @@ -102,7 +101,7 @@ private static void loadConfig(Config config) { if (ignores != null && !ignores.isEmpty()) { ignoredScopes.clear(); String[] ignoresList = ignores.split(","); - + for(String ignore : ignoresList) { if (!ignoredScopes.contains(ignore)) { ignoredScopes.add(ignore); @@ -112,61 +111,103 @@ private static void loadConfig(Config config) { } else if(!ignoredScopes.isEmpty()) { ignoredScopes.clear(); } - + } - + public static boolean ignoreScope(CoroutineScope scope) { CoroutineContext ctx = scope.getCoroutineContext(); String name = getCoroutineName(ctx); String className = scope.getClass().getName(); return ignoreScope(className) || ignoreScope(name); } - + public static boolean ignoreScope(String coroutineScope) { return ignoredScopes.contains(coroutineScope); } - + public static boolean ignoreContinuation(String cont_string) { return ignoredContinuations.contains(cont_string); } - + + /* + * Allows certain Coroutine scopes to be ignored + * coroutineScope can be a Coroutine name or CoroutineScope class name + */ + public static boolean continueWithScope(CoroutineScope scope) { + CoroutineContext ctx = scope.getCoroutineContext(); + String name = getCoroutineName(ctx); + String className = scope.getClass().getName(); + return continueWithScope(className) && continueWithScope(name); + } + + /* + * Allows certain Coroutine scopes to be ignored + * coroutineScope can be a Coroutine name or CoroutineScope class name + */ + public static boolean continueWithScope(String coroutineScope) { + for(String ignoredScope : ignoredScopes) { + if(coroutineScope.matches(ignoredScope)) { + return false; + } + } + return !ignoredScopes.contains(coroutineScope); + } + + public static boolean continueWithContinuation(Continuation continuation) { + /* + * Don't trace internal Coroutines Continuations + */ + String className = continuation.getClass().getName(); + if(className.startsWith("kotlin")) return false; + + /* + * Get the continuation string and check if it should be ignored + */ + String cont_string = getContinuationString(continuation); + if(cont_string == null) { return false; } + + for(String ignored : ignoredContinuations) { + if(cont_string.matches(ignored)) { + return false; + } + } + return !ignoredContinuations.contains(cont_string); + } + + + public static String sub = "createCoroutineFromSuspendFunction"; - public static NRCoroutineToken setToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken == null) { + public static void setToken(CoroutineContext context) { + TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); + if (tokenContext == null) { Token t = NewRelic.getAgent().getTransaction().getToken(); if(t != null && t.isActive()) { - coroutineToken = new NRCoroutineToken(t); - return coroutineToken; + NRTokenContextKt.addTokenContext(context, t); } else if(t != null) { t.expire(); t = null; } } - return null; } public static Token getToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - Token token = null; - if(coroutineToken != null) { - token = coroutineToken.getToken(); + TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); + if(tokenContext != null) { + return tokenContext.getToken(); } - - return token; + return null; } public static void expireToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken != null) { - Token token = coroutineToken.getToken(); + TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); + if(tokenContext != null) { + Token token = tokenContext.getToken(); token.expire(); - context.minusKey(NRCoroutineToken.key); + NRTokenContextKt.removeTokenContext(context); } } - - @SuppressWarnings("unchecked") + public static String getCoroutineName(CoroutineContext context, Continuation continuation) { if(continuation instanceof AbstractCoroutine) { return ((AbstractCoroutine)continuation).nameString$kotlinx_coroutines_core(); @@ -178,13 +219,7 @@ public static String getCoroutineName(CoroutineContext context, Continuation } public static String getCoroutineName(CoroutineContext context) { - CoroutineName cName = context.get(CoroutineName.Key); - if(cName != null) { - String name = cName.getName(); - if(name != null && !name.isEmpty()) return name; - } - - return null; + return CoroutineNameUtilsKt.getCoroutineName(context); } @Override @@ -199,27 +234,27 @@ public void configChanged(String appName, AgentConfig agentConfig) { } } } - + public static String getContinuationString(Continuation continuation) { String contString = continuation.toString(); - + if(contString.equals(CREATEMETHOD1) || contString.equals(CREATEMETHOD2)) { return sub; } - + if(contString.startsWith(CONT_LOC)) { return contString; } - + if(continuation instanceof AbstractCoroutine) { return ((AbstractCoroutine)continuation).nameString$kotlinx_coroutines_core(); } - + int index = contString.indexOf('@'); if(index > -1) { return contString.substring(0, index); } - + return null; } } diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlin/coroutines/ContinuationKt.java b/Kotlin-Coroutines_1.7/src/main/java/kotlin/coroutines/ContinuationKt.java deleted file mode 100644 index 1cf16ae..0000000 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlin/coroutines/ContinuationKt.java +++ /dev/null @@ -1,34 +0,0 @@ -package kotlin.coroutines; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2Wrapper; - -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; - -@Weave -public abstract class ContinuationKt { - - @Trace - public static void startCoroutine(Function1, ? extends Object> f, Continuation cont ) { - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper, ? extends Object> wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @Trace - public static void startCoroutine(Function2, ? extends Object> f, R receiver, Continuation cont) { - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - -} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java b/Kotlin-Coroutines_1.7/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java new file mode 100644 index 0000000..300c483 --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java @@ -0,0 +1,32 @@ +package kotlin.coroutines; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; + +import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function2; + +@Weave(originalName = "kotlin.coroutines.ContinuationKt") +public abstract class ContinuationKt_Instrumentation { + + @Trace + public static void startCoroutine(Function1, ?> f, Continuation cont ) { + if(!(f instanceof NRFunction1SuspendWrapper)) { + f = new NRFunction1SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + @Trace + public static void startCoroutine(Function2, ?> f, R receiver, Continuation cont) { + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + +} diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/AbstractCoroutine.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java similarity index 78% rename from Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/AbstractCoroutine.java rename to Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java index 7300641..a83b9b4 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/AbstractCoroutine.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java @@ -7,15 +7,15 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_15.Utils; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; import kotlin.coroutines.Continuation; import kotlin.coroutines.CoroutineContext; import kotlin.jvm.functions.Function2; -@Weave(type=MatchType.BaseClass) -public abstract class AbstractCoroutine { +@Weave(type=MatchType.BaseClass, originalName = "kotlinx.coroutines.AbstractCoroutine") +public abstract class AbstractCoroutine_Instrumentation { public abstract CoroutineContext getContext(); public abstract String nameString$kotlinx_coroutines_core(); @@ -35,10 +35,9 @@ protected void onCancelled(Throwable t, boolean b) { } @Trace - public void start(CoroutineStart start, R receiver, Function2, ? extends Object> block) { - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper<>(block); - block = wrapper; + public void start(CoroutineStart start, R receiver, Function2, ?> block) { + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = new NRFunction2SuspendWrapper<>(block); } String ctxName = Utils.getCoroutineName(getContext()); String name = ctxName != null ? ctxName : nameString$kotlinx_coroutines_core(); diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AwaitAll.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java similarity index 76% rename from Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AwaitAll.java rename to Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java index 81064be..9ef878e 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AwaitAll.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java @@ -9,14 +9,13 @@ import kotlin.coroutines.Continuation; -@Weave -abstract class AwaitAll { +@Weave(originalName = "kotlinx.coroutines.AwaitAll") +abstract class AwaitAll_Instrumentation { @Trace public Object await(Continuation> cont) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AwaitAll","await"); - Object result = Weaver.callOriginal(); - return result; + return Weaver.callOriginal(); } } diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java deleted file mode 100644 index 9f2ac41..0000000 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt.java +++ /dev/null @@ -1,179 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRCoroutineToken; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; - -import kotlin.Unit; -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function2; - -@SuppressWarnings({ "unchecked", "rawtypes" }) -@Weave -public class BuildersKt { - - @Trace(dispatcher = true) - public static final T runBlocking(CoroutineContext context, Function2, ? extends Object> block) { - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - String name = Utils.getCoroutineName(context); - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - - if (!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - T t = Weaver.callOriginal(); - return t; - } - - @Trace(dispatcher = true) - public static final Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if (!Utils.ignoreScope(scope)) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); - } - - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - } else { - NewRelic.getAgent().getTransaction().ignore(); - } - return Weaver.callOriginal(); - } - - @Trace(dispatcher = true) - public static final Object invoke(CoroutineDispatcher dispatcher, Function2, ? extends Object> block, Continuation c) { - - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", c.toString()); - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - if(c != null && !Utils.ignoreContinuation(c.toString())) { - boolean isSuspend = c instanceof SuspendFunction; - if(!isSuspend) { - String cont_string = Utils.getContinuationString(c); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(c, cont_string); - c = wrapper; - } - } - Object t = Weaver.callOriginal(); - return t; - } - - @Trace(dispatcher = true) - public static final kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if (!Utils.ignoreScope(scope)) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); - - String name = Utils.getCoroutineName(context); - if (name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if (name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if (!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper( - block); - block = wrapper; - } - } else { - NewRelic.getAgent().getTransaction().ignore(); - } - Job j = Weaver.callOriginal(); - return j; - } - - @Trace(dispatcher = true) - public static final Object withContext(CoroutineContext context,Function2, ? extends Object> block, Continuation completion) { - String name = Utils.getCoroutineName(context,completion); - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext"); - } - if(completion != null) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Completion", completion.toString()); - } - - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - if(completion != null && !Utils.ignoreContinuation(completion.toString())) { - if(!(completion instanceof NRContinuationWrapper)) { - String cont_string = Utils.getContinuationString(completion); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(completion, cont_string); - completion = wrapper; - } - } - return Weaver.callOriginal(); - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java new file mode 100644 index 0000000..6a97c4c --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java @@ -0,0 +1,138 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRContinuationWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; + +import kotlin.Unit; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; +import kotlin.coroutines.jvm.internal.SuspendFunction; +import kotlin.jvm.functions.Function2; + +@SuppressWarnings({ "unchecked", "rawtypes" }) +@Weave(originalName = "kotlinx.coroutines.BuildersKt") +public class BuildersKt_Instrumentation { + + @Trace(dispatcher = true) + public static T runBlocking(CoroutineContext context, Function2, ?> block) { + String name = Utils.getCoroutineName(context); + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking",name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking"); + } + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); + + if (!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, + Function2, ?> block) { + if (Utils.continueWithScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-CoroutineName", Utils.getCoroutineName(scope.getCoroutineContext())); + + String name = Utils.getCoroutineName(context); + if(name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); + } + + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + } else { + NewRelic.getAgent().getTransaction().ignore(); + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static Object invoke(CoroutineDispatcher_Instrumentation dispatcher, + Function2, ?> block, Continuation cont) { + + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", cont.toString()); + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + if(Utils.continueWithContinuation(cont)) { + boolean isSuspend = cont instanceof SuspendFunction; + if(!isSuspend) { + String cont_string = Utils.getContinuationString(cont); + cont = new NRContinuationWrapper<>(cont, cont_string); + } + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, + Function2, ?> block) { + if (Utils.continueWithScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-CoroutineName", Utils.getCoroutineName(scope.getCoroutineContext())); + + String name = Utils.getCoroutineName(context); + if (name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if (name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); + } + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); + if (!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper( + block); + } + } else { + NewRelic.getAgent().getTransaction().ignore(); + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static Object withContext(CoroutineContext context, Function2, ?> block, + Continuation completion) { + String name = Utils.getCoroutineName(context,completion); + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext",name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext"); + } + if(completion != null) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Completion", completion.toString()); + } + + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + if(completion != null && Utils.continueWithContinuation(completion)) { + if(!(completion instanceof NRContinuationWrapper)) { + String cont_string = Utils.getContinuationString(completion); + completion = new NRContinuationWrapper<>(completion, cont_string); + } + } + return Weaver.callOriginal(); + } +} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineDispatcher_Instrumentation.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineDispatcher_Instrumentation.java new file mode 100644 index 0000000..14b5365 --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineDispatcher_Instrumentation.java @@ -0,0 +1,26 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRRunnable; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; + +import kotlin.coroutines.CoroutineContext; + +/* + * Dispatchers are used to dispatch tasks to another thread. By wrapping the Runable + * we can track the Coroutine across threads + */ +@Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.CoroutineDispatcher") +public abstract class CoroutineDispatcher_Instrumentation { + + public void dispatch(CoroutineContext ctx, Runnable r) { + NRRunnable wrapper = Utils.getRunnableWrapper(r); + if(wrapper != null) { + r = wrapper; + } + + Weaver.callOriginal(); + } +} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerImplKt.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerImplKt.java deleted file mode 100644 index f025bac..0000000 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerImplKt.java +++ /dev/null @@ -1,15 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.weaver.SkipIfPresent; - -/** - * Included in order to avoid loading in lower versions - * - * @author dhilpipre - * - */ - -@SkipIfPresent -public class CoroutineExceptionHandlerImplKt { - -} diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt_Instrumentation.java similarity index 67% rename from Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java rename to Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt_Instrumentation.java index 97fc24b..393f625 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt_Instrumentation.java @@ -4,8 +4,8 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -@Weave -public abstract class CoroutineExceptionHandlerKt { +@Weave(originalName = "kotlinx.coroutines.CoroutineExceptionHandlerKt") +public abstract class CoroutineExceptionHandlerKt_Instrumentation { @Trace public static void handleCoroutineException(kotlin.coroutines.CoroutineContext ctx,Throwable t) { diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler_Instrumentation.java similarity index 71% rename from Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java rename to Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler_Instrumentation.java index 6419a85..9aa2936 100644 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler_Instrumentation.java @@ -6,8 +6,8 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -@Weave(type = MatchType.Interface) -public abstract class CoroutineExceptionHandler { +@Weave(type = MatchType.Interface, originalName = "kotlinx.coroutines.CoroutineExceptionHandler") +public abstract class CoroutineExceptionHandler_Instrumentation { @Trace public void handleException(kotlin.coroutines.CoroutineContext ctx, java.lang.Throwable t) { diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DelayKt_Instrumentation.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DelayKt_Instrumentation.java new file mode 100644 index 0000000..4a387d1 --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DelayKt_Instrumentation.java @@ -0,0 +1,30 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRDelayContinuation; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; +import kotlin.Unit; +import kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.DelayKt") +public class DelayKt_Instrumentation { + + @Trace + public static Object awaitCancellation(Continuation cont) { + if(Utils.DELAYED_ENABLED && !(cont instanceof NRDelayContinuation)) { + cont = new NRDelayContinuation<>(cont,"AwaitCancellation"); + } + return Weaver.callOriginal(); + } + + @Trace + public static Object delay(long timeMillis, Continuation cont) { + if(Utils.DELAYED_ENABLED && !(cont instanceof NRDelayContinuation)) { + cont = new NRDelayContinuation<>(cont,"Delay"); + } + return Weaver.callOriginal(); + } + +} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/Delay_Instrumentation.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/Delay_Instrumentation.java new file mode 100644 index 0000000..aeee82e --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/Delay_Instrumentation.java @@ -0,0 +1,37 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRDelayCancellableContinuation; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRDelayContinuation; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; +import kotlin.Unit; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; + +@Weave(type = MatchType.Interface, originalName = "kotlinx.coroutines.Delay") +public class Delay_Instrumentation { + + @Trace + public Object delay(long timeMills, Continuation continuation) { + if(Utils.DELAYED_ENABLED && !(continuation instanceof NRDelayContinuation)) { + continuation = new NRDelayContinuation<>(continuation,"Delay"); + } + return Weaver.callOriginal(); + } + + @Trace + public void scheduleResumeAfterDelay(long timeMills, CancellableContinuation continuation) { + if(Utils.DELAYED_ENABLED && !(continuation instanceof NRDelayContinuation)) { + continuation = new NRDelayCancellableContinuation<>(continuation,"scheduleResumeAfterDelay"); + } + Weaver.callOriginal(); + } + + @Trace + public DisposableHandle invokeOnTimeout(long timeMills, Runnable r, CoroutineContext context) { + return Weaver.callOriginal(); + } +} diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/DispatcherExecutor.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor_Instrumentation.java similarity index 60% rename from Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/DispatcherExecutor.java rename to Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor_Instrumentation.java index b75d8d1..eb823ba 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/DispatcherExecutor.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/DispatcherExecutor_Instrumentation.java @@ -1,15 +1,17 @@ package kotlinx.coroutines; import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_15.NRRunnable; -import com.newrelic.instrumentation.kotlin.coroutines_15.Utils; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRRunnable; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; -@Weave(type = MatchType.BaseClass) -abstract class DispatcherExecutor { +@Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.DispatcherExecutor") +abstract class DispatcherExecutor_Instrumentation { + @Trace public void execute(Runnable r) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","DispatcherExecutor","execute"); NRRunnable wrapper = Utils.getRunnableWrapper(r); diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/EventLoopImplBase.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java similarity index 52% rename from Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/EventLoopImplBase.java rename to Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java index 9257bc5..95033d1 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/EventLoopImplBase.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java @@ -7,15 +7,15 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_15.Utils; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; -@Weave -public abstract class EventLoopImplBase { +@Weave(originalName = "kotlinx.coroutines.EventLoopImplBase") +public abstract class EventLoopImplBase_Instrumentation { - @Weave(type = MatchType.BaseClass) - public static abstract class DelayedTask { + @Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedTask") + public static abstract class DelayedTask_Instrumentation { - public DelayedTask(long nanos) { + public DelayedTask_Instrumentation(long nanos) { if(Utils.DELAYED_ENABLED) { token = NewRelic.getAgent().getTransaction().getToken(); } @@ -35,11 +35,11 @@ public void dispose() { } } - @Weave - static class DelayedResumeTask extends DelayedTask { + @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask") + static class DelayedResumeTask_Instrumentation extends DelayedTask_Instrumentation { - public DelayedResumeTask(long nanos, kotlinx.coroutines.CancellableContinuation cont) { + public DelayedResumeTask_Instrumentation(long nanos, kotlinx.coroutines.CancellableContinuation cont) { super(nanos); } @@ -55,10 +55,10 @@ public void run() { } } - @Weave - static class DelayedRunnableTask extends DelayedTask { + @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask") + static class DelayedRunnableTask_Instrumentation extends DelayedTask_Instrumentation { - public DelayedRunnableTask(long nanos, Runnable r) { + public DelayedRunnableTask_Instrumentation(long nanos, Runnable r) { super(nanos); } @@ -73,8 +73,8 @@ public void run() { } } - @Weave - public static class DelayedTaskQueue { + @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedTaskQueue") + public static class DelayedTaskQueue_Instrumentation { } } diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java similarity index 69% rename from Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java rename to Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java index 0f9ec41..817a87d 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java @@ -7,13 +7,13 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -@Weave -abstract class ResumeUndispatchedRunnable { +@Weave(originalName = "kotlinx.coroutines.ResumeUndispatchedRunnable") +abstract class ResumeUndispatchedRunnable_Instrumentation { @NewField private Token token = null; - public ResumeUndispatchedRunnable(CoroutineDispatcher dispatcher, CancellableContinuation cont) { + public ResumeUndispatchedRunnable_Instrumentation(CoroutineDispatcher_Instrumentation dispatcher, CancellableContinuation cont) { Token t = NewRelic.getAgent().getTransaction().getToken(); if(t != null && t.isActive()) { token = t; diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/TimeSource.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/TimeSource.java deleted file mode 100644 index 98780e5..0000000 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/TimeSource.java +++ /dev/null @@ -1,16 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.weaver.SkipIfPresent; - -/** - * Included to avoid loading in version 1.4.x - * Only difference between 1.5.x and 1.4.x is one method in AbstractCoroutine that is in 1.4.x but not 1.5.x - * TimeSource is only 1.4.x versions - * - * @author dhilpipre - * - */ -@SkipIfPresent -public class TimeSource { - -} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/YieldKt.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/YieldKt_Instrumentation.java similarity index 78% rename from Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/YieldKt.java rename to Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/YieldKt_Instrumentation.java index 3abb85e..3f8e573 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/YieldKt.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/YieldKt_Instrumentation.java @@ -4,13 +4,13 @@ import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_14.Utils; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; import kotlin.Unit; import kotlin.coroutines.Continuation; -@Weave -public class YieldKt { +@Weave(originalName = "kotlinx.coroutines.YieldKt") +public class YieldKt_Instrumentation { @Trace public static Object yield(Continuation cont) { diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java new file mode 100644 index 0000000..8fe0d9b --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java @@ -0,0 +1,34 @@ +package kotlinx.coroutines.channels; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import kotlin.Unit; +import kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.channels.BufferedChannel") +public abstract class BufferedChannel_Instrumentation { + + @Trace(dispatcher = true) + public Object receive(Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"receive"); + + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public void cancel(java.util.concurrent.CancellationException ex) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"Cancel"); + + Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public Object send(E e, Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"send"); + return Weaver.callOriginal(); + } + + +} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java new file mode 100644 index 0000000..51cacb5 --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java @@ -0,0 +1,35 @@ +package kotlinx.coroutines.channels; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +import kotlin.Unit; +import kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.channels.ChannelCoroutine") +public abstract class ChannelCoroutine_Instrumentation { + + @Trace(dispatcher = true) + public Object receive(Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"receive"); + + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public void cancel(java.util.concurrent.CancellationException ex) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"Cancel"); + + Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public Object send(E e, Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"send"); + return Weaver.callOriginal(); + } + + +} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java similarity index 50% rename from Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java rename to Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java index 8091748..ead5823 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java @@ -5,49 +5,45 @@ import com.newrelic.api.agent.TracedMethod; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_14.Utils; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRContinuationWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1Wrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; import kotlin.coroutines.Continuation; import kotlin.coroutines.jvm.internal.SuspendFunction; import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; -@Weave -public abstract class CancellableKt { +@Weave(originalName = "kotlinx.coroutines.intrinsics.CancellableKt") +public abstract class CancellableKt_Instrumentation { - @SuppressWarnings({ "unchecked", "rawtypes" }) @Trace - public static void startCoroutineCancellable(Function1, ? extends java.lang.Object> f, Continuation cont) { + public static void startCoroutineCancellable(Function1, ?> f, Continuation cont) { String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; + if(!(cont instanceof SuspendFunction)) { + if(!(cont instanceof NRContinuationWrapper) && Utils.continueWithContinuation(cont)) { + cont = new NRContinuationWrapper<>(cont, continuationString); } } if(continuationString != null) { NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); } NewRelic.getAgent().getTracedMethod().addCustomAttribute("Suspend-Type", "Function1"); - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; + if(!(f instanceof NRFunction1SuspendWrapper)) { + f = new NRFunction1SuspendWrapper<>(f); } Weaver.callOriginal(); } - @SuppressWarnings({ "unchecked", "rawtypes" }) @Trace - public static void startCoroutineCancellable(Function2, ? extends java.lang.Object> f, R receiver, Continuation cont, Function1 onCancellation) { + public static void startCoroutineCancellable(Function2, ?> f, R receiver, Continuation cont, Function1 onCancellation) { String continuationString = Utils.getContinuationString(cont); if(!(cont instanceof SuspendFunction)) { // create continuation wrapper if needed - if(cont != null && !Utils.ignoreContinuation(continuationString) && !(cont instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; + if(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { + cont = new NRContinuationWrapper<>(cont, continuationString); } } if(continuationString != null) { @@ -57,13 +53,11 @@ public static void startCoroutineCancellable(Function2(onCancellation); - onCancellation = wrapper; + onCancellation = new NRFunction1Wrapper<>(onCancellation); } - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends java.lang.Object> wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); } Weaver.callOriginal(); } @@ -71,19 +65,17 @@ public static void startCoroutineCancellable(Function2 completion, Continuation cont) { String completionString = Utils.getContinuationString(completion); - if(completion != null && !(completion instanceof SuspendFunction)) { + if(!(completion instanceof SuspendFunction)) { // create continuation wrapper if needed - if(!Utils.ignoreContinuation(completionString) && !(completion instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(completion, completionString); - completion = wrapper; + if(Utils.continueWithContinuation(completion) && !(completion instanceof NRContinuationWrapper)) { + completion = new NRContinuationWrapper<>(completion, completionString); } } String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { + if(!(cont instanceof SuspendFunction)) { // create continuation wrapper if needed - if(cont != null && !Utils.ignoreContinuation(continuationString) && !(cont instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; + if(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { + cont = new NRContinuationWrapper<>(cont, continuationString); } } TracedMethod traced = NewRelic.getAgent().getTracedMethod(); diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java deleted file mode 100644 index 1e7b64a..0000000 --- a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java +++ /dev/null @@ -1,93 +0,0 @@ -package kotlinx.coroutines.intrinsics; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.TracedMethod; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; -import kotlinx.coroutines.internal.ScopeCoroutine; - -@Weave -public class UndispatchedKt { - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUnintercepted(Function1, ? extends Object> f, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function1"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final void startCoroutineUndispatched(Function2, ? extends Object> f, R receiver, Continuation cont) { - String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } - } - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - if(continuationString != null) { - traced.addCustomAttribute("Continuation", continuationString); - } - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final Object startUndispatchedOrReturn(ScopeCoroutine scope, R receiver, Function2, ? extends Object> f) { - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - return Weaver.callOriginal(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Trace - public static final Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutine scope, R receiver, Function2, ? extends Object> f) { - TracedMethod traced = NewRelic.getAgent().getTracedMethod(); - traced.addCustomAttribute("Suspend-Type", "Function2"); - traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - return Weaver.callOriginal(); - } - -} diff --git a/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java new file mode 100644 index 0000000..dcd4fd2 --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java @@ -0,0 +1,86 @@ +package kotlinx.coroutines.intrinsics; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.TracedMethod; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRContinuationWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction1SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.NRFunction2SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_17.Utils; + +import kotlin.coroutines.Continuation; +import kotlin.coroutines.jvm.internal.SuspendFunction; +import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function2; +import kotlinx.coroutines.internal.ScopeCoroutine; + +@Weave(originalName = "kotlinx.coroutines.intrinsics.UndispatchedKt") +public class UndispatchedKt_Instrumentation { + + @Trace + public static void startCoroutineUnintercepted(Function1, ?> f, Continuation cont) { + String continuationString = Utils.getContinuationString(cont); + if(!(cont instanceof SuspendFunction)) { + if(!(cont instanceof NRContinuationWrapper) && Utils.continueWithContinuation(cont)) { + cont = new NRContinuationWrapper<>(cont, continuationString); + } + } + TracedMethod traced = NewRelic.getAgent().getTracedMethod(); + traced.addCustomAttribute("Suspend-Type", "Function1"); + if(continuationString != null) { + traced.addCustomAttribute("Continuation", continuationString); + } + if(!(f instanceof NRFunction1SuspendWrapper)) { + f = new NRFunction1SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + @Trace + public static void startCoroutineUndispatched(Function2, ?> f, R receiver, + Continuation cont) { + String continuationString = Utils.getContinuationString(cont); + if(!(cont instanceof SuspendFunction)) { + if(!(cont instanceof NRContinuationWrapper) && Utils.continueWithContinuation(cont)) { + cont = new NRContinuationWrapper<>(cont, continuationString); + } + } + TracedMethod traced = NewRelic.getAgent().getTracedMethod(); + traced.addCustomAttribute("Suspend-Type", "Function2"); + if(continuationString != null) { + traced.addCustomAttribute("Continuation", continuationString); + } + traced.addCustomAttribute("Receiver", receiver.getClass().getName()); + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + @Trace + public static Object startUndispatchedOrReturn(ScopeCoroutine scope, R receiver, + Function2, ?> f) { + TracedMethod traced = NewRelic.getAgent().getTracedMethod(); + traced.addCustomAttribute("Suspend-Type", "Function2"); + traced.addCustomAttribute("Receiver", receiver.getClass().getName()); + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); + } + return Weaver.callOriginal(); + } + + @Trace + public static Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutine scope, R receiver, + Function2, ?> f) { + TracedMethod traced = NewRelic.getAgent().getTracedMethod(); + traced.addCustomAttribute("Suspend-Type", "Function2"); + traced.addCustomAttribute("Receiver", receiver.getClass().getName()); + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); + } + return Weaver.callOriginal(); + } + +} diff --git a/Kotlin-Coroutines_1.7/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/CoroutineNameUtils.kt b/Kotlin-Coroutines_1.7/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/CoroutineNameUtils.kt new file mode 100644 index 0000000..355fcd8 --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/CoroutineNameUtils.kt @@ -0,0 +1,6 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17 + +import kotlinx.coroutines.CoroutineName +import kotlin.coroutines.CoroutineContext + +fun CoroutineContext.getCoroutineName(): String? = this[CoroutineName.Key]?.name diff --git a/Kotlin-Coroutines_1.7/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/NRTokenContext.kt b/Kotlin-Coroutines_1.7/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/NRTokenContext.kt new file mode 100644 index 0000000..3c1b1ad --- /dev/null +++ b/Kotlin-Coroutines_1.7/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_17/NRTokenContext.kt @@ -0,0 +1,34 @@ +package com.newrelic.instrumentation.kotlin.coroutines_17 + +import kotlin.coroutines.* +import com.newrelic.api.agent.Token +import org.jetbrains.annotations.Nullable + +data class TokenContext(@Nullable var token: Token) + +class TokenContextElement(val context: TokenContext) : AbstractCoroutineContextElement(Key) { + companion object Key : CoroutineContext.Key +} + +fun CoroutineContext.getTokenContext(): TokenContext? = this[TokenContextElement]?.context + +fun CoroutineContext.getTokenContextOrNull(): TokenContext? = this[TokenContextElement]?.context + +fun addTokenContext(context : CoroutineContext, @Nullable token : Token) : CoroutineContext { + val tokenContext = TokenContext(token) + return context + TokenContextElement(tokenContext) +} + +fun removeTokenContext(context : CoroutineContext) : CoroutineContext { + val tokenContext = context.getTokenContextOrNull(); + if (tokenContext != null) { + @Nullable var token = tokenContext.token + token.expire() + + return context.minusKey(com.newrelic.instrumentation.kotlin.coroutines_17.TokenContextElement.Key) + } + return context +} + + + diff --git a/Kotlin-Coroutines_1.7/src/test/java/SampleTestTest.kt b/Kotlin-Coroutines_1.7/src/test/java/SampleTestTest.kt deleted file mode 100644 index 87a568a..0000000 --- a/Kotlin-Coroutines_1.7/src/test/java/SampleTestTest.kt +++ /dev/null @@ -1,3 +0,0 @@ -import junit.framework.TestCase - -class SampleTestTest : TestCase() \ No newline at end of file diff --git a/Kotlin-Coroutines_1.7/src/test/kotlin/Sample.kt b/Kotlin-Coroutines_1.7/src/test/kotlin/Sample.kt deleted file mode 100644 index c4e7255..0000000 --- a/Kotlin-Coroutines_1.7/src/test/kotlin/Sample.kt +++ /dev/null @@ -1,6 +0,0 @@ -class Sample() { - - fun sum(a: Int, b: Int) : Int { - return a + b - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.7/src/test/kotlin/SampleTest.kt b/Kotlin-Coroutines_1.7/src/test/kotlin/SampleTest.kt deleted file mode 100644 index a6ea6e9..0000000 --- a/Kotlin-Coroutines_1.7/src/test/kotlin/SampleTest.kt +++ /dev/null @@ -1,14 +0,0 @@ - -import kotlin.test.assertEquals -import org.junit.Test - -internal class SampleTest { - - private val testSample: Sample = Sample() - - @Test - fun testSum() { - val expected = 42 - assertEquals(expected, testSample.sum(40,2)) - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.9/build.gradle b/Kotlin-Coroutines_1.9/build.gradle index 703e58d..652b470 100644 --- a/Kotlin-Coroutines_1.9/build.gradle +++ b/Kotlin-Coroutines_1.9/build.gradle @@ -1,7 +1,6 @@ -// Build.gradle generated for instrumentation module Kotlin-Coroutines_1.2 - apply plugin: 'java' +apply plugin: 'org.jetbrains.kotlin.jvm' targetCompatibility = JavaVersion.VERSION_1_9 @@ -9,8 +8,6 @@ dependencies { implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.9.0' // New Relic Java Agent dependencies - implementation 'com.newrelic.agent.java:newrelic-agent:6.0.0' - implementation 'com.newrelic.agent.java:newrelic-api:6.0.0' implementation fileTree(include: ['*.jar'], dir: '../libs') } @@ -24,7 +21,7 @@ jar { } verifyInstrumentation { - passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core:[1.9.0,)]' + passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core:[1.9.0,)' passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.9.0,)' excludeRegex '.*SNAPSHOT' excludeRegex '.*alpha' diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/DispatchedTaskIgnores.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/DispatchedTaskIgnores.java index 25547fa..148cf89 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/DispatchedTaskIgnores.java +++ b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/DispatchedTaskIgnores.java @@ -1,29 +1,37 @@ package com.newrelic.instrumentation.kotlin.coroutines_19; +import com.newrelic.api.agent.Config; +import com.newrelic.api.agent.NewRelic; + import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.logging.Level; -import com.newrelic.api.agent.Config; -import com.newrelic.api.agent.NewRelic; - +/* +* Used to ignore Dispatched Tasks that the user has configured to ignore +*/ public class DispatchedTaskIgnores { - private static List ignoredTasks = new ArrayList<>(); - private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; + private static final List ignoredTasks = new ArrayList<>(); + private static final String DISPATCHED_IGNORE_CONFIG = "Coroutines.ignores.dispatched"; static { Config config = NewRelic.getAgent().getConfig(); - String ignores = config.getValue(DISPATCHEDIGNORECONFIG); + String ignores = config.getValue(DISPATCHED_IGNORE_CONFIG); configure(ignores); } - public static boolean ignoreDispatchedTask(String contString) { - return ignoredTasks.contains(contString); + public static boolean ignoreDispatchedTask(String dispatchedTask) { + return ignoredTasks.contains(dispatchedTask); } - + + public static void addIgnoredTasks(Collection toIgnore) { + ignoredTasks.addAll(toIgnore); + } + public static void addIgnore(String ignore) { if(!ignoredTasks.contains(ignore)) { ignoredTasks.add(ignore); diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRContinuationWrapper.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRContinuationWrapper.java index 9773d3b..5989cc8 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRContinuationWrapper.java +++ b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRContinuationWrapper.java @@ -4,10 +4,14 @@ import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; - import kotlin.coroutines.Continuation; import kotlin.coroutines.CoroutineContext; +import org.jetbrains.annotations.NotNull; +/* +* Used to wrap a Continuation instance. Necessary to control which Continuations +* are tracked. Tracking all Continuations can effect performance. +* */ public class NRContinuationWrapper implements Continuation { private Continuation delegate = null; @@ -23,6 +27,7 @@ public NRContinuationWrapper(Continuation d, String n) { } } + @NotNull @Override public CoroutineContext getContext() { return delegate.getContext(); @@ -30,7 +35,7 @@ public CoroutineContext getContext() { @Override @Trace(async=true) - public void resumeWith(Object p0) { + public void resumeWith(@NotNull Object p0) { String contString = Utils.getContinuationString(delegate); if(contString != null && !contString.isEmpty()) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",contString); diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRCoroutineToken.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRCoroutineToken.java deleted file mode 100644 index 93dcdac..0000000 --- a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRCoroutineToken.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_19; - -import com.newrelic.api.agent.Token; - -import kotlin.coroutines.AbstractCoroutineContextElement; -import kotlin.coroutines.CoroutineContext; - -public class NRCoroutineToken extends AbstractCoroutineContextElement - { - public static Key key = new Key(); - - public NRCoroutineToken(Token t) { - super(key); - token = t; - } - - private Token token = null; - - public static final class Key implements CoroutineContext.Key { - private Key() {} - } - - public Token getToken() { - return token; - } - - @Override - public int hashCode() { - return token.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(this != obj ) { - if(obj instanceof NRCoroutineToken) { - NRCoroutineToken t = (NRCoroutineToken)obj; - return t.token == token; - } - } else { - return true; - } - return false; - } - - @Override - public String toString() { - return "NRCoroutineToken"; - } - - -} diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRDelayCancellableContinuation.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRDelayCancellableContinuation.java new file mode 100644 index 0000000..9ea9be0 --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRDelayCancellableContinuation.java @@ -0,0 +1,159 @@ +package com.newrelic.instrumentation.kotlin.coroutines_19; + +import kotlin.jvm.functions.Function3; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; + +import kotlin.Unit; +import kotlin.coroutines.CoroutineContext; +import kotlin.jvm.functions.Function1; +import kotlinx.coroutines.CancellableContinuation; +import kotlinx.coroutines.CoroutineDispatcher; + +/* + * Used to wrap the tracking of a call to the Coroutine delay function if tracking is enabled + * Will report the delay as a segment + */ +public class NRDelayCancellableContinuation implements CancellableContinuation { + + private final CancellableContinuation delegate; + private Segment segment; + + public NRDelayCancellableContinuation(CancellableContinuation delegate, String type) { + this.delegate = delegate; + String name = Utils.getContinuationString(delegate); + segment = NewRelic.getAgent().getTransaction().startSegment(type); + segment.addCustomAttribute("CancellableContinuation", name); + } + + @Override + public void resumeWith(@NotNull Object o) { + if(segment != null) { + segment.end(); + segment = null; + } + if(delegate != null) { + delegate.resumeWith(o); + } + } + + @Override + public @NotNull CoroutineContext getContext() { + return delegate.getContext(); + } + + @Override + public boolean isActive() { + return delegate.isActive(); + } + + @Override + public boolean isCompleted() { + return delegate.isCompleted(); + } + + @Override + public boolean isCancelled() { + return delegate.isCancelled(); + } + + @Override + public @Nullable Object tryResume(T t, @Nullable Object o) { + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.tryResume(t,o); + } + + @Override + public @Nullable Object tryResumeWithException(@NotNull Throwable throwable) { + NewRelic.noticeError(throwable); + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.tryResumeWithException(throwable); + } + + @Override + public void completeResume(@NotNull Object o) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.completeResume(o); + } + + @Override + public void initCancellability() { + delegate.initCancellability(); + } + + @Override + public boolean cancel(@Nullable Throwable throwable) { + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.cancel(throwable); + } + + @Override + public void invokeOnCancellation(@NotNull Function1 function1) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.invokeOnCancellation(function1); + } + + @Override + public void resumeUndispatched(@NotNull CoroutineDispatcher coroutineDispatcher, T t) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resumeUndispatched(coroutineDispatcher, t); + } + + @Override + public void resumeUndispatchedWithException(@NotNull CoroutineDispatcher coroutineDispatcher, @NotNull Throwable throwable) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resumeUndispatchedWithException(coroutineDispatcher, throwable); + } + + @SuppressWarnings("deprecation") + @Override + public void resume(T t, @Nullable Function1 function1) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resume(t,function1); + } + + @Override + public @Nullable Object tryResume(R r, @Nullable Object o,@Nullable Function3 function3) { + if(segment != null) { + segment.end(); + segment = null; + } + return delegate.tryResume(r,o,function3); + } + + @Override + public void resume(R r, @Nullable Function3 function3) { + if(segment != null) { + segment.end(); + segment = null; + } + delegate.resume(r,function3); + } +} diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRDelayContinuation.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRDelayContinuation.java new file mode 100644 index 0000000..168439f --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRDelayContinuation.java @@ -0,0 +1,38 @@ +package com.newrelic.instrumentation.kotlin.coroutines_19; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; +import kotlinx.coroutines.CancellableContinuation; +import org.jetbrains.annotations.NotNull; + +public class NRDelayContinuation implements Continuation { + + private final Continuation delegate; + private final Segment segment; + + public NRDelayContinuation(Continuation delegate, String type) { + this.delegate = delegate; + String name = Utils.getContinuationString(delegate); + segment = NewRelic.getAgent().getTransaction().startSegment(type); + segment.addCustomAttribute("Continuation", name); + boolean isCancellable = delegate instanceof CancellableContinuation; + segment.addCustomAttribute("isCancellable", isCancellable); + } + + @Override + public void resumeWith(@NotNull Object o) { + if(segment != null) { + segment.end(); + } + if(delegate != null) { + delegate.resumeWith(o); + } + } + + @Override + public @NotNull CoroutineContext getContext() { + return delegate.getContext(); + } +} diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1SuspendWrapper.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1SuspendWrapper.java new file mode 100644 index 0000000..7339257 --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1SuspendWrapper.java @@ -0,0 +1,41 @@ +package com.newrelic.instrumentation.kotlin.coroutines_19; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import kotlin.coroutines.Continuation; +import kotlin.jvm.functions.Function1; + +/* +* Used to wrap a suspend function that was used as an input as a parameter to the +* Coroutine functions runBlocking, async, invoke, launch and withContext +*/ +public class NRFunction1SuspendWrapper implements Function1 { + + private Function1 delegate = null; + private static boolean isTransformed = false; + + public NRFunction1SuspendWrapper(Function1 d) { + delegate = d; + if(!isTransformed) { + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + isTransformed = true; + } + } + + @Override + public R invoke(P1 p1) { + if(p1 instanceof Continuation) { + Continuation cont = (Continuation)p1; + + String cont_string = Utils.getContinuationString(cont); + if(cont_string != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",cont_string); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction","UnknownSource"); + + } + } + return delegate != null ? delegate.invoke(p1) : null; + } + +} diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1Wrapper.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1Wrapper.java index f3f3038..5ae7500 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1Wrapper.java +++ b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1Wrapper.java @@ -1,7 +1,6 @@ package com.newrelic.instrumentation.kotlin.coroutines_19; import com.newrelic.agent.bridge.AgentBridge; - import kotlin.coroutines.Continuation; import kotlin.coroutines.jvm.internal.SuspendFunction; import kotlin.jvm.functions.Function1; @@ -24,9 +23,9 @@ public NRFunction1Wrapper(Function1 d) { public R invoke(P1 p1) { if(p1 instanceof Continuation && !(p1 instanceof SuspendFunction)) { // wrap if needed - if(!(p1 instanceof NRContinuationWrapper)) { - String cont_string = Utils.getContinuationString((Continuation)p1); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p1, cont_string); + Continuation continuation = (Continuation) p1; + if(!(p1 instanceof NRContinuationWrapper) && Utils.continueWithContinuation(continuation)) { + NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation) p1, Utils.getContinuationString(continuation)); p1 = (P1) wrapper; } } diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2SuspendWrapper.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2SuspendWrapper.java new file mode 100644 index 0000000..316ce8c --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2SuspendWrapper.java @@ -0,0 +1,66 @@ +package com.newrelic.instrumentation.kotlin.coroutines_19; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; +import kotlin.jvm.functions.Function2; +import kotlinx.coroutines.CoroutineScope; + +/* + * Used to wrap a suspend function that was used as an input as a parameter to the + * Coroutine functions runBlocking, async, invoke, launch and withContext + */ +public class NRFunction2SuspendWrapper implements Function2 { + + private Function2 delegate = null; + private static boolean isTransformed = false; + + public NRFunction2SuspendWrapper(Function2 d) { + delegate = d; + if(!isTransformed) { + isTransformed = true; + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + } + } + + @Override + public R invoke(S s, T t) { + + // set name + boolean name_set = false; + if(s instanceof CoroutineScope) { + CoroutineScope scope = (CoroutineScope)s; + CoroutineContext ctx = scope.getCoroutineContext(); + Token token = Utils.getToken(ctx); + if(token != null) { + token.link(); + } + CoroutineContext context = scope.getCoroutineContext(); + String coroutineName = Utils.getCoroutineName(context); + if(coroutineName != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",coroutineName); + name_set = true; + } + + } + + if(!name_set && t instanceof Continuation) { + Continuation cont = (Continuation)t; + + String cont_string = Utils.getContinuationString(cont); + if(cont_string != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",cont_string); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction","UnknownSource"); + + } + } + if(delegate != null) { + return delegate.invoke(s, t); + } + return null; + } + +} diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2Wrapper.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2Wrapper.java deleted file mode 100644 index fce671a..0000000 --- a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2Wrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_19; - -import com.newrelic.agent.bridge.AgentBridge; - -import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function2; - -public class NRFunction2Wrapper implements Function2 { - - private Function2 delegate = null; - private static boolean isTransformed = false; - - public NRFunction2Wrapper(Function2 d) { - delegate = d; - if(!isTransformed) { - isTransformed = true; - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public R invoke(P1 p1, P2 p2) { - - if(p2 instanceof Continuation && !(p2 instanceof SuspendFunction)) { - // wrap if needed - String cont_string = Utils.getContinuationString((Continuation)p2); - if(!(p2 instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(cont_string)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p2, cont_string); - p2 = (P2) wrapper; - } - } - if(delegate != null) { - return delegate.invoke(p1, p2); - } - return null; - } - -} diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRRunnable.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRRunnable.java index 233dd62..b7b1a9c 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRRunnable.java +++ b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRRunnable.java @@ -4,7 +4,6 @@ import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; - import kotlin.coroutines.Continuation; import kotlinx.coroutines.DispatchedTask; @@ -30,13 +29,11 @@ public void run() { if(delegate != null && delegate instanceof DispatchedTask) { DispatchedTask task = (DispatchedTask)delegate; Continuation cont_delegate = task.getDelegate$kotlinx_coroutines_core(); - if(cont_delegate != null) { - String cont_string = Utils.getContinuationString(cont_delegate); - if(cont_string == null) cont_string = cont_delegate.getClass().getName(); - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","DispatchedTask",Utils.getContinuationString(cont_delegate)); - nameSet = true; - } - } + String cont_string = Utils.getContinuationString(cont_delegate); + if(cont_string == null) cont_string = cont_delegate.getClass().getName(); + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","DispatchedTask",cont_string); + nameSet = true; + } if(!nameSet) { String delegateType = delegate != null ? delegate.getClass().getName() : "null"; NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AsyncRunnableWrapper",delegateType); diff --git a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/Utils.java b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/Utils.java index 2029f33..7c0c694 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/Utils.java +++ b/Kotlin-Coroutines_1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/Utils.java @@ -15,7 +15,6 @@ import kotlin.coroutines.CoroutineContext; import kotlin.coroutines.jvm.internal.BaseContinuationImpl; import kotlinx.coroutines.AbstractCoroutine; -import kotlinx.coroutines.CoroutineName; import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.DispatchedTask; @@ -27,7 +26,7 @@ public class Utils implements AgentConfigListener { private static final String SCOPESIGNORECONFIG = "Coroutines.ignores.scopes"; private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched"; private static final String DELAYED_ENABLED_CONFIG = "Coroutines.delayed.enabled"; - + public static final String CREATEMETHOD1 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4"; public static final String CREATEMETHOD2 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3"; private static final Utils INSTANCE = new Utils(); @@ -48,9 +47,9 @@ public class Utils implements AgentConfigListener { DELAYED_ENABLED = Boolean.valueOf(value.toString()); } } - + } - + public static NRRunnable getRunnableWrapper(Runnable r) { if(r instanceof NRRunnable) { return null; @@ -65,7 +64,7 @@ public static NRRunnable getRunnableWrapper(Runnable r) { } } } - + Token t = NewRelic.getAgent().getTransaction().getToken(); if(t != null && t.isActive()) { return new NRRunnable(r, t); @@ -75,14 +74,14 @@ public static NRRunnable getRunnableWrapper(Runnable r) { } return null; } - + private static void loadConfig(Config config) { String ignores = config.getValue(CONTIGNORECONFIG); NewRelic.getAgent().getLogger().log(Level.FINE, "Value of {0}: {1}", CONTIGNORECONFIG, ignores); if (ignores != null && !ignores.isEmpty()) { ignoredContinuations.clear(); String[] ignoresList = ignores.split(","); - + for(String ignore : ignoresList) { if (!ignoredContinuations.contains(ignore)) { ignoredContinuations.add(ignore); @@ -102,7 +101,7 @@ private static void loadConfig(Config config) { if (ignores != null && !ignores.isEmpty()) { ignoredScopes.clear(); String[] ignoresList = ignores.split(","); - + for(String ignore : ignoresList) { if (!ignoredScopes.contains(ignore)) { ignoredScopes.add(ignore); @@ -112,61 +111,103 @@ private static void loadConfig(Config config) { } else if(!ignoredScopes.isEmpty()) { ignoredScopes.clear(); } - + } - + public static boolean ignoreScope(CoroutineScope scope) { CoroutineContext ctx = scope.getCoroutineContext(); String name = getCoroutineName(ctx); String className = scope.getClass().getName(); return ignoreScope(className) || ignoreScope(name); } - + public static boolean ignoreScope(String coroutineScope) { return ignoredScopes.contains(coroutineScope); } - + public static boolean ignoreContinuation(String cont_string) { return ignoredContinuations.contains(cont_string); } - + + /* + * Allows certain Coroutine scopes to be ignored + * coroutineScope can be a Coroutine name or CoroutineScope class name + */ + public static boolean continueWithScope(CoroutineScope scope) { + CoroutineContext ctx = scope.getCoroutineContext(); + String name = getCoroutineName(ctx); + String className = scope.getClass().getName(); + return continueWithScope(className) && continueWithScope(name); + } + + /* + * Allows certain Coroutine scopes to be ignored + * coroutineScope can be a Coroutine name or CoroutineScope class name + */ + public static boolean continueWithScope(String coroutineScope) { + for(String ignoredScope : ignoredScopes) { + if(coroutineScope.matches(ignoredScope)) { + return false; + } + } + return !ignoredScopes.contains(coroutineScope); + } + + public static boolean continueWithContinuation(Continuation continuation) { + /* + * Don't trace internal Coroutines Continuations + */ + String className = continuation.getClass().getName(); + if(className.startsWith("kotlin")) return false; + + /* + * Get the continuation string and check if it should be ignored + */ + String cont_string = getContinuationString(continuation); + if(cont_string == null) { return false; } + + for(String ignored : ignoredContinuations) { + if(cont_string.matches(ignored)) { + return false; + } + } + return !ignoredContinuations.contains(cont_string); + } + + + public static String sub = "createCoroutineFromSuspendFunction"; - public static NRCoroutineToken setToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken == null) { + public static void setToken(CoroutineContext context) { + TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); + if (tokenContext == null) { Token t = NewRelic.getAgent().getTransaction().getToken(); if(t != null && t.isActive()) { - coroutineToken = new NRCoroutineToken(t); - return coroutineToken; + NRTokenContextKt.addTokenContext(context, t); } else if(t != null) { t.expire(); t = null; } } - return null; } public static Token getToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - Token token = null; - if(coroutineToken != null) { - token = coroutineToken.getToken(); + TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); + if(tokenContext != null) { + return tokenContext.getToken(); } - - return token; + return null; } public static void expireToken(CoroutineContext context) { - NRCoroutineToken coroutineToken = context.get(NRCoroutineToken.key); - if(coroutineToken != null) { - Token token = coroutineToken.getToken(); + TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); + if(tokenContext != null) { + Token token = tokenContext.getToken(); token.expire(); - context.minusKey(NRCoroutineToken.key); + NRTokenContextKt.removeTokenContext(context); } } - - @SuppressWarnings("unchecked") + public static String getCoroutineName(CoroutineContext context, Continuation continuation) { if(continuation instanceof AbstractCoroutine) { return ((AbstractCoroutine)continuation).nameString$kotlinx_coroutines_core(); @@ -178,13 +219,7 @@ public static String getCoroutineName(CoroutineContext context, Continuation } public static String getCoroutineName(CoroutineContext context) { - CoroutineName cName = context.get(CoroutineName.Key); - if(cName != null) { - String name = cName.getName(); - if(name != null && !name.isEmpty()) return name; - } - - return null; + return CoroutineNameUtilsKt.getCoroutineName(context); } @Override @@ -199,27 +234,27 @@ public void configChanged(String appName, AgentConfig agentConfig) { } } } - + public static String getContinuationString(Continuation continuation) { String contString = continuation.toString(); - + if(contString.equals(CREATEMETHOD1) || contString.equals(CREATEMETHOD2)) { return sub; } - + if(contString.startsWith(CONT_LOC)) { return contString; } - + if(continuation instanceof AbstractCoroutine) { return ((AbstractCoroutine)continuation).nameString$kotlinx_coroutines_core(); } - + int index = contString.indexOf('@'); if(index > -1) { return contString.substring(0, index); } - + return null; } } diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlin/coroutines/ContinuationKt.java b/Kotlin-Coroutines_1.9/src/main/java/kotlin/coroutines/ContinuationKt.java deleted file mode 100644 index 16b73ad..0000000 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlin/coroutines/ContinuationKt.java +++ /dev/null @@ -1,34 +0,0 @@ -package kotlin.coroutines; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2Wrapper; - -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; - -@Weave -public abstract class ContinuationKt { - - @Trace - public static void startCoroutine(Function1, ? extends Object> f, Continuation cont ) { - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper, ? extends Object> wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - @Trace - public static void startCoroutine(Function2, ? extends Object> f, R receiver, Continuation cont) { - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; - } - Weaver.callOriginal(); - } - - -} diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java b/Kotlin-Coroutines_1.9/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java new file mode 100644 index 0000000..83a9451 --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java @@ -0,0 +1,32 @@ +package kotlin.coroutines; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction1SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2SuspendWrapper; + +import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function2; + +@Weave(originalName = "kotlin.coroutines.ContinuationKt") +public abstract class ContinuationKt_Instrumentation { + + @Trace + public static void startCoroutine(Function1, ?> f, Continuation cont ) { + if(!(f instanceof NRFunction1SuspendWrapper)) { + f = new NRFunction1SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + @Trace + public static void startCoroutine(Function2, ?> f, R receiver, Continuation cont) { + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); + } + Weaver.callOriginal(); + } + + +} diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java similarity index 84% rename from Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java index 54c8e52..94b2603 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java @@ -7,15 +7,15 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2Wrapper; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2SuspendWrapper; import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; import kotlin.coroutines.Continuation; import kotlin.coroutines.CoroutineContext; import kotlin.jvm.functions.Function2; -@Weave(type=MatchType.BaseClass) -public abstract class AbstractCoroutine { +@Weave(type=MatchType.BaseClass, originalName = "kotlinx.coroutines.AbstractCoroutine") +public abstract class AbstractCoroutine_Instrumentation { public abstract CoroutineContext getContext(); public abstract String nameString$kotlinx_coroutines_core(); @@ -35,10 +35,9 @@ protected void onCancelled(Throwable t, boolean b) { } @Trace - public void start(CoroutineStart start, R receiver, Function2, ? extends Object> block) { - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper<>(block); - block = wrapper; + public void start(CoroutineStart start, R receiver, Function2, ?> block) { + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = new NRFunction2SuspendWrapper<>(block); } String ctxName = Utils.getCoroutineName(getContext()); String name = ctxName != null ? ctxName : nameString$kotlinx_coroutines_core(); diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AwaitAll.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java similarity index 76% rename from Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AwaitAll.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java index 81064be..9ef878e 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/AwaitAll.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java @@ -9,14 +9,13 @@ import kotlin.coroutines.Continuation; -@Weave -abstract class AwaitAll { +@Weave(originalName = "kotlinx.coroutines.AwaitAll") +abstract class AwaitAll_Instrumentation { @Trace public Object await(Continuation> cont) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AwaitAll","await"); - Object result = Weaver.callOriginal(); - return result; + return Weaver.callOriginal(); } } diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/BuildersKt.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/BuildersKt.java deleted file mode 100644 index e61df65..0000000 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/BuildersKt.java +++ /dev/null @@ -1,179 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.newrelic.instrumentation.kotlin.coroutines_19.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_19.NRCoroutineToken; -import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; - -import kotlin.Unit; -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; -import kotlin.coroutines.jvm.internal.SuspendFunction; -import kotlin.jvm.functions.Function2; - -@SuppressWarnings({ "unchecked", "rawtypes" }) -@Weave -public class BuildersKt { - - @Trace(dispatcher = true) - public static final T runBlocking(CoroutineContext context, Function2, ? extends Object> block) { - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - String name = Utils.getCoroutineName(context); - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - - if (!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - T t = Weaver.callOriginal(); - return t; - } - - @Trace(dispatcher = true) - public static final Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if (!Utils.ignoreScope(scope)) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); - String name = Utils.getCoroutineName(context); - if(name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); - } - - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - } else { - NewRelic.getAgent().getTransaction().ignore(); - } - return Weaver.callOriginal(); - } - - @Trace(dispatcher = true) - public static final Object invoke(CoroutineDispatcher dispatcher, Function2, ? extends Object> block, Continuation c) { - - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", c.toString()); - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - if(c != null && !Utils.ignoreContinuation(c.toString())) { - boolean isSuspend = c instanceof SuspendFunction; - if(!isSuspend) { - String cont_string = Utils.getContinuationString(c); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(c, cont_string); - c = wrapper; - } - } - Object t = Weaver.callOriginal(); - return t; - } - - @Trace(dispatcher = true) - public static final kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ? extends Object> block) { - if (!Utils.ignoreScope(scope)) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); - - String name = Utils.getCoroutineName(context); - if (name == null) { - name = Utils.getCoroutineName(scope.getCoroutineContext()); - } - if (name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); - NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); - } - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if (!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper( - block); - block = wrapper; - } - } else { - NewRelic.getAgent().getTransaction().ignore(); - } - Job j = Weaver.callOriginal(); - return j; - } - - @Trace(dispatcher = true) - public static final Object withContext(CoroutineContext context,Function2, ? extends Object> block, Continuation completion) { - String name = Utils.getCoroutineName(context,completion); - if(name != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext",name); - } else { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext"); - } - if(completion != null) { - NewRelic.getAgent().getTracedMethod().addCustomAttribute("Completion", completion.toString()); - } - - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } else { - NRCoroutineToken nrContextToken = Utils.setToken(context); - if(nrContextToken != null) { - context = context.plus(nrContextToken); - } - } - if(!(block instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper, ? extends Object> wrapper = new NRFunction2Wrapper(block); - block = wrapper; - } - if(completion != null && !Utils.ignoreContinuation(completion.toString())) { - if(!(completion instanceof NRContinuationWrapper)) { - String cont_string = Utils.getContinuationString(completion); - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(completion, cont_string); - completion = wrapper; - } - } - return Weaver.callOriginal(); - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java new file mode 100644 index 0000000..b9429e9 --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java @@ -0,0 +1,138 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRContinuationWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; + +import kotlin.Unit; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; +import kotlin.coroutines.jvm.internal.SuspendFunction; +import kotlin.jvm.functions.Function2; + +@SuppressWarnings({ "unchecked", "rawtypes" }) +@Weave(originalName = "kotlinx.coroutines.BuildersKt") +public class BuildersKt_Instrumentation { + + @Trace(dispatcher = true) + public static T runBlocking(CoroutineContext context, Function2, ?> block) { + String name = Utils.getCoroutineName(context); + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking",name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking"); + } + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); + + if (!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, + Function2, ?> block) { + if (Utils.continueWithScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-CoroutineName", Utils.getCoroutineName(scope.getCoroutineContext())); + + String name = Utils.getCoroutineName(context); + if(name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); + } + + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + } else { + NewRelic.getAgent().getTransaction().ignore(); + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static Object invoke(CoroutineDispatcher_Instrumentation dispatcher, + Function2, ?> block, Continuation cont) { + + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", cont.toString()); + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + if(Utils.continueWithContinuation(cont)) { + boolean isSuspend = cont instanceof SuspendFunction; + if(!isSuspend) { + String cont_string = Utils.getContinuationString(cont); + cont = new NRContinuationWrapper<>(cont, cont_string); + } + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, + Function2, ?> block) { + if (Utils.continueWithScope(scope)) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-CoroutineName", Utils.getCoroutineName(scope.getCoroutineContext())); + + String name = Utils.getCoroutineName(context); + if (name == null) { + name = Utils.getCoroutineName(scope.getCoroutineContext()); + } + if (name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch", name); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Builders", "launch"); + NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); + } + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); + if (!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper( + block); + } + } else { + NewRelic.getAgent().getTransaction().ignore(); + } + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public static Object withContext(CoroutineContext context, Function2, ?> block, + Continuation completion) { + String name = Utils.getCoroutineName(context,completion); + if(name != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext",name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","withContext"); + } + if(completion != null) { + NewRelic.getAgent().getTracedMethod().addCustomAttribute("Completion", completion.toString()); + } + + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + } + if(completion != null && Utils.continueWithContinuation(completion)) { + if(!(completion instanceof NRContinuationWrapper)) { + String cont_string = Utils.getContinuationString(completion); + completion = new NRContinuationWrapper<>(completion, cont_string); + } + } + return Weaver.callOriginal(); + } +} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineDispatcher_Instrumentation.java similarity index 64% rename from Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineDispatcher_Instrumentation.java index 8e0220f..55726db 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineDispatcher.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineDispatcher_Instrumentation.java @@ -8,8 +8,12 @@ import kotlin.coroutines.CoroutineContext; -@Weave(type = MatchType.BaseClass) -public abstract class CoroutineDispatcher { +/* + * Dispatchers are used to dispatch tasks to another thread. By wrapping the Runable + * we can track the Coroutine across threads + */ +@Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.CoroutineDispatcher") +public abstract class CoroutineDispatcher_Instrumentation { public void dispatch(CoroutineContext ctx, Runnable r) { NRRunnable wrapper = Utils.getRunnableWrapper(r); diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java deleted file mode 100644 index 6419a85..0000000 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java +++ /dev/null @@ -1,17 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(type = MatchType.Interface) -public abstract class CoroutineExceptionHandler { - - @Trace - public void handleException(kotlin.coroutines.CoroutineContext ctx, java.lang.Throwable t) { - NewRelic.noticeError(t); - Weaver.callOriginal(); - } -} diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerImplKt.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerImplKt.java deleted file mode 100644 index f025bac..0000000 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerImplKt.java +++ /dev/null @@ -1,15 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.weaver.SkipIfPresent; - -/** - * Included in order to avoid loading in lower versions - * - * @author dhilpipre - * - */ - -@SkipIfPresent -public class CoroutineExceptionHandlerImplKt { - -} diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerImplKt_Instrumentation.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerImplKt_Instrumentation.java new file mode 100644 index 0000000..12aaf9f --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerImplKt_Instrumentation.java @@ -0,0 +1,15 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +/** + * Included to avoid loading in lower versions + * + * @author dhilpipre + * + */ + +@SkipIfPresent(originalName = "kotlinx.coroutines.CoroutineExceptionHandlerImplKt") +public class CoroutineExceptionHandlerImplKt_Instrumentation { + +} diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt_Instrumentation.java similarity index 67% rename from Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt_Instrumentation.java index 97fc24b..393f625 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandlerKt_Instrumentation.java @@ -4,8 +4,8 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -@Weave -public abstract class CoroutineExceptionHandlerKt { +@Weave(originalName = "kotlinx.coroutines.CoroutineExceptionHandlerKt") +public abstract class CoroutineExceptionHandlerKt_Instrumentation { @Trace public static void handleCoroutineException(kotlin.coroutines.CoroutineContext ctx,Throwable t) { diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler_Instrumentation.java similarity index 71% rename from Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler_Instrumentation.java index 6419a85..9aa2936 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/CoroutineExceptionHandler_Instrumentation.java @@ -6,8 +6,8 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -@Weave(type = MatchType.Interface) -public abstract class CoroutineExceptionHandler { +@Weave(type = MatchType.Interface, originalName = "kotlinx.coroutines.CoroutineExceptionHandler") +public abstract class CoroutineExceptionHandler_Instrumentation { @Trace public void handleException(kotlin.coroutines.CoroutineContext ctx, java.lang.Throwable t) { diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/DelayKt_Instrumentation.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/DelayKt_Instrumentation.java new file mode 100644 index 0000000..a6c0ca0 --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/DelayKt_Instrumentation.java @@ -0,0 +1,30 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRDelayContinuation; +import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; +import kotlin.Unit; +import kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.DelayKt") +public class DelayKt_Instrumentation { + + @Trace + public static Object awaitCancellation(Continuation cont) { + if(Utils.DELAYED_ENABLED && !(cont instanceof NRDelayContinuation)) { + cont = new NRDelayContinuation<>(cont,"AwaitCancellation"); + } + return Weaver.callOriginal(); + } + + @Trace + public static Object delay(long timeMillis, Continuation cont) { + if(Utils.DELAYED_ENABLED && !(cont instanceof NRDelayContinuation)) { + cont = new NRDelayContinuation<>(cont,"Delay"); + } + return Weaver.callOriginal(); + } + +} diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/Delay_Instrumentation.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/Delay_Instrumentation.java new file mode 100644 index 0000000..2137b85 --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/Delay_Instrumentation.java @@ -0,0 +1,37 @@ +package kotlinx.coroutines; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRDelayCancellableContinuation; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRDelayContinuation; +import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; +import kotlin.Unit; +import kotlin.coroutines.Continuation; +import kotlin.coroutines.CoroutineContext; + +@Weave(type = MatchType.Interface, originalName = "kotlinx.coroutines.Delay") +public class Delay_Instrumentation { + + @Trace + public Object delay(long timeMills, Continuation continuation) { + if(Utils.DELAYED_ENABLED && !(continuation instanceof NRDelayContinuation)) { + continuation = new NRDelayContinuation<>(continuation,"Delay"); + } + return Weaver.callOriginal(); + } + + @Trace + public void scheduleResumeAfterDelay(long timeMills, CancellableContinuation continuation) { + if(Utils.DELAYED_ENABLED && !(continuation instanceof NRDelayContinuation)) { + continuation = new NRDelayCancellableContinuation<>(continuation,"scheduleResumeAfterDelay"); + } + Weaver.callOriginal(); + } + + @Trace + public DisposableHandle invokeOnTimeout(long timeMills, Runnable r, CoroutineContext context) { + return Weaver.callOriginal(); + } +} diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/DispatcherExecutor.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/DispatcherExecutor_Instrumentation.java similarity index 82% rename from Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/DispatcherExecutor.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/DispatcherExecutor_Instrumentation.java index 939567d..d082b10 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/DispatcherExecutor.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/DispatcherExecutor_Instrumentation.java @@ -8,8 +8,8 @@ import com.newrelic.instrumentation.kotlin.coroutines_19.NRRunnable; import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; -@Weave(type = MatchType.BaseClass) -abstract class DispatcherExecutor { +@Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.DispatcherExecutor") +abstract class DispatcherExecutor_Instrumentation { @Trace public void execute(Runnable r) { diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java similarity index 55% rename from Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java index 2847ecb..76abc18 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java @@ -9,13 +9,13 @@ import com.newrelic.api.agent.weaver.Weaver; import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; -@Weave -public abstract class EventLoopImplBase { +@Weave(originalName = "kotlinx.coroutines.EventLoopImplBase") +public abstract class EventLoopImplBase_Instrumentation { - @Weave(type = MatchType.BaseClass) - public static abstract class DelayedTask { + @Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedTask") + public static abstract class DelayedTask_Instrumentation { - public DelayedTask(long nanos) { + public DelayedTask_Instrumentation(long nanos) { if(Utils.DELAYED_ENABLED) { token = NewRelic.getAgent().getTransaction().getToken(); } @@ -35,11 +35,11 @@ public void dispose() { } } - @Weave - static class DelayedResumeTask extends DelayedTask { + @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask") + static class DelayedResumeTask_Instrumentation extends DelayedTask_Instrumentation { - public DelayedResumeTask(long nanos, kotlinx.coroutines.CancellableContinuation cont) { + public DelayedResumeTask_Instrumentation(long nanos, kotlinx.coroutines.CancellableContinuation cont) { super(nanos); } @@ -55,10 +55,10 @@ public void run() { } } - @Weave - static class DelayedRunnableTask extends DelayedTask { + @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask") + static class DelayedRunnableTask_Instrumentation extends DelayedTask_Instrumentation { - public DelayedRunnableTask(long nanos, Runnable r) { + public DelayedRunnableTask_Instrumentation(long nanos, Runnable r) { super(nanos); } @@ -73,8 +73,8 @@ public void run() { } } - @Weave - public static class DelayedTaskQueue { + @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedTaskQueue") + public static class DelayedTaskQueue_Instrumentation { } } diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java deleted file mode 100644 index 0f9ec41..0000000 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java +++ /dev/null @@ -1,34 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave -abstract class ResumeUndispatchedRunnable { - - @NewField - private Token token = null; - - public ResumeUndispatchedRunnable(CoroutineDispatcher dispatcher, CancellableContinuation cont) { - Token t = NewRelic.getAgent().getTransaction().getToken(); - if(t != null && t.isActive()) { - token = t; - } else if(t != null) { - t.expire(); - t = null; - } - } - - @Trace(async = true) - public void run() { - if(token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } -} diff --git a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java similarity index 69% rename from Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java index 0f9ec41..817a87d 100644 --- a/Kotlin-Coroutines_1.4/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java @@ -7,13 +7,13 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -@Weave -abstract class ResumeUndispatchedRunnable { +@Weave(originalName = "kotlinx.coroutines.ResumeUndispatchedRunnable") +abstract class ResumeUndispatchedRunnable_Instrumentation { @NewField private Token token = null; - public ResumeUndispatchedRunnable(CoroutineDispatcher dispatcher, CancellableContinuation cont) { + public ResumeUndispatchedRunnable_Instrumentation(CoroutineDispatcher_Instrumentation dispatcher, CancellableContinuation cont) { Token t = NewRelic.getAgent().getTransaction().getToken(); if(t != null && t.isActive()) { token = t; diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/TimeSource.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/TimeSource.java deleted file mode 100644 index 98780e5..0000000 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/TimeSource.java +++ /dev/null @@ -1,16 +0,0 @@ -package kotlinx.coroutines; - -import com.newrelic.api.agent.weaver.SkipIfPresent; - -/** - * Included to avoid loading in version 1.4.x - * Only difference between 1.5.x and 1.4.x is one method in AbstractCoroutine that is in 1.4.x but not 1.5.x - * TimeSource is only 1.4.x versions - * - * @author dhilpipre - * - */ -@SkipIfPresent -public class TimeSource { - -} diff --git a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/TimeSource.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/TimeSource_Instrumentation.java similarity index 74% rename from Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/TimeSource.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/TimeSource_Instrumentation.java index 98780e5..5cff915 100644 --- a/Kotlin-Coroutines_1.5/src/main/java/kotlinx/coroutines/TimeSource.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/TimeSource_Instrumentation.java @@ -10,7 +10,7 @@ * @author dhilpipre * */ -@SkipIfPresent -public class TimeSource { +@SkipIfPresent(originalName = "kotlinx.coroutines.TimeSource") +public class TimeSource_Instrumentation { } diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/YieldKt.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/YieldKt_Instrumentation.java similarity index 87% rename from Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/YieldKt.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/YieldKt_Instrumentation.java index 4d4900a..b466f44 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/YieldKt.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/YieldKt_Instrumentation.java @@ -9,8 +9,8 @@ import kotlin.Unit; import kotlin.coroutines.Continuation; -@Weave -public class YieldKt { +@Weave(originalName = "kotlinx.coroutines.YieldKt") +public class YieldKt_Instrumentation { @Trace public static Object yield(Continuation cont) { diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java new file mode 100644 index 0000000..8fe0d9b --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java @@ -0,0 +1,34 @@ +package kotlinx.coroutines.channels; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import kotlin.Unit; +import kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.channels.BufferedChannel") +public abstract class BufferedChannel_Instrumentation { + + @Trace(dispatcher = true) + public Object receive(Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"receive"); + + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public void cancel(java.util.concurrent.CancellationException ex) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"Cancel"); + + Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public Object send(E e, Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"send"); + return Weaver.callOriginal(); + } + + +} diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java new file mode 100644 index 0000000..51cacb5 --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java @@ -0,0 +1,35 @@ +package kotlinx.coroutines.channels; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +import kotlin.Unit; +import kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.channels.ChannelCoroutine") +public abstract class ChannelCoroutine_Instrumentation { + + @Trace(dispatcher = true) + public Object receive(Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"receive"); + + return Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public void cancel(java.util.concurrent.CancellationException ex) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"Cancel"); + + Weaver.callOriginal(); + } + + @Trace(dispatcher = true) + public Object send(E e, Continuation cont) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"send"); + return Weaver.callOriginal(); + } + + +} diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java similarity index 58% rename from Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java index 23efac0..46c02ce 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java @@ -6,8 +6,8 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.newrelic.instrumentation.kotlin.coroutines_19.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction1Wrapper; -import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2Wrapper; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction1SuspendWrapper; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2SuspendWrapper; import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; import kotlin.coroutines.Continuation; @@ -15,38 +15,34 @@ import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; -@Weave -public abstract class CancellableKt { +@Weave(originalName = "kotlinx.coroutines.intrinsics.CancellableKt") +public abstract class CancellableKt_Instrumentation { - @SuppressWarnings({ "unchecked", "rawtypes" }) @Trace - public static void startCoroutineCancellable(Function1, ? extends java.lang.Object> f, Continuation cont) { + public static void startCoroutineCancellable(Function1, ?> f, Continuation cont) { String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; + if(!(cont instanceof SuspendFunction)) { + if(!(cont instanceof NRContinuationWrapper) && Utils.continueWithContinuation(cont)) { + cont = new NRContinuationWrapper<>(cont, continuationString); } } if(continuationString != null) { NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); } NewRelic.getAgent().getTracedMethod().addCustomAttribute("Suspend-Type", "Function1"); - if(!(f instanceof NRFunction1Wrapper)) { - NRFunction1Wrapper wrapper = new NRFunction1Wrapper<>(f); - f = wrapper; + if(!(f instanceof NRFunction1SuspendWrapper)) { + f = new NRFunction1SuspendWrapper<>(f); } Weaver.callOriginal(); } @Trace - public static void startCoroutineCancellable(Function2, ? extends java.lang.Object> f, R receiver, Continuation cont) { + public static void startCoroutineCancellable(Function2, ?> f, R receiver, Continuation cont) { String continuationString = Utils.getContinuationString(cont); if(!(cont instanceof SuspendFunction)) { // create continuation wrapper if needed - if(cont != null && !Utils.ignoreContinuation(continuationString) && !(cont instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; + if(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { + cont = new NRContinuationWrapper<>(cont, continuationString); } } if(continuationString != null) { @@ -55,9 +51,8 @@ public static void startCoroutineCancellable(Function2, ? extends java.lang.Object> wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); } Weaver.callOriginal(); } @@ -65,19 +60,17 @@ public static void startCoroutineCancellable(Function2 completion, Continuation cont) { String completionString = Utils.getContinuationString(completion); - if(completion != null && !(completion instanceof SuspendFunction)) { + if(!(completion instanceof SuspendFunction)) { // create continuation wrapper if needed - if(!Utils.ignoreContinuation(completionString) && !(completion instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(completion, completionString); - completion = wrapper; + if(Utils.continueWithContinuation(completion) && !(completion instanceof NRContinuationWrapper)) { + completion = new NRContinuationWrapper<>(completion, completionString); } } String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { + if(!(cont instanceof SuspendFunction)) { // create continuation wrapper if needed - if(cont != null && !Utils.ignoreContinuation(continuationString) && !(cont instanceof NRContinuationWrapper)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; + if(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { + cont = new NRContinuationWrapper<>(cont, continuationString); } } TracedMethod traced = NewRelic.getAgent().getTracedMethod(); diff --git a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java similarity index 52% rename from Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java rename to Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java index 12977d0..6997aff 100644 --- a/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt.java +++ b/Kotlin-Coroutines_1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java @@ -6,7 +6,7 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.newrelic.instrumentation.kotlin.coroutines_19.NRContinuationWrapper; -import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2Wrapper; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2SuspendWrapper; import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; import kotlin.coroutines.Continuation; @@ -14,18 +14,17 @@ import kotlin.jvm.functions.Function2; import kotlinx.coroutines.internal.ScopeCoroutine; -@Weave -public class UndispatchedKt { +@Weave(originalName = "kotlinx.coroutines.intrinsics.UndispatchedKt") +public class UndispatchedKt_Instrumentation { - @SuppressWarnings({ "unchecked", "rawtypes" }) @Trace - public static final void startCoroutineUndispatched(Function2, ? extends Object> f, R receiver, Continuation cont) { + public static void startCoroutineUndispatched(Function2, ?> f, R receiver, + Continuation cont) { String continuationString = Utils.getContinuationString(cont); - if(cont != null && !(cont instanceof SuspendFunction)) { - if(!(cont instanceof NRContinuationWrapper) && !Utils.ignoreContinuation(continuationString)) { - NRContinuationWrapper wrapper = new NRContinuationWrapper<>(cont, continuationString); - cont = wrapper; - } + if(!(cont instanceof SuspendFunction)) { + if(!(cont instanceof NRContinuationWrapper) && Utils.continueWithContinuation(cont)) { + cont = new NRContinuationWrapper<>(cont, continuationString); + } } TracedMethod traced = NewRelic.getAgent().getTracedMethod(); traced.addCustomAttribute("Suspend-Type", "Function2"); @@ -33,37 +32,35 @@ public static final void startCoroutineUndispatched(Function2(f); - f = wrapper; + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); } Weaver.callOriginal(); } - @SuppressWarnings({ "unchecked", "rawtypes" }) @Trace - public static final Object startUndispatchedOrReturn(ScopeCoroutine scope, R receiver, Function2, ? extends Object> f) { + public static Object startUndispatchedOrReturn(ScopeCoroutine scope, R receiver, + Function2, ?> f) { TracedMethod traced = NewRelic.getAgent().getTracedMethod(); traced.addCustomAttribute("Suspend-Type", "Function2"); traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); } return Weaver.callOriginal(); } - @SuppressWarnings({ "unchecked", "rawtypes" }) @Trace - public static final Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutine scope, R receiver, Function2, ? extends Object> f) { + public static Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutine scope, R receiver, + Function2, ?> f) { TracedMethod traced = NewRelic.getAgent().getTracedMethod(); traced.addCustomAttribute("Suspend-Type", "Function2"); traced.addCustomAttribute("Receiver", receiver.getClass().getName()); - if(!(f instanceof NRFunction2Wrapper)) { - NRFunction2Wrapper wrapper = new NRFunction2Wrapper<>(f); - f = wrapper; + if(!(f instanceof NRFunction2SuspendWrapper)) { + f = new NRFunction2SuspendWrapper<>(f); } return Weaver.callOriginal(); } + } diff --git a/Kotlin-Coroutines_1.9/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_19/CoroutineNameUtils.kt b/Kotlin-Coroutines_1.9/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_19/CoroutineNameUtils.kt new file mode 100644 index 0000000..9e730c0 --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_19/CoroutineNameUtils.kt @@ -0,0 +1,6 @@ +package com.newrelic.instrumentation.kotlin.coroutines_19 + +import kotlinx.coroutines.CoroutineName +import kotlin.coroutines.CoroutineContext + +fun CoroutineContext.getCoroutineName(): String? = this[CoroutineName.Key]?.name diff --git a/Kotlin-Coroutines_1.9/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_19/NRTokenContext.kt b/Kotlin-Coroutines_1.9/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_19/NRTokenContext.kt new file mode 100644 index 0000000..22377f9 --- /dev/null +++ b/Kotlin-Coroutines_1.9/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_19/NRTokenContext.kt @@ -0,0 +1,34 @@ +package com.newrelic.instrumentation.kotlin.coroutines_19 + +import kotlin.coroutines.* +import com.newrelic.api.agent.Token +import org.jetbrains.annotations.Nullable + +data class TokenContext(@Nullable var token: Token) + +class TokenContextElement(val context: TokenContext) : AbstractCoroutineContextElement(Key) { + companion object Key : CoroutineContext.Key +} + +fun CoroutineContext.getTokenContext(): TokenContext? = this[TokenContextElement]?.context + +fun CoroutineContext.getTokenContextOrNull(): TokenContext? = this[TokenContextElement]?.context + +fun addTokenContext(context : CoroutineContext, @Nullable token : Token) : CoroutineContext { + val tokenContext = TokenContext(token) + return context + TokenContextElement(tokenContext) +} + +fun removeTokenContext(context : CoroutineContext) : CoroutineContext { + val tokenContext = context.getTokenContextOrNull(); + if (tokenContext != null) { + @Nullable var token = tokenContext.token + token.expire() + + return context.minusKey(TokenContextElement.Key) + } + return context +} + + + diff --git a/Kotlin-Coroutines_1.9/src/test/java/SampleTestTest.kt b/Kotlin-Coroutines_1.9/src/test/java/SampleTestTest.kt deleted file mode 100644 index 87a568a..0000000 --- a/Kotlin-Coroutines_1.9/src/test/java/SampleTestTest.kt +++ /dev/null @@ -1,3 +0,0 @@ -import junit.framework.TestCase - -class SampleTestTest : TestCase() \ No newline at end of file diff --git a/Kotlin-Coroutines_1.9/src/test/kotlin/Sample.kt b/Kotlin-Coroutines_1.9/src/test/kotlin/Sample.kt deleted file mode 100644 index c4e7255..0000000 --- a/Kotlin-Coroutines_1.9/src/test/kotlin/Sample.kt +++ /dev/null @@ -1,6 +0,0 @@ -class Sample() { - - fun sum(a: Int, b: Int) : Int { - return a + b - } -} \ No newline at end of file diff --git a/Kotlin-Coroutines_1.9/src/test/kotlin/SampleTest.kt b/Kotlin-Coroutines_1.9/src/test/kotlin/SampleTest.kt deleted file mode 100644 index a6ea6e9..0000000 --- a/Kotlin-Coroutines_1.9/src/test/kotlin/SampleTest.kt +++ /dev/null @@ -1,14 +0,0 @@ - -import kotlin.test.assertEquals -import org.junit.Test - -internal class SampleTest { - - private val testSample: Sample = Sample() - - @Test - fun testSum() { - val expected = 42 - assertEquals(expected, testSample.sum(40,2)) - } -} \ No newline at end of file diff --git a/README.md b/README.md index 94e4574..12c6ea2 100644 --- a/README.md +++ b/README.md @@ -28,14 +28,16 @@ Kotlin-Coroutines-Suspends - provides tracing of Kotlin Coroutine suspend functi Kotlin-Coroutines-1.0 - all 1.0.x versions. Kotlin-Coroutines-1.1 - all 1.1.x versions. Kotlin-Coroutines-1.2 - all 1.2.x and 1.3.x versions. -Kotlin-Coroutines-1.4 - all 1.4.x versions. -Kotlin-Coroutines-1.5 - all 1.5.x and 1.6.x versions. +Kotlin-Coroutines-1.4 - all 1.4.x, 1.5.x and 1.6.x versions. Kotlin-Coroutines-1.7 - all 1.7.x and 1.8.x versions. Kotlin-Coroutines-1.9 - all 1.9.x and later versions. ## Installation To use this instrumentation. Download the latest release. +Use the 8.20.0 or later version of the Java Agent. +Set the system flag -Dnewrelic.config.class_transformer.clear_return_stacks=true +(see https://docs.newrelic.com/docs/release-notes/agent-release-notes/java-release-notes/java-agent-8200/) In the New Relic Java Agent directory (directory containing newrelic.jar), create a directory named extensions if it does not already exist. Copy the jars into the extensions directory. Restart the application. diff --git a/build.gradle b/build.gradle index bf251a2..73b6747 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,7 @@ buildscript { plugins { id "de.undercouch.download" version "5.0.0" + id "org.jetbrains.kotlin.jvm" version "1.8.21" apply false } project.ext {