diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction1SuspendWrapper.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction1SuspendWrapper.java index e074a3affc..292eaf2721 100644 --- a/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction1SuspendWrapper.java +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction1SuspendWrapper.java @@ -2,6 +2,7 @@ import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; import kotlin.coroutines.Continuation; import kotlin.jvm.functions.Function1; @@ -23,6 +24,7 @@ public NRFunction1SuspendWrapper(Function1 d) { } @Override + @Trace public R invoke(P1 p1) { if(p1 instanceof Continuation) { Continuation cont = (Continuation)p1; diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction2SuspendWrapper.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction2SuspendWrapper.java index 820b94104f..2f1e7bfdf1 100644 --- a/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction2SuspendWrapper.java +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/NRFunction2SuspendWrapper.java @@ -2,7 +2,8 @@ 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 kotlin.jvm.functions.Function2; @@ -15,52 +16,62 @@ public class NRFunction2SuspendWrapper implements Function2 { private Function2 delegate = null; + private String name = null; + private String type = null; private static boolean isTransformed = false; - public NRFunction2SuspendWrapper(Function2 d) { + public NRFunction2SuspendWrapper(String nameToUse, String typeToUse, Function2 d) { delegate = d; - if(!isTransformed) { + this.name = nameToUse; + this.type = typeToUse; + if (!isTransformed) { isTransformed = true; AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); } } @Override + @Trace 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(); + if (name != null) { + if (type != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", type, name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", "UnknownType", name); + } + } else { + String generatedName = getName(s,t); + if (type != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", type, generatedName); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", "UnknownType", generatedName); } + } + if (delegate != null) { + return delegate.invoke(s, t); + } + return null; + } + + private String getName(S s, T t) { + if (s instanceof CoroutineScope) { + CoroutineScope scope = (CoroutineScope) s; CoroutineContext context = scope.getCoroutineContext(); String coroutineName = Utils.getCoroutineName(context); - if(coroutineName != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",coroutineName); - name_set = true; + if (coroutineName != null) { + return coroutineName; } - } - - 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 (t instanceof Continuation) { + Continuation cont = (Continuation) t; + String cont_string = Utils.getContinuationString(cont); + if (cont_string != null) { + return cont_string; } } - if(delegate != null) { - return delegate.invoke(s, t); - } - return null; + return "UnknownSource"; } } diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java index d67dc23cd7..58fbec1a78 100644 --- a/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_14/Utils.java @@ -84,6 +84,9 @@ public static boolean continueWithScope(CoroutineScope scope) { * coroutineScope can be a Coroutine name or CoroutineScope class name */ public static boolean continueWithScope(String coroutineScope) { + if(coroutineScope == null) { + return true; + } for(Pattern ignoredScope : ignoredScopePatterns) { if(ignoredScope.matcher(coroutineScope).matches()) { return false; diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java index a98077a6f8..d279b42914 100644 --- a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java @@ -23,7 +23,7 @@ public static void startCoroutine(Function1, @Trace public static void startCoroutine(Function2, ?> f, R receiver, Continuation cont) { if(!(f instanceof NRFunction2SuspendWrapper)) { - f = new NRFunction2SuspendWrapper<>(f); + f = new NRFunction2SuspendWrapper<>(null, "Coroutine",f); } Weaver.callOriginal(); } diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java index b9ec57abdc..2834d90f1b 100644 --- a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java @@ -36,11 +36,11 @@ protected void onCancelled(Throwable t, boolean b) { @Trace 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(); + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = new NRFunction2SuspendWrapper<>(name, "Coroutine", block); + } TracedMethod traced = NewRelic.getAgent().getTracedMethod(); traced.addCustomAttribute("Coroutine-Name", name); traced.addCustomAttribute("Block", block.toString()); diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/AwaitKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/AwaitKt_Instrumentation.java new file mode 100644 index 0000000000..3333118209 --- /dev/null +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/AwaitKt_Instrumentation.java @@ -0,0 +1,40 @@ +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 kotlin.Unit; +import kotlin.coroutines.Continuation; + +import java.util.Collection; +import java.util.List; + +@Weave(originalName = "kotlinx.coroutines.AwaitKt") +public class AwaitKt_Instrumentation { + + @Trace + public static Object awaitAll(Deferred[] deferreds, Continuation> continuation) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","AwaitKt","awaitAll"); + return Weaver.callOriginal(); + } + + @Trace + public static Object awaitAll(Collection> deferreds, Continuation> continuation) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","AwaitKt","awaitAll"); + return Weaver.callOriginal(); + } + + @Trace + public static Object joinAll(Job[] jobs, Continuation continuation) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","AwaitKt","joinAll"); + return Weaver.callOriginal(); + } + + @Trace + public static Object joinAll(Collection jobs, Continuation continuation) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","AwaitKt","joinAll"); + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java index c43463c35d..3e6b6fb5fa 100644 --- a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java @@ -29,7 +29,7 @@ public static T runBlocking(CoroutineContext context, Function2, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "RunBlocking", block); } return Weaver.callOriginal(); } @@ -55,7 +55,7 @@ public static Deferred async(CoroutineScope scope, CoroutineContext conte } if(!(block instanceof NRFunction2SuspendWrapper)) { - block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "Async", block); } } else { NewRelic.getAgent().getTransaction().ignore(); @@ -69,7 +69,7 @@ public static Object invoke(CoroutineDispatcher_Instrumentation dispatcher, NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", cont.toString()); if(!(block instanceof NRFunction2SuspendWrapper)) { - block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(null, "Invoke", block); } if(Utils.continueWithContinuation(cont)) { boolean isSuspend = cont instanceof SuspendFunction; @@ -102,8 +102,7 @@ public static kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineConte } NewRelic.getAgent().getTracedMethod().addCustomAttribute("Block", block.toString()); if (!(block instanceof NRFunction2SuspendWrapper)) { - block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper( - block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "Launch", block); } } else { NewRelic.getAgent().getTransaction().ignore(); @@ -125,7 +124,7 @@ public static Object withContext(CoroutineContext context, Function2, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(null, "WithContext", block); } if(completion != null && Utils.continueWithContinuation(completion)) { if(!(completion instanceof NRContinuationWrapper)) { diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/CancellableContinuationImpl_Instrumentation.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/CancellableContinuationImpl_Instrumentation.java new file mode 100644 index 0000000000..8dfb3b1b2b --- /dev/null +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/CancellableContinuationImpl_Instrumentation.java @@ -0,0 +1,64 @@ +package kotlinx.coroutines; + +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.CoroutineContext; +import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function3; + +@Weave(originalName = "kotlinx.coroutines.CancellableContinuationImpl") +public abstract class CancellableContinuationImpl_Instrumentation { + + @Trace + public void resumeWith(Object obj) { + Weaver.callOriginal(); + } + + @Trace + public java.lang.Object tryResumeWithException(Throwable t) { + + return Weaver.callOriginal(); + } + + @Trace + public Object tryResume(T t, Object o) { + return Weaver.callOriginal(); + } + + @Trace + public java.lang.Object tryResume(T r, Object obj, Function1 function1) { + return Weaver.callOriginal(); + } + + @Trace + public void completeResume(Object object) { + Weaver.callOriginal(); + } + + @Trace + public boolean cancel(Throwable t) { + return Weaver.callOriginal(); + } + + @Trace + public void invokeOnCancellation(Function1 function1) { + Weaver.callOriginal(); + } + + @Trace + public void resumeUndispatched(CoroutineDispatcher dispatcher, T t) { + Weaver.callOriginal(); + } + + @Trace + public void resumeUndispatchedWithException(CoroutineDispatcher dispatcher, Throwable t) { + Weaver.callOriginal(); + } + + @Trace + public void resume(T r, Function1 function3) { + Weaver.callOriginal(); + } +} diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/Deferred_Instrumentation.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/Deferred_Instrumentation.java new file mode 100644 index 0000000000..a394002916 --- /dev/null +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/Deferred_Instrumentation.java @@ -0,0 +1,16 @@ +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 kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.Deferred", type = MatchType.Interface) +public class Deferred_Instrumentation { + + @Trace + public Object await(Continuation continuation) { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java index 1a85bf5ce2..e226e0ac31 100644 --- a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java @@ -12,6 +12,21 @@ @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase") public abstract class EventLoopImplBase_Instrumentation { + public void schedule(long nanos, DelayedTask_Instrumentation task) { + if(task.token == null) { + Token t = NewRelic.getAgent().getTransaction().getToken(); + if(t != null) { + if(!t.isActive()) { + t.expire(); + t = null; + } else { + task.token = t; + } + } + } + Weaver.callOriginal(); + } + @Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedTask") public static abstract class DelayedTask_Instrumentation { @@ -26,6 +41,14 @@ public DelayedTask_Instrumentation(long nanos) { @NewField protected Token token = null; + public int scheduleTask(long delay, DelayedTaskQueue_Instrumentation queue, EventLoopImplBase_Instrumentation eventLoop) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + return Weaver.callOriginal(); + } + public void dispose() { if(token != null) { token.expire(); diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java index a6b31d4ab7..fdc7374ab0 100644 --- a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java @@ -57,7 +57,7 @@ public static void startCoroutineCancellable(Function2(f); + f = new NRFunction2SuspendWrapper<>(null, "CoroutineCancellable", f); } Weaver.callOriginal(); } diff --git a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java index 84823a1ada..8d33f0e6d1 100644 --- a/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.4/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java @@ -54,7 +54,7 @@ public static void startCoroutineUndispatched(Function2(f); + f = new NRFunction2SuspendWrapper<>(null, "CoroutineUndispatched", f); } Weaver.callOriginal(); } @@ -66,7 +66,7 @@ public static Object startUndispatchedOrReturn(ScopeCoroutine traced.addCustomAttribute("Suspend-Type", "Function2"); traced.addCustomAttribute("Receiver", receiver.getClass().getName()); if(!(f instanceof NRFunction2SuspendWrapper)) { - f = new NRFunction2SuspendWrapper<>(f); + f = new NRFunction2SuspendWrapper<>(null, "CoroutineUndispatched", f); } return Weaver.callOriginal(); } @@ -78,7 +78,7 @@ public static Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutin traced.addCustomAttribute("Suspend-Type", "Function2"); traced.addCustomAttribute("Receiver", receiver.getClass().getName()); if(!(f instanceof NRFunction2SuspendWrapper)) { - f = new NRFunction2SuspendWrapper<>(f); + f = new NRFunction2SuspendWrapper<>(null, "CoroutineUndispatched", f); } return Weaver.callOriginal(); } diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java index 70347087c5..28a77ff7ce 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRContinuationWrapper.java @@ -2,7 +2,6 @@ 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; @@ -44,10 +43,6 @@ public void resumeWith(@NotNull Object p0) { } 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/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java index e6fea420aa..8c253c4b57 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction1SuspendWrapper.java @@ -3,6 +3,7 @@ import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; import kotlin.coroutines.Continuation; import kotlin.jvm.functions.Function1; @@ -24,6 +25,7 @@ public NRFunction1SuspendWrapper(Function1 d) { } @Override + @Trace public R invoke(P1 p1) { if(p1 instanceof Continuation) { Continuation cont = (Continuation)p1; diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java index ba18cda4d1..fa2d071dd3 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/NRFunction2SuspendWrapper.java @@ -2,8 +2,8 @@ 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 kotlin.jvm.functions.Function2; @@ -16,51 +16,62 @@ public class NRFunction2SuspendWrapper implements Function2 { private Function2 delegate = null; + private String name = null; + private String type = null; private static boolean isTransformed = false; - public NRFunction2SuspendWrapper(Function2 d) { + public NRFunction2SuspendWrapper(String nameToUse, String typeToUse, Function2 d) { delegate = d; - if(!isTransformed) { + this.name = nameToUse; + this.type = typeToUse; + if (!isTransformed) { isTransformed = true; AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); } } @Override + @Trace 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(); + if (name != null) { + if (type != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", type, name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", "UnknownType", name); + } + } else { + String generatedName = getName(s,t); + if (type != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", type, generatedName); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", "UnknownType", generatedName); } + } + if (delegate != null) { + return delegate.invoke(s, t); + } + return null; + } + + private String getName(S s, T t) { + if (s instanceof CoroutineScope) { + CoroutineScope scope = (CoroutineScope) s; CoroutineContext context = scope.getCoroutineContext(); String coroutineName = Utils.getCoroutineName(context); - if(coroutineName != null) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",coroutineName); - name_set = true; + if (coroutineName != null) { + return coroutineName; } - } - if(!name_set && t instanceof Continuation) { - Continuation cont = (Continuation)t; + if (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 (cont_string != null) { + return cont_string; } } - if(delegate != null) { - return delegate.invoke(s, t); - } - return null; + return "UnknownSource"; } } diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java index fdbc6599d4..11b7ce92e0 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_17/Utils.java @@ -84,6 +84,9 @@ public static boolean continueWithScope(CoroutineScope scope) { * coroutineScope can be a Coroutine name or CoroutineScope class name */ public static boolean continueWithScope(String coroutineScope) { + if(coroutineScope == null) { + return true; + } for(Pattern ignoredScope : ignoredScopePatterns) { if(ignoredScope.matcher(coroutineScope).matches()) { return false; @@ -106,7 +109,6 @@ public static boolean continueWithContinuation(Continuation continuation) { if(cont_string == null) { return false; } if(ignoredContinuations.contains(cont_string)) { - NewRelic.getAgent().getLogger().log(Level.FINE, "Returning false for continuation {0}", cont_string); return false; } @@ -116,54 +118,11 @@ public static boolean continueWithContinuation(Continuation continuation) { } } - NewRelic.getAgent().getLogger().log(Level.FINE, "Returning true for continuation {0}", cont_string); return true; } public static String sub = "createCoroutineFromSuspendFunction"; - /* - * Set the async token in the CoroutineContext - * Used to track the transaction across multiple threads - */ - 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()) { - NRTokenContextKt.addTokenContext(context, t); - } else if(t != null) { - t.expire(); - t = null; - } - } - - } - - /* - * Gets the async token in the CoroutineContext if it is set - */ - public static Token getToken(CoroutineContext context) { - TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); - if(tokenContext != null) { - return tokenContext.getToken(); - } - return null; - } - - /* - * Expires the async token in the CoroutineContext if it is set and - * removes the tokencontext from the CoroutineContext - */ - public static void expireToken(CoroutineContext context) { - TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); - if(tokenContext != null) { - Token token = tokenContext.getToken(); - token.expire(); - NRTokenContextKt.removeTokenContext(context); - } - } - @SuppressWarnings("unchecked") public static String getCoroutineName(CoroutineContext context, Continuation continuation) { diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java index 300c48385c..d137ad54ab 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java @@ -23,7 +23,7 @@ public static void startCoroutine(Function1, @Trace public static void startCoroutine(Function2, ?> f, R receiver, Continuation cont) { if(!(f instanceof NRFunction2SuspendWrapper)) { - f = new NRFunction2SuspendWrapper<>(f); + f = new NRFunction2SuspendWrapper<>(null, "CoroutineFromContinuation", f); } Weaver.callOriginal(); } diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java index a83b9b41e9..852e7a0734 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java @@ -21,12 +21,10 @@ public abstract class AbstractCoroutine_Instrumentation { 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(); } @@ -36,11 +34,11 @@ protected void onCancelled(Throwable t, boolean b) { @Trace 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(); + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = new NRFunction2SuspendWrapper<>(name, "Coroutine", block); + } TracedMethod traced = NewRelic.getAgent().getTracedMethod(); traced.addCustomAttribute("Coroutine-Name", name); traced.addCustomAttribute("Block", block.toString()); diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java index 9ef878e559..8648e5e338 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java @@ -3,7 +3,8 @@ import java.util.List; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -12,10 +13,20 @@ @Weave(originalName = "kotlinx.coroutines.AwaitAll") abstract class AwaitAll_Instrumentation { - @Trace + @NewField + private Segment segment = null; + + public AwaitAll_Instrumentation(kotlinx.coroutines.Deferred[] deferreds) { + segment = NewRelic.getAgent().getTransaction().startSegment("AwaitAll"); + } + public Object await(Continuation> cont) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AwaitAll","await"); - return Weaver.callOriginal(); + Object result = Weaver.callOriginal(); + if(segment != null) { + segment.end(); + segment = null; + } + return result; } } diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java index 18c78906d5..fd17045275 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java @@ -31,7 +31,7 @@ public static T runBlocking(CoroutineContext context, Function2, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name,"RunBlocking", block); } return Weaver.callOriginal(); } @@ -39,9 +39,7 @@ public static T runBlocking(CoroutineContext context, Function2 Deferred async(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2, ?> block) { - NewRelic.getAgent().getLogger().log(Level.FINE,"call to BuildersKt_Instrumentation.async, scope is {0}", scope); if (Utils.continueWithScope(scope)) { - NewRelic.getAgent().getLogger().log(Level.FINE,"in call to BuildersKt_Instrumentation.async, continuing with scope {0}", scope); NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineStart", cStart.name()); NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineScope-Class", scope.getClass().getName()); String name = Utils.getCoroutineName(context); @@ -57,10 +55,9 @@ public static Deferred async(CoroutineScope scope, CoroutineContext conte } if(!(block instanceof NRFunction2SuspendWrapper)) { - block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "Async", block); } } else { - NewRelic.getAgent().getLogger().log(Level.FINE,"in call to BuildersKt_Instrumentation.async, did not continue with scope {0}", scope); NewRelic.getAgent().getTransaction().ignore(); } return Weaver.callOriginal(); @@ -72,7 +69,7 @@ public static Object invoke(CoroutineDispatcher_Instrumentation dispatcher, NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", cont.toString()); if(!(block instanceof NRFunction2SuspendWrapper)) { - block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(null, "Invoke", block); } if(Utils.continueWithContinuation(cont)) { boolean isSuspend = cont instanceof SuspendFunction; @@ -103,14 +100,8 @@ public static kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineConte 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 { -// Utils.setToken(context); -// } if (!(block instanceof NRFunction2SuspendWrapper)) { - block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "Launch",block); } } else { NewRelic.getAgent().getTransaction().ignore(); @@ -132,7 +123,7 @@ public static Object withContext(CoroutineContext context, Function2, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "WithContext", block); } if(completion != null && Utils.continueWithContinuation(completion)) { if(!(completion instanceof NRContinuationWrapper)) { diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/CancellableContinuationImpl_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/CancellableContinuationImpl_Instrumentation.java new file mode 100644 index 0000000000..0476a718d9 --- /dev/null +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/CancellableContinuationImpl_Instrumentation.java @@ -0,0 +1,56 @@ +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 kotlin.Unit; +import kotlin.coroutines.CoroutineContext; +import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function3; + +@Weave(originalName = "kotlinx.coroutines.CancellableContinuationImpl") +public abstract class CancellableContinuationImpl_Instrumentation { + + @Trace + public void resumeWith(Object obj) { + Weaver.callOriginal(); + } + + @Trace + public java.lang.Object tryResumeWithException(Throwable t) { + + return Weaver.callOriginal(); + } + + @Trace + public Object tryResume(T t, Object o) { + return Weaver.callOriginal(); + } + + @Trace + public void completeResume(Object object) { + Weaver.callOriginal(); + } + + @Trace + public boolean cancel(Throwable t) { + return Weaver.callOriginal(); + } + + @Trace + public void invokeOnCancellation(Function1 function1) { + Weaver.callOriginal(); + } + + @Trace + public void resumeUndispatched(CoroutineDispatcher dispatcher, T t) { + Weaver.callOriginal(); + } + + @Trace + public void resumeUndispatchedWithException(CoroutineDispatcher dispatcher, Throwable t) { + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java index 95033d1464..6dbf041998 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java @@ -12,6 +12,21 @@ @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase") public abstract class EventLoopImplBase_Instrumentation { + public void schedule(long nanos, DelayedTask_Instrumentation task) { + if(task.token == null) { + Token t = NewRelic.getAgent().getTransaction().getToken(); + if(t != null) { + if(!t.isActive()) { + t.expire(); + t = null; + } else { + task.token = t; + } + } + } + Weaver.callOriginal(); + } + @Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedTask") public static abstract class DelayedTask_Instrumentation { @@ -26,6 +41,14 @@ public DelayedTask_Instrumentation(long nanos) { @NewField protected Token token = null; + public int scheduleTask(long delay, DelayedTaskQueue_Instrumentation queue, EventLoopImplBase_Instrumentation eventLoop) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + return Weaver.callOriginal(); + } + public void dispose() { if(token != null) { token.expire(); diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java index f70c304971..275445da0b 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java @@ -18,14 +18,14 @@ public Object receive(Continuation cont) { return Weaver.callOriginal(); } - @Trace(dispatcher = true) + @Trace public void cancel(java.util.concurrent.CancellationException ex) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"Cancel"); Weaver.callOriginal(); } - @Trace(dispatcher = true) + @Trace public Object send(E e, Continuation cont) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"send"); return Weaver.callOriginal(); diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java index 51cacb57c9..58d60a2e30 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java @@ -18,14 +18,14 @@ public Object receive(Continuation cont) { return Weaver.callOriginal(); } - @Trace(dispatcher = true) + @Trace public void cancel(java.util.concurrent.CancellationException ex) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"Cancel"); Weaver.callOriginal(); } - @Trace(dispatcher = true) + @Trace public Object send(E e, Continuation cont) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"send"); return Weaver.callOriginal(); diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java index ead582396d..9e949fc58d 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java @@ -5,14 +5,12 @@ 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.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; @@ -22,11 +20,6 @@ public abstract class CancellableKt_Instrumentation { @Trace public static void startCoroutineCancellable(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); - } - } if(continuationString != null) { NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); } @@ -40,12 +33,6 @@ public static void startCoroutineCancellable(Function1 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(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { - cont = new NRContinuationWrapper<>(cont, continuationString); - } - } if(continuationString != null) { NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); } @@ -57,7 +44,7 @@ public static void startCoroutineCancellable(Function2(f); + f = new NRFunction2SuspendWrapper<>(null,"CoroutineCancellable", f); } Weaver.callOriginal(); } @@ -65,19 +52,7 @@ public static void startCoroutineCancellable(Function2 completion, Continuation cont) { String completionString = Utils.getContinuationString(completion); - if(!(completion instanceof SuspendFunction)) { - // create continuation wrapper if needed - if(Utils.continueWithContinuation(completion) && !(completion instanceof NRContinuationWrapper)) { - completion = new NRContinuationWrapper<>(completion, completionString); - } - } String continuationString = Utils.getContinuationString(cont); - if(!(cont instanceof SuspendFunction)) { - // create continuation wrapper if needed - if(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { - cont = new NRContinuationWrapper<>(cont, continuationString); - } - } TracedMethod traced = NewRelic.getAgent().getTracedMethod(); if(completionString != null) { traced.addCustomAttribute("Completion", completionString); diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java index dcd4fd2879..43403aad9a 100644 --- a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java @@ -54,7 +54,7 @@ public static void startCoroutineUndispatched(Function2(f); + f = new NRFunction2SuspendWrapper<>(null, "CoroutineUndispatched", f); } Weaver.callOriginal(); } @@ -66,7 +66,7 @@ public static Object startUndispatchedOrReturn(ScopeCoroutine traced.addCustomAttribute("Suspend-Type", "Function2"); traced.addCustomAttribute("Receiver", receiver.getClass().getName()); if(!(f instanceof NRFunction2SuspendWrapper)) { - f = new NRFunction2SuspendWrapper<>(f); + new NRFunction2SuspendWrapper<>(null, "CoroutineUndispatched", f); } return Weaver.callOriginal(); } @@ -78,7 +78,7 @@ public static Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutin traced.addCustomAttribute("Suspend-Type", "Function2"); traced.addCustomAttribute("Receiver", receiver.getClass().getName()); if(!(f instanceof NRFunction2SuspendWrapper)) { - f = new NRFunction2SuspendWrapper<>(f); + new NRFunction2SuspendWrapper<>(null, "CoroutineUndispatched", f); } return Weaver.callOriginal(); } diff --git a/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/scheduling/CoroutineScheduler_Instrumentation.java b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/scheduling/CoroutineScheduler_Instrumentation.java new file mode 100644 index 0000000000..0f45278024 --- /dev/null +++ b/instrumentation/kotlin-coroutines-1.7/src/main/java/kotlinx/coroutines/scheduling/CoroutineScheduler_Instrumentation.java @@ -0,0 +1,18 @@ +package kotlinx.coroutines.scheduling; + +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; + +@Weave(originalName = "kotlinx.coroutines.scheduling.CoroutineScheduler") +public class CoroutineScheduler_Instrumentation { + + public void dispatch(Runnable block, TaskContext taskContext, boolean tailDispatch) { + NRRunnable wrapper = Utils.getRunnableWrapper(block); + if (wrapper != null) { + block = wrapper; + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRContinuationWrapper.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRContinuationWrapper.java deleted file mode 100644 index 9a4a695f56..0000000000 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRContinuationWrapper.java +++ /dev/null @@ -1,56 +0,0 @@ -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 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; - if(!isTransformed) { - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - isTransformed = true; - } - } - - @Override - public @NotNull CoroutineContext getContext() { - return delegate.getContext(); - } - - @Override - @Trace(async=true) - public void resumeWith(@NotNull 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/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1SuspendWrapper.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1SuspendWrapper.java index cefb6a310d..b122922075 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1SuspendWrapper.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1SuspendWrapper.java @@ -3,6 +3,7 @@ import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; import kotlin.coroutines.Continuation; import kotlin.jvm.functions.Function1; @@ -24,6 +25,7 @@ public NRFunction1SuspendWrapper(Function1 d) { } @Override + @Trace public R invoke(P1 p1) { if(p1 instanceof Continuation) { Continuation cont = (Continuation)p1; diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1Wrapper.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1Wrapper.java deleted file mode 100644 index f3f3038134..0000000000 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction1Wrapper.java +++ /dev/null @@ -1,36 +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.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/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2SuspendWrapper.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2SuspendWrapper.java index 57e6e393f2..6ca209cd5a 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2SuspendWrapper.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/NRFunction2SuspendWrapper.java @@ -2,8 +2,8 @@ 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 kotlin.jvm.functions.Function2; @@ -15,52 +15,63 @@ */ public class NRFunction2SuspendWrapper implements Function2 { - private Function2 delegate = null; - private static boolean isTransformed = false; + private Function2 delegate = null; + private String name = null; + private String type = null; + private static boolean isTransformed = false; - public NRFunction2SuspendWrapper(Function2 d) { - delegate = d; - if(!isTransformed) { - isTransformed = true; - AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); - } - } + public NRFunction2SuspendWrapper(String nameToUse, String typeToUse, Function2 d) { + delegate = d; + this.name = nameToUse; + this.type = typeToUse; + 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; - } + @Override + @Trace + public R invoke(S s, T t) { + if (name != null) { + if (type != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", type, name); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", "UnknownType", name); + } + } else { + String generatedName = getName(s,t); + if (type != null) { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", type, generatedName); + } else { + NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "Kotlin-Coroutines", "Block", "UnknownType", generatedName); + } + } + if (delegate != null) { + return delegate.invoke(s, t); + } + return null; + } - } + private String getName(S s, T t) { + if (s instanceof CoroutineScope) { + CoroutineScope scope = (CoroutineScope) s; + CoroutineContext context = scope.getCoroutineContext(); + String coroutineName = Utils.getCoroutineName(context); + if (coroutineName != null) { + return coroutineName; + } + } - if(!name_set && t instanceof Continuation) { - Continuation cont = (Continuation)t; + if (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; - } + String cont_string = Utils.getContinuationString(cont); + if (cont_string != null) { + return cont_string; + } + } + return "UnknownSource"; + } } diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/Utils.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/Utils.java index 0e8e993017..d7ba6e9fc6 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/Utils.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/com/newrelic/instrumentation/kotlin/coroutines_19/Utils.java @@ -84,6 +84,9 @@ public static boolean continueWithScope(CoroutineScope scope) { * coroutineScope can be a Coroutine name or CoroutineScope class name */ public static boolean continueWithScope(String coroutineScope) { + if(coroutineScope == null) { + return true; + } for(Pattern ignoredScope : ignoredScopePatterns) { if(ignoredScope.matcher(coroutineScope).matches()) { return false; @@ -120,48 +123,6 @@ public static boolean continueWithContinuation(Continuation continuation) { public static String sub = "createCoroutineFromSuspendFunction"; - /* - * Set the async token in the CoroutineContext - * Used to track the transaction across multiple threads - */ - 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()) { - NRTokenContextKt.addTokenContext(context, t); - } else if(t != null) { - t.expire(); - t = null; - } - } - - } - - /* - * Gets the async token in the CoroutineContext if it is set - */ - public static Token getToken(CoroutineContext context) { - TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); - if(tokenContext != null) { - return tokenContext.getToken(); - } - return null; - } - - /* - * Expires the async token in the CoroutineContext if it is set and - * removes the tokencontext from the CoroutineContext - */ - public static void expireToken(CoroutineContext context) { - TokenContext tokenContext = NRTokenContextKt.getTokenContextOrNull(context); - if(tokenContext != null) { - Token token = tokenContext.getToken(); - token.expire(); - NRTokenContextKt.removeTokenContext(context); - } - } - @SuppressWarnings("unchecked") public static String getCoroutineName(CoroutineContext context, Continuation continuation) { diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java index 83a94517fb..1b5c6bb043 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlin/coroutines/ContinuationKt_Instrumentation.java @@ -23,7 +23,7 @@ public static void startCoroutine(Function1, @Trace public static void startCoroutine(Function2, ?> f, R receiver, Continuation cont) { if(!(f instanceof NRFunction2SuspendWrapper)) { - f = new NRFunction2SuspendWrapper<>(f); + f = new NRFunction2SuspendWrapper<>(null,"CoroutineFromContinuation",f); } Weaver.callOriginal(); } diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java index d3813a3e80..52b782045f 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/AbstractCoroutine_Instrumentation.java @@ -21,12 +21,10 @@ public abstract class AbstractCoroutine_Instrumentation { 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(); } @@ -36,13 +34,13 @@ protected void onCancelled(Throwable t, boolean b) { @Trace public void start(CoroutineStart start, R receiver, Function2, ? extends Object> block) { - if(!(block instanceof NRFunction2SuspendWrapper)) { - block = new NRFunction2SuspendWrapper<>(block); - } String ctxName = Utils.getCoroutineName(getContext()); String name = ctxName != null ? ctxName : nameString$kotlinx_coroutines_core(); TracedMethod traced = NewRelic.getAgent().getTracedMethod(); traced.addCustomAttribute("Coroutine-Name", name); + if(!(block instanceof NRFunction2SuspendWrapper)) { + block = new NRFunction2SuspendWrapper<>(name, "Coroutine", block); + } traced.addCustomAttribute("Block", block.toString()); if(name != null && !name.isEmpty()) { diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java index 9ef878e559..5974a3dcc6 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/AwaitAll_Instrumentation.java @@ -3,7 +3,8 @@ import java.util.List; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -12,10 +13,20 @@ @Weave(originalName = "kotlinx.coroutines.AwaitAll") abstract class AwaitAll_Instrumentation { - @Trace + @NewField + private Segment segment = null; + + public AwaitAll_Instrumentation(kotlinx.coroutines.Deferred[] deferreds) { + segment = NewRelic.getAgent().getTransaction().startSegment("AwaitAll"); + } + public Object await(Continuation> cont) { - NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AwaitAll","await"); - return Weaver.callOriginal(); + Object result = Weaver.callOriginal(); + if(segment != null) { + segment.end(); + segment = null; + } + return result; } } diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java index 6a1c05291c..2453f347f6 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/BuildersKt_Instrumentation.java @@ -5,14 +5,12 @@ 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" }) @@ -21,10 +19,6 @@ public class BuildersKt_Instrumentation { @Trace(dispatcher = true) public static T runBlocking(CoroutineContext context, Function2, ?> block) { - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } String name = Utils.getCoroutineName(context); if(name != null) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","runBlocking",name); @@ -34,7 +28,7 @@ public static T runBlocking(CoroutineContext context, Function2, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "RunBlocking",block); } return Weaver.callOriginal(); } @@ -57,12 +51,8 @@ public static Deferred async(CoroutineScope scope, CoroutineContext conte NewRelic.getAgent().getTracedMethod().addCustomAttribute("CoroutineName", "Could not determine"); } - Token token = Utils.getToken(context); - if(token != null) { - token.link(); - } if(!(block instanceof NRFunction2SuspendWrapper)) { - block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "Async", block); } } else { NewRelic.getAgent().getTransaction().ignore(); @@ -76,14 +66,7 @@ public static Object invoke(CoroutineDispatcher_Instrumentation dispatcher, 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); - } + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(null, "Invoke",block); } return Weaver.callOriginal(); } @@ -107,12 +90,8 @@ public static kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineConte 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(); - } if (!(block instanceof NRFunction2SuspendWrapper)) { - block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(block); + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "Launch", block); } } else { NewRelic.getAgent().getTransaction().ignore(); @@ -133,18 +112,8 @@ public static Object withContext(CoroutineContext context, Function2, ?>) 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); - } + block = (NRFunction2SuspendWrapper, ?>) new NRFunction2SuspendWrapper(name, "WithContext", block); } return Weaver.callOriginal(); } diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/CancellableContinuationImpl_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/CancellableContinuationImpl_Instrumentation.java new file mode 100644 index 0000000000..279800a61e --- /dev/null +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/CancellableContinuationImpl_Instrumentation.java @@ -0,0 +1,65 @@ +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 kotlin.Unit; +import kotlin.coroutines.CoroutineContext; +import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function3; + +@Weave(originalName = "kotlinx.coroutines.CancellableContinuationImpl") +public abstract class CancellableContinuationImpl_Instrumentation { + + @Trace + public void resumeWith(Object obj) { + Weaver.callOriginal(); + } + + @Trace + public java.lang.Object tryResumeWithException(Throwable t) { + + return Weaver.callOriginal(); + } + + @Trace + public Object tryResume(T t, Object o) { + return Weaver.callOriginal(); + } + + @Trace + public java.lang.Object tryResume(R r, Object obj, Function3 function3) { + return Weaver.callOriginal(); + } + + @Trace + public void completeResume(Object object) { + Weaver.callOriginal(); + } + + @Trace + public boolean cancel(Throwable t) { + return Weaver.callOriginal(); + } + + @Trace + public void invokeOnCancellation(Function1 function1) { + Weaver.callOriginal(); + } + + @Trace + public void resumeUndispatched(CoroutineDispatcher dispatcher, T t) { + Weaver.callOriginal(); + } + + @Trace + public void resumeUndispatchedWithException(CoroutineDispatcher dispatcher, Throwable t) { + Weaver.callOriginal(); + } + + @Trace + public void resume(R r, Function3 function3) { + Weaver.callOriginal(); + } +} diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/Deferred_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/Deferred_Instrumentation.java new file mode 100644 index 0000000000..a394002916 --- /dev/null +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/Deferred_Instrumentation.java @@ -0,0 +1,16 @@ +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 kotlin.coroutines.Continuation; + +@Weave(originalName = "kotlinx.coroutines.Deferred", type = MatchType.Interface) +public class Deferred_Instrumentation { + + @Trace + public Object await(Continuation continuation) { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java index 76abc18a6b..ed5a616f6b 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/EventLoopImplBase_Instrumentation.java @@ -12,6 +12,21 @@ @Weave(originalName = "kotlinx.coroutines.EventLoopImplBase") public abstract class EventLoopImplBase_Instrumentation { + public void schedule(long nanos, DelayedTask_Instrumentation task) { + if(task.token == null) { + Token t = NewRelic.getAgent().getTransaction().getToken(); + if(t != null) { + if(!t.isActive()) { + t.expire(); + t = null; + } else { + task.token = t; + } + } + } + Weaver.callOriginal(); + } + @Weave(type = MatchType.BaseClass, originalName = "kotlinx.coroutines.EventLoopImplBase$DelayedTask") public static abstract class DelayedTask_Instrumentation { @@ -26,6 +41,14 @@ public DelayedTask_Instrumentation(long nanos) { @NewField protected Token token = null; + public int scheduleTask(long delay, DelayedTaskQueue_Instrumentation queue, EventLoopImplBase_Instrumentation eventLoop) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + return Weaver.callOriginal(); + } + public void dispose() { if(token != null) { token.expire(); diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java index 817a87d34f..a0bd41b8bb 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/ResumeUndispatchedRunnable_Instrumentation.java @@ -25,6 +25,7 @@ public ResumeUndispatchedRunnable_Instrumentation(CoroutineDispatcher_Instrument @Trace(async = true) public void run() { + NewRelic.getAgent().getTracedMethod().setMetricName(new String[] {"Custom","Kotlin","Coroutines","ResumeUndispatchedRunnable","run"}); if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java index f70c304971..bbb1a39652 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/channels/BufferedChannel_Instrumentation.java @@ -11,21 +11,21 @@ @Weave(originalName = "kotlinx.coroutines.channels.BufferedChannel") public abstract class BufferedChannel_Instrumentation { - @Trace(dispatcher = true) + @Trace public Object receive(Continuation cont) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"receive"); return Weaver.callOriginal(); } - @Trace(dispatcher = true) + @Trace public void cancel(java.util.concurrent.CancellationException ex) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"Cancel"); Weaver.callOriginal(); } - @Trace(dispatcher = true) + @Trace public Object send(E e, Continuation cont) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"send"); return Weaver.callOriginal(); diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java index 51cacb57c9..0e3b4a0df3 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/channels/ChannelCoroutine_Instrumentation.java @@ -18,7 +18,7 @@ public Object receive(Continuation cont) { return Weaver.callOriginal(); } - @Trace(dispatcher = true) + @Trace public void cancel(java.util.concurrent.CancellationException ex) { NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","Coroutines","Channel",getClass().getSimpleName(),"Cancel"); diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java index 3ec294ab9c..8348684163 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/intrinsics/CancellableKt_Instrumentation.java @@ -5,13 +5,11 @@ 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_19.NRContinuationWrapper; 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; -import kotlin.coroutines.jvm.internal.SuspendFunction; import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; @@ -21,11 +19,6 @@ public abstract class CancellableKt_Instrumentation { @Trace public static void startCoroutineCancellable(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); - } - } if(continuationString != null) { NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); } @@ -39,12 +32,6 @@ public static void startCoroutineCancellable(Function1 void startCoroutineCancellable(Function2, ?> f, R receiver, Continuation cont) { String continuationString = Utils.getContinuationString(cont); - if(!(cont instanceof SuspendFunction)) { - // create continuation wrapper if needed - if(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { - cont = new NRContinuationWrapper<>(cont, continuationString); - } - } if(continuationString != null) { NewRelic.getAgent().getTracedMethod().addCustomAttribute("Continuation", continuationString); } @@ -52,7 +39,7 @@ public static void startCoroutineCancellable(Function2(f); + f = new NRFunction2SuspendWrapper<>(null, "StartCoroutineCancellable", f); } Weaver.callOriginal(); } @@ -60,19 +47,7 @@ public static void startCoroutineCancellable(Function2 completion, Continuation cont) { String completionString = Utils.getContinuationString(completion); - if(!(completion instanceof SuspendFunction)) { - // create continuation wrapper if needed - if(Utils.continueWithContinuation(completion) && !(completion instanceof NRContinuationWrapper)) { - completion = new NRContinuationWrapper<>(completion, completionString); - } - } String continuationString = Utils.getContinuationString(cont); - if(!(cont instanceof SuspendFunction)) { - // create continuation wrapper if needed - if(Utils.continueWithContinuation(cont) && !(cont instanceof NRContinuationWrapper)) { - cont = new NRContinuationWrapper<>(cont, continuationString); - } - } TracedMethod traced = NewRelic.getAgent().getTracedMethod(); if(completionString != null) { traced.addCustomAttribute("Completion", completionString); diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java index e665715ecf..719a528cf4 100644 --- a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/intrinsics/UndispatchedKt_Instrumentation.java @@ -5,12 +5,10 @@ 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_19.NRContinuationWrapper; import com.newrelic.instrumentation.kotlin.coroutines_19.NRFunction2SuspendWrapper; import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; import kotlin.coroutines.Continuation; -import kotlin.coroutines.jvm.internal.SuspendFunction; import kotlin.jvm.functions.Function2; import kotlinx.coroutines.internal.ScopeCoroutine; @@ -21,11 +19,6 @@ public class UndispatchedKt_Instrumentation { 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) { @@ -33,7 +26,7 @@ public static void startCoroutineUndispatched(Function2(f); + f = new NRFunction2SuspendWrapper<>(null, "Undispatched", f); } Weaver.callOriginal(); } @@ -45,7 +38,7 @@ public static Object startUndispatchedOrReturn(ScopeCoroutine traced.addCustomAttribute("Suspend-Type", "Function2"); traced.addCustomAttribute("Receiver", receiver.getClass().getName()); if(!(f instanceof NRFunction2SuspendWrapper)) { - f = new NRFunction2SuspendWrapper<>(f); + f = new NRFunction2SuspendWrapper<>(null, "Undispatched", f); } return Weaver.callOriginal(); } @@ -57,7 +50,7 @@ public static Object startUndispatchedOrReturnIgnoreTimeout(ScopeCoroutin traced.addCustomAttribute("Suspend-Type", "Function2"); traced.addCustomAttribute("Receiver", receiver.getClass().getName()); if(!(f instanceof NRFunction2SuspendWrapper)) { - f = new NRFunction2SuspendWrapper<>(f); + f = new NRFunction2SuspendWrapper<>(null, "Undispatched", f); } return Weaver.callOriginal(); } diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/scheduling/CoroutineScheduler_Instrumentation.java b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/scheduling/CoroutineScheduler_Instrumentation.java new file mode 100644 index 0000000000..fd1c43082c --- /dev/null +++ b/instrumentation/kotlin-coroutines-1.9/src/main/java/kotlinx/coroutines/scheduling/CoroutineScheduler_Instrumentation.java @@ -0,0 +1,18 @@ +package kotlinx.coroutines.scheduling; + +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.newrelic.instrumentation.kotlin.coroutines_19.NRRunnable; +import com.newrelic.instrumentation.kotlin.coroutines_19.Utils; + +@Weave(originalName = "kotlinx.coroutines.scheduling.CoroutineScheduler") +public class CoroutineScheduler_Instrumentation { + + public void dispatch(Runnable block, boolean taskContext, boolean tailDispatch) { + NRRunnable wrapper = Utils.getRunnableWrapper(block); + if (wrapper != null) { + block = wrapper; + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/kotlin-coroutines-1.9/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_19/NRTokenContext.kt b/instrumentation/kotlin-coroutines-1.9/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_19/NRTokenContext.kt deleted file mode 100644 index 9e2d522998..0000000000 --- a/instrumentation/kotlin-coroutines-1.9/src/main/kotlin/com/newrelic/instrumentation/kotlin/coroutines_19/NRTokenContext.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.newrelic.instrumentation.kotlin.coroutines_19 - -import kotlin.coroutines.AbstractCoroutineContextElement -import kotlin.coroutines.CoroutineContext - -import kotlin.coroutines.* -import com.newrelic.api.agent.Token - -data class TokenContext(val 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, token : Token) : CoroutineContext { - val tokenContext = TokenContext(token) - return context + TokenContextElement(tokenContext) -} - -fun removeTokenContext(context : CoroutineContext) : CoroutineContext { - val tokenContext = context.getTokenContextOrNull(); - if (tokenContext != null) { - val token = tokenContext.token - token.expire() - - return context.minusKey(TokenContextElement.Key) - } - return context -}