Skip to content
This repository was archived by the owner on Dec 2, 2025. It is now read-only.

Commit 052f384

Browse files
committed
refactoring to better capture and filter suspends and dispatched
1 parent 472498a commit 052f384

File tree

155 files changed

+6646
-571
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

155 files changed

+6646
-571
lines changed

Kotlin-Coroutines_1.4/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ jar {
1818
attributes 'Implementation-Vendor': 'New Relic Labs'
1919
attributes 'Implementation-Vendor-Id': 'com.newrelic.labs'
2020
attributes 'Implementation-Version': 1.0
21+
attributes 'Agent-Class': 'com.newrelic.instrumentation.kotlin.coroutines.tracing.CoroutinesPreMain'
2122
}
2223
}
2324

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines;
2+
3+
import static com.newrelic.instrumentation.kotlin.coroutines.Utils.CREATEMETHOD1;
4+
import static com.newrelic.instrumentation.kotlin.coroutines.Utils.CREATEMETHOD2;
5+
import static com.newrelic.instrumentation.kotlin.coroutines.Utils.sub;
6+
7+
import java.lang.reflect.Method;
8+
import java.util.ArrayList;
9+
import java.util.Hashtable;
10+
import java.util.List;
11+
import java.util.logging.Level;
12+
13+
import com.newrelic.api.agent.Config;
14+
import com.newrelic.api.agent.NewRelic;
15+
16+
public class DispatchedTaskIgnores {
17+
18+
private static Class<?> dispatchedTaskClass = null;
19+
private static List<String> ignoredTasks = new ArrayList<>();
20+
private static Hashtable<Class<?>,Method> methodMappings = new Hashtable<>();
21+
private static final String DISPATCHEDIGNORECONFIG = "Coroutines.ignores.dispatched";
22+
23+
static {
24+
Config config = NewRelic.getAgent().getConfig();
25+
String ignores = config.getValue(DISPATCHEDIGNORECONFIG);
26+
configure(ignores);
27+
28+
}
29+
30+
public static void addIgnore(String ignore) {
31+
if(!ignoredTasks.contains(ignore)) {
32+
ignoredTasks.add(ignore);
33+
}
34+
}
35+
36+
public static void reset() {
37+
ignoredTasks.clear();
38+
}
39+
40+
protected static void configure(String result) {
41+
String[] ignores = result.split(",");
42+
for(String ignore : ignores) {
43+
addIgnore(ignore);
44+
}
45+
}
46+
47+
public static boolean ignoreDispatchedTask(Object obj) {
48+
String result = invoke(obj);
49+
if(result.equals(CREATEMETHOD1) || result.equals(CREATEMETHOD2)) {
50+
result = sub;
51+
}
52+
if(result != null) {
53+
return ignoredTasks.contains(result);
54+
}
55+
return false;
56+
}
57+
58+
public static String invoke(Object obj) {
59+
Class<?> clazz = obj.getClass();
60+
if(!methodMappings.containsKey(clazz)) {
61+
initialize(obj);
62+
}
63+
Method toUse = methodMappings.get(clazz);
64+
65+
if(toUse != null) {
66+
try {
67+
Object value = toUse.invoke(obj, new Object[] {});
68+
if(value != null) {
69+
return value.toString();
70+
}
71+
} catch (Exception e) {
72+
NewRelic.getAgent().getLogger().log(Level.FINE, e, "Failed to invoke method on DispatchedTask");
73+
}
74+
}
75+
return null;
76+
}
77+
78+
private static void initialize(Object obj) {
79+
try {
80+
dispatchedTaskClass = obj.getClass();
81+
Method method = dispatchedTaskClass.getMethod("getDelegate$kotlinx_coroutines_core", new Class<?>[] {} );
82+
method.setAccessible(true);
83+
methodMappings.put(obj.getClass(), method);
84+
} catch (Exception e) {
85+
NewRelic.getAgent().getLogger().log(Level.FINE, e, "Failed to get DispatchedTask class and method");
86+
}
87+
}
88+
}

Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/NRContinuationWrapper.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ public CoroutineContext getContext() {
3636
@Override
3737
@Trace(async=true)
3838
public void resumeWith(Object p0) {
39-
String s = delegate.toString();
40-
if(s != null && !s.isEmpty()) {
41-
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",s);
39+
String contString = Utils.getContinuationString(delegate);
40+
if(contString != null && !contString.isEmpty()) {
41+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",contString);
4242
} else {
4343
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",name != null ? name : Utils.getCoroutineName(getContext(), delegate));
4444
}

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package com.newrelic.instrumentation.kotlin.coroutines;
22

33
import com.newrelic.agent.bridge.AgentBridge;
4-
import com.newrelic.api.agent.Trace;
54

5+
import kotlin.coroutines.Continuation;
6+
import kotlin.coroutines.jvm.internal.SuspendFunction;
67
import kotlin.jvm.functions.Function1;
78

89
public class NRFunction1Wrapper<P1, R> implements Function1<P1, R> {
@@ -18,10 +19,17 @@ public NRFunction1Wrapper(Function1<P1,R> d) {
1819
}
1920
}
2021

22+
@SuppressWarnings({ "rawtypes", "unchecked" })
2123
@Override
22-
@Trace(dispatcher = true)
23-
public R invoke(P1 arg0) {
24-
return delegate != null ? delegate.invoke(arg0) : null;
24+
public R invoke(P1 p1) {
25+
if(p1 instanceof Continuation && !(p1 instanceof SuspendFunction)) {
26+
// wrap if needed
27+
if(!(p1 instanceof NRContinuationWrapper)) {
28+
NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p1, p1.toString());
29+
p1 = (P1) wrapper;
30+
}
31+
}
32+
return delegate != null ? delegate.invoke(p1) : null;
2533
}
2634

2735
}
Lines changed: 13 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,18 @@
11
package com.newrelic.instrumentation.kotlin.coroutines;
22

3-
import java.util.HashMap;
4-
import java.util.logging.Level;
5-
63
import com.newrelic.agent.bridge.AgentBridge;
7-
import com.newrelic.api.agent.Logger;
8-
import com.newrelic.api.agent.NewRelic;
9-
import com.newrelic.api.agent.Token;
10-
import com.newrelic.api.agent.Trace;
114

125
import kotlin.coroutines.Continuation;
13-
import kotlin.coroutines.CoroutineContext;
6+
import kotlin.coroutines.jvm.internal.SuspendFunction;
147
import kotlin.jvm.functions.Function2;
15-
import kotlinx.coroutines.CoroutineScope;
168

179
public class NRFunction2Wrapper<P1, P2, R> implements Function2<P1, P2, R> {
1810

1911
private Function2<P1, P2, R> delegate = null;
20-
private String name = null;
2112
private static boolean isTransformed = false;
2213

23-
public NRFunction2Wrapper(Function2<P1, P2, R> d,String n) {
24-
NewRelic.getAgent().getLogger().log(Level.FINE, new Exception("In NRFunction2Wrapper.<init>"), "Creating NRFunction2Wrapper using {0}, {1}, function class is {2}" , d, n, d.getClass());
14+
public NRFunction2Wrapper(Function2<P1, P2, R> d) {
2515
delegate = d;
26-
name = n;
2716
if(!isTransformed) {
2817
isTransformed = true;
2918
AgentBridge.instrumentation.retransformUninstrumentedClass(getClass());
@@ -32,81 +21,19 @@ public NRFunction2Wrapper(Function2<P1, P2, R> d,String n) {
3221

3322
@SuppressWarnings({ "rawtypes", "unchecked" })
3423
@Override
35-
// @Trace(async=true)
3624
public R invoke(P1 p1, P2 p2) {
37-
String nameStr = null;
38-
HashMap<String, Object> attributes = new HashMap<>();
39-
// Token token = null;
40-
// boolean linked = false;
41-
String suspendName = p2.getClass().getName();
42-
if (p2 instanceof Continuation) {
43-
Continuation continuation = (Continuation) p2;
44-
suspendName = Utils.getContinuationString(continuation);
45-
//
46-
// if(!Utils.ignoreSuspend(p2.getClass(), delegate))
47-
//
48-
// if (!linked) {
49-
// token = Utils.getToken(continuation.getContext());
50-
// if (token != null) {
51-
// token.link();
52-
// linked = true;
53-
// }
54-
// }
55-
//
56-
// if(suspendName == null) {
57-
// suspendName = p2.getClass().getName();
58-
// }
59-
//
60-
// if (!Utils.ignoreContinuation(suspendName)) {
61-
//
62-
// NRContinuationWrapper wrapper = new NRContinuationWrapper(continuation, suspendName);
63-
//
64-
// p2 = (P2) wrapper;
65-
// }
25+
26+
if(p2 instanceof Continuation && !(p2 instanceof SuspendFunction)) {
27+
// wrap if needed
28+
if(!(p2 instanceof NRContinuationWrapper)) {
29+
NRContinuationWrapper wrapper = new NRContinuationWrapper<>((Continuation)p2, p2.toString());
30+
p2 = (P2) wrapper;
31+
}
6632
}
67-
// if (p1 instanceof CoroutineContext) {
68-
// CoroutineContext ctx = (CoroutineContext) p1;
69-
// nameStr = Utils.getCoroutineName(ctx);
70-
// token = Utils.getToken(ctx);
71-
//// if (token != null) {
72-
//// token.link();
73-
//// linked = true;
74-
//// }
75-
// }
76-
// if (p1 instanceof CoroutineScope) {
77-
// CoroutineScope scope = (CoroutineScope) p1;
78-
// nameStr = Utils.getCoroutineName(scope.getCoroutineContext());
79-
// if (token == null) {
80-
// token = Utils.getToken(scope.getCoroutineContext());
81-
// }
82-
// }
83-
// if(nameStr != null) {
84-
// NewRelic.getAgent().getTracedMethod().addCustomAttribute("Coroutine-Name", nameStr);
85-
// }
86-
//
87-
//
88-
// NewRelic.getAgent().getTracedMethod().setMetricName("Custom", "WrappedSuspend", suspendName);
89-
//
90-
// logger.log(Level.FINE, "Finished processing NRFunction2Wrapper.invoke, linked = {3}, suspendName = {0}, namestring = {1}, name = {2}", suspendName, nameStr, name, linked);
91-
//
92-
// if(!Utils.ignoreSuspend(p2.getClass(), suspendName)) {
93-
return NRWrappedSuspend.getInstance().invoke(p1, p2, delegate, attributes,"Custom", "WrappedSuspend", suspendName);
94-
// }
95-
// if(delegate != null) {
96-
// return delegate.invoke(p1, p2);
97-
// }
98-
// return null;
99-
}
100-
101-
public void markUndispatched() {
102-
103-
}
104-
105-
@Override
106-
public String toString() {
107-
return delegate != null ? delegate.toString() : super.toString();
33+
if(delegate != null) {
34+
return delegate.invoke(p1, p2);
35+
}
36+
return null;
10837
}
10938

110-
111-
11239
}

Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/NRRunnable.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.newrelic.instrumentation.kotlin.coroutines;
22

33
import com.newrelic.agent.bridge.AgentBridge;
4+
import com.newrelic.api.agent.NewRelic;
45
import com.newrelic.api.agent.Token;
56
import com.newrelic.api.agent.Trace;
67

@@ -20,8 +21,9 @@ public NRRunnable(Runnable r,Token t) {
2021
}
2122

2223
@Override
23-
@Trace(async = true)
24+
@Trace(async = true, excludeFromTransactionTrace = true)
2425
public void run() {
26+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Kotlin","AsyncRunnableWrapper");
2527
if(token != null) {
2628
token.linkAndExpire();
2729
token = null;

Kotlin-Coroutines_1.4/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/NRWrappedSuspend.java

Lines changed: 0 additions & 37 deletions
This file was deleted.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.logging.Level;
6+
7+
import com.newrelic.api.agent.Config;
8+
import com.newrelic.api.agent.NewRelic;
9+
10+
public class SuspendIgnores {
11+
12+
private static final List<String> ignoredSuspends = new ArrayList<String>();
13+
private static final String CREATEMETHOD1 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4";
14+
private static final String CREATEMETHOD2 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3";
15+
private static final String SUSPENDSIGNORECONFIG = "Coroutines.ignores.suspends";
16+
17+
static {
18+
ignoredSuspends.add(CREATEMETHOD1);
19+
ignoredSuspends.add(CREATEMETHOD2);
20+
Config config = NewRelic.getAgent().getConfig();
21+
String value = config.getValue(SUSPENDSIGNORECONFIG);
22+
init(value);
23+
24+
}
25+
26+
private static void init(String value) {
27+
if(value != null && !value.isEmpty()) {
28+
String[] ignores = value.split(",");
29+
for(String ignore : ignores) {
30+
addIgnore(ignore);
31+
}
32+
}
33+
}
34+
35+
public static void reset(Config config) {
36+
ignoredSuspends.clear();
37+
ignoredSuspends.add(CREATEMETHOD1);
38+
ignoredSuspends.add(CREATEMETHOD2);
39+
String value = config.getValue(SUSPENDSIGNORECONFIG);
40+
init(value);
41+
}
42+
43+
public static void addIgnore(String s) {
44+
if(!ignoredSuspends.contains(s)) {
45+
ignoredSuspends.add(s);
46+
NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore suspends named {0}", s);
47+
}
48+
}
49+
50+
public static boolean ignoreSuspend(Object obj) {
51+
return ignoredSuspends.contains(obj.toString()) || ignoredSuspends.contains(obj.getClass().getName());
52+
}
53+
}

0 commit comments

Comments
 (0)