Skip to content

Commit 1918e1d

Browse files
authored
Merge pull request #18 from newrelic/undispatched
Fixed "Unable to ignore UndispatchedCoroutine"
2 parents 362f8e9 + f4e9138 commit 1918e1d

File tree

17 files changed

+305
-204
lines changed

17 files changed

+305
-204
lines changed

Kotlin-Coroutines_1.2/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jar {
2323

2424
verifyInstrumentation {
2525
passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core:[1.2.0,1.4.0)'
26-
passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.3.9,1.4.0)'
26+
passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.2.0,1.4.0)'
2727
excludeRegex '.*SNAPSHOT'
2828
excludeRegex '.*alpha'
2929
excludeRegex '.*-eap-.*'

Kotlin-Coroutines_1.2/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/NRFunction1Wrapper.java

Lines changed: 0 additions & 34 deletions
This file was deleted.

Kotlin-Coroutines_1.2/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/NRFunction2Wrapper.java

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -27,58 +27,69 @@ public NRFunction2Wrapper(Function2<P1, P2, R> d,String n) {
2727

2828
@SuppressWarnings({ "rawtypes", "unchecked" })
2929
@Override
30-
@Trace(async=true)
30+
@Trace(async=true, excludeFromTransactionTrace = true)
3131
public R invoke(P1 p1, P2 p2) {
32-
String nameStr = null;
33-
boolean linked = false;
34-
if(p1 instanceof CoroutineContext) {
35-
CoroutineContext ctx = (CoroutineContext)p1;
36-
nameStr = Utils.getCoroutineName(ctx);
37-
Token token = Utils.getToken(ctx);
38-
if(token != null) {
39-
token.link();
40-
linked = true;
41-
}
42-
}
43-
if(p1 instanceof CoroutineScope) {
44-
CoroutineScope scope = (CoroutineScope)p1;
45-
nameStr = Utils.getCoroutineName(scope.getCoroutineContext());
46-
if (!linked) {
47-
Token token = Utils.getToken(scope.getCoroutineContext());
32+
33+
boolean isUndispatched = p1.getClass().getName().equals("kotlinx.coroutines.UndispatchedCoroutine") || p2.getClass().getName().equals("kotlinx.coroutines.UndispatchedCoroutine");
34+
if (!isUndispatched) {
35+
String nameStr = null;
36+
boolean linked = false;
37+
if (p1 instanceof CoroutineContext) {
38+
CoroutineContext ctx = (CoroutineContext) p1;
39+
nameStr = Utils.getCoroutineName(ctx);
40+
Token token = Utils.getToken(ctx);
4841
if (token != null) {
4942
token.link();
5043
linked = true;
51-
}
44+
}
5245
}
53-
}
54-
if(p2 instanceof Continuation) {
55-
Continuation continuation = (Continuation)p2;
56-
if(nameStr == null) nameStr = Utils.getCoroutineName(continuation.getContext(), continuation);
57-
if(nameStr == null || nameStr.equals(Utils.CREATEMETHOD1) || nameStr.equals(Utils.CREATEMETHOD2)) nameStr = name;
58-
59-
if(!linked) {
60-
Token token = Utils.getToken(continuation.getContext());
61-
if (token != null) {
62-
token.link();
63-
linked = true;
64-
}
46+
if (p1 instanceof CoroutineScope) {
47+
CoroutineScope scope = (CoroutineScope) p1;
48+
nameStr = Utils.getCoroutineName(scope.getCoroutineContext());
49+
if (!linked) {
50+
Token token = Utils.getToken(scope.getCoroutineContext());
51+
if (token != null) {
52+
token.link();
53+
linked = true;
54+
}
55+
}
6556
}
66-
67-
if (!Utils.ignoreContinuation(name) && !Utils.ignoreContinuation(continuation.getClass(), continuation.getContext())) {
68-
NRContinuationWrapper wrapper = new NRContinuationWrapper(continuation, nameStr);
69-
p2 = (P2) wrapper;
57+
if (p2 instanceof Continuation) {
58+
Continuation continuation = (Continuation) p2;
59+
if (nameStr == null)
60+
nameStr = Utils.getCoroutineName(continuation.getContext(), continuation);
61+
if (nameStr == null || nameStr.equals(Utils.CREATEMETHOD1) || nameStr.equals(Utils.CREATEMETHOD2))
62+
nameStr = name;
63+
64+
if (!linked) {
65+
Token token = Utils.getToken(continuation.getContext());
66+
if (token != null) {
67+
token.link();
68+
linked = true;
69+
}
70+
}
71+
72+
if (!Utils.ignoreContinuation(name)
73+
&& !Utils.ignoreContinuation(continuation.getClass(), continuation.getContext())) {
74+
NRContinuationWrapper wrapper = new NRContinuationWrapper(continuation, nameStr);
75+
p2 = (P2) wrapper;
76+
}
7077
}
71-
}
72-
if(nameStr == null) {
73-
nameStr = name;
74-
}
75-
if(nameStr != null) {
76-
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","WrappedSuspend",nameStr);
78+
if (nameStr == null) {
79+
nameStr = name;
80+
}
81+
if (nameStr != null) {
82+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "WrappedSuspend", nameStr);
83+
}
7784
}
7885
if(delegate != null) {
7986
return delegate.invoke(p1, p2);
8087
}
8188
return null;
8289
}
90+
91+
public void markUndispatched() {
92+
93+
}
8394

8495
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines;
2+
3+
import com.newrelic.agent.bridge.AgentBridge;
4+
import com.newrelic.api.agent.Token;
5+
import com.newrelic.api.agent.Trace;
6+
7+
public class NRRunnable implements Runnable {
8+
9+
private Runnable delegate = null;
10+
private Token token = null;
11+
private static boolean isTransformed = false;
12+
13+
public NRRunnable(Runnable r,Token t) {
14+
if(!isTransformed) {
15+
AgentBridge.instrumentation.retransformUninstrumentedClass(getClass());
16+
isTransformed = true;
17+
}
18+
delegate = r;
19+
token = t;
20+
}
21+
22+
@Override
23+
@Trace(async = true)
24+
public void run() {
25+
if(token != null) {
26+
token.linkAndExpire();
27+
token = null;
28+
}
29+
if(delegate != null) {
30+
delegate.run();
31+
}
32+
}
33+
34+
}

Kotlin-Coroutines_1.2/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/Utils.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import java.util.ArrayList;
44
import java.util.List;
55
import java.util.StringTokenizer;
6-
import java.util.logging.Level;
76

87
import com.newrelic.agent.config.AgentConfig;
98
import com.newrelic.agent.config.AgentConfigListener;
@@ -14,8 +13,10 @@
1413

1514
import kotlin.coroutines.Continuation;
1615
import kotlin.coroutines.CoroutineContext;
16+
import kotlin.coroutines.jvm.internal.SuspendLambda;
1717
import kotlinx.coroutines.AbstractCoroutine;
1818
import kotlinx.coroutines.CoroutineName;
19+
import kotlinx.coroutines.DispatchedTask;
1920

2021
public class Utils implements AgentConfigListener {
2122

@@ -37,14 +38,31 @@ public class Utils implements AgentConfigListener {
3738
loadConfig(config);
3839
}
3940

41+
public static NRRunnable getRunnableWrapper(Runnable r) {
42+
if(r instanceof NRRunnable) {
43+
return null;
44+
}
45+
if(r instanceof DispatchedTask) {
46+
return null;
47+
}
48+
49+
Token t = NewRelic.getAgent().getTransaction().getToken();
50+
if(t != null && t.isActive()) {
51+
return new NRRunnable(r, t);
52+
} else if(t != null) {
53+
t.expire();
54+
t = null;
55+
}
56+
return null;
57+
}
58+
4059
private static void loadConfig(Config config) {
4160
String ignores = config.getValue(SUSPENDSIGNORECONFIG);
4261
if (ignores != null && !ignores.isEmpty()) {
4362
StringTokenizer st = new StringTokenizer(ignores, ",");
4463
while (st.hasMoreTokens()) {
4564
String token = st.nextToken();
4665
if (token != null && !token.isEmpty()) {
47-
NewRelic.getAgent().getLogger().log(Level.INFO, "will ignore suspend: {0}", token);
4866
ignoredSuspends.add(token);
4967
}
5068
}
@@ -55,7 +73,6 @@ private static void loadConfig(Config config) {
5573
while (st.hasMoreTokens()) {
5674
String token = st.nextToken();
5775
if (token != null && !token.isEmpty()) {
58-
NewRelic.getAgent().getLogger().log(Level.INFO, "will ignore continuation: {0}", token);
5976
ignoredContinuations.add(token);
6077
}
6178
}
@@ -66,7 +83,6 @@ private static void loadConfig(Config config) {
6683
while (st.hasMoreTokens()) {
6784
String token = st.nextToken();
6885
if (token != null && !token.isEmpty()) {
69-
NewRelic.getAgent().getLogger().log(Level.INFO, "will ignore dispatched continuation: {0}", token);
7086
ignoredDispatchs.add(token);
7187
}
7288
}
@@ -118,7 +134,7 @@ public static boolean ignoreDispatched(Class<?> dispatched, CoroutineContext con
118134
}
119135

120136
public static boolean ignoreSuspend(Class<?> suspend, CoroutineContext context) {
121-
137+
122138
String classname = suspend.getName();
123139

124140
if(ignoredSuspends.contains(classname)) return true;
@@ -183,6 +199,9 @@ public static <T> String getCoroutineName(CoroutineContext context, Continuation
183199
if(continuation instanceof AbstractCoroutine) {
184200
return ((AbstractCoroutine<T>)continuation).nameString$kotlinx_coroutines_core();
185201
}
202+
if(continuation instanceof SuspendLambda) {
203+
return ((SuspendLambda)continuation).toString();
204+
}
186205
return getCoroutineName(context,continuation.getClass());
187206
}
188207

Kotlin-Coroutines_1.2/src/main/java/kotlinx/coroutines/BuildersKt.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static final <T> Deferred<T> async(CoroutineScope scope, CoroutineContext
3939
}
4040
if(name == null) name = block.getClass().getName();
4141
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","async",name);
42-
if(!Utils.ignoreSuspend(block.getClass(),context) && !Utils.ignoreSuspend(block.getClass(), scope.getCoroutineContext())) {
42+
if(cStart != CoroutineStart.UNDISPATCHED && !Utils.ignoreSuspend(block.getClass(),context) && !Utils.ignoreSuspend(block.getClass(), scope.getCoroutineContext())) {
4343

4444
NRCoroutineToken nrContextToken = Utils.setToken(context);
4545
if(nrContextToken != null) {
@@ -67,13 +67,16 @@ public static final <T> Object invoke(CoroutineDispatcher dispatcher, Function2<
6767

6868
@Trace
6969
public static final kotlinx.coroutines.Job launch(CoroutineScope scope, CoroutineContext context, CoroutineStart cStart, Function2<? super CoroutineScope, ? super Continuation<? super Unit>, ? extends Object> block) {
70+
7071
String name = Utils.getCoroutineName(context);
7172
if(name == null) {
7273
name = Utils.getCoroutineName(scope.getCoroutineContext());
7374
}
7475
if(name == null) name = block.getClass().getName();
7576
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Builders","launch",name);
76-
if(!Utils.ignoreSuspend(block.getClass(), context) && !Utils.ignoreSuspend(block.getClass(), scope.getCoroutineContext())) {
77+
boolean check1 = Utils.ignoreSuspend(block.getClass(), context);
78+
boolean check2 = Utils.ignoreSuspend(block.getClass(), scope.getCoroutineContext());
79+
if(cStart != CoroutineStart.UNDISPATCHED && !check1 && !check2) {
7780
NRCoroutineToken nrContextToken = Utils.setToken(context);
7881
if(nrContextToken != null) {
7982
context = context.plus(nrContextToken);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package kotlinx.coroutines;
2+
3+
import com.newrelic.api.agent.Trace;
4+
import com.newrelic.api.agent.weaver.MatchType;
5+
import com.newrelic.api.agent.weaver.Weave;
6+
import com.newrelic.api.agent.weaver.Weaver;
7+
import com.newrelic.instrumentation.kotlin.coroutines.NRRunnable;
8+
import com.newrelic.instrumentation.kotlin.coroutines.Utils;
9+
10+
import kotlin.coroutines.CoroutineContext;
11+
12+
@Weave(type = MatchType.BaseClass)
13+
public abstract class CoroutineDispatcher {
14+
15+
@Trace
16+
public void dispatch(CoroutineContext ctx, Runnable r) {
17+
NRRunnable wrapper = Utils.getRunnableWrapper(r);
18+
if(wrapper != null) {
19+
r = wrapper;
20+
}
21+
22+
Weaver.callOriginal();
23+
}
24+
}

Kotlin-Coroutines_1.3/src/main/java/kotlinx/coroutines/DispatchedTask.java

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)