Skip to content

Commit a311dd1

Browse files
committed
added support for native-mt versions
1 parent 0c3af7f commit a311dd1

31 files changed

+1646
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
3+
apply plugin: 'java'
4+
apply plugin: 'org.jetbrains.kotlin.jvm'
5+
6+
dependencies {
7+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1-native-mt")
8+
9+
// New Relic Java Agent dependencies
10+
implementation fileTree(include: ['*.jar'], dir: '../libs')
11+
}
12+
13+
jar {
14+
manifest {
15+
attributes 'Implementation-Title': 'com.newrelic.instrumentation.labs.Kotlin-Coroutines_1.4-native-mt'
16+
attributes 'Implementation-Vendor': 'New Relic Labs'
17+
attributes 'Implementation-Vendor-Id': 'com.newrelic.labs'
18+
attributes 'Implementation-Version': 2.0
19+
}
20+
}
21+
22+
verifyInstrumentation {
23+
// passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core:[1.4.0,1.7.0)'
24+
// passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.4.0,1.7.0)'
25+
passes 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:[1.5.1.native-mt,1.7.0)'
26+
excludeRegex '.*SNAPSHOT'
27+
excludeRegex '.*alpha'
28+
excludeRegex '.*-eap-.*'
29+
excludeRegex '.*-native-.*'
30+
excludeRegex '.*-M[0-9]'
31+
excludeRegex '.*-rc'
32+
excludeRegex '.*-RC.*'
33+
excludeRegex '.*-Beta'
34+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_14;
2+
3+
import com.newrelic.api.agent.Config;
4+
import com.newrelic.api.agent.NewRelic;
5+
6+
import java.util.ArrayList;
7+
import java.util.Collection;
8+
import java.util.List;
9+
import java.util.logging.Level;
10+
11+
/*
12+
* Used to ignore Dispatched Tasks that the user has configured to ignore
13+
*/
14+
public class DispatchedTaskIgnores {
15+
16+
private static final List<String> ignoredTasks = new ArrayList<>();
17+
private static final String DISPATCHED_IGNORE_CONFIG = "Coroutines.ignores.dispatched";
18+
19+
static {
20+
Config config = NewRelic.getAgent().getConfig();
21+
String ignores = config.getValue(DISPATCHED_IGNORE_CONFIG);
22+
configure(ignores);
23+
24+
}
25+
26+
27+
public static boolean ignoreDispatchedTask(String dispatchedTask) {
28+
return ignoredTasks.contains(dispatchedTask);
29+
}
30+
31+
public static void addIgnoredTasks(Collection<String> toIgnore) {
32+
ignoredTasks.addAll(toIgnore);
33+
}
34+
35+
public static void addIgnore(String ignore) {
36+
if(!ignoredTasks.contains(ignore)) {
37+
ignoredTasks.add(ignore);
38+
NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore DispatchedTasks with continuation string {0}", ignore);
39+
}
40+
}
41+
42+
public static void reset() {
43+
ignoredTasks.clear();
44+
}
45+
46+
protected static void configure(String result) {
47+
if(result == null || result.isEmpty()) return;
48+
String[] ignores = result.split(",");
49+
for(String ignore : ignores) {
50+
addIgnore(ignore);
51+
}
52+
}
53+
54+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_14;
2+
3+
import com.newrelic.agent.bridge.AgentBridge;
4+
import com.newrelic.api.agent.NewRelic;
5+
import com.newrelic.api.agent.Token;
6+
import com.newrelic.api.agent.Trace;
7+
import kotlin.coroutines.Continuation;
8+
import kotlin.coroutines.CoroutineContext;
9+
import org.jetbrains.annotations.NotNull;
10+
11+
/*
12+
* Used to wrap a Continuation instance. Necessary to control which Continuations
13+
* are tracked. Tracking all Continuations can effect performance.
14+
* */
15+
public class NRContinuationWrapper<T> implements Continuation<T> {
16+
17+
private Continuation<T> delegate = null;
18+
private String name = null;
19+
private static boolean isTransformed = false;
20+
21+
public NRContinuationWrapper(Continuation<T> d, String n) {
22+
delegate = d;
23+
name = n;
24+
if(!isTransformed) {
25+
AgentBridge.instrumentation.retransformUninstrumentedClass(getClass());
26+
isTransformed = true;
27+
}
28+
}
29+
30+
@NotNull
31+
@Override
32+
public CoroutineContext getContext() {
33+
return delegate.getContext();
34+
}
35+
36+
@Override
37+
@Trace(async=true)
38+
public void resumeWith(@NotNull Object p0) {
39+
String contString = Utils.getContinuationString(delegate);
40+
if(contString != null && !contString.isEmpty()) {
41+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",contString);
42+
} else if(name != null) {
43+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",name);
44+
} else {
45+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","ContinuationWrapper","resumeWith",p0.getClass().getName());
46+
}
47+
Token t = Utils.getToken(getContext());
48+
if(t != null) {
49+
t.link();
50+
}
51+
if(delegate != null) {
52+
delegate.resumeWith(p0);
53+
}
54+
}
55+
56+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_14;
2+
3+
import com.newrelic.api.agent.NewRelic;
4+
import com.newrelic.api.agent.Segment;
5+
import kotlin.Unit;
6+
import kotlin.coroutines.CoroutineContext;
7+
import kotlin.jvm.functions.Function1;
8+
import kotlinx.coroutines.CancellableContinuation;
9+
import kotlinx.coroutines.CoroutineDispatcher;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
12+
13+
/*
14+
* Used to wrap the tracking of a call to the Coroutine delay function if tracking is enabled
15+
* Will report the delay as a segment
16+
*/
17+
public class NRDelayCancellableContinuation<T> implements CancellableContinuation<T> {
18+
19+
private final CancellableContinuation<T> delegate;
20+
private Segment segment;
21+
22+
public NRDelayCancellableContinuation(CancellableContinuation<T> delegate, String type) {
23+
this.delegate = delegate;
24+
String name = Utils.getContinuationString(delegate);
25+
segment = NewRelic.getAgent().getTransaction().startSegment(type);
26+
segment.addCustomAttribute("CancellableContinuation", name);
27+
}
28+
29+
@Override
30+
public void resumeWith(@NotNull Object o) {
31+
if(segment != null) {
32+
segment.end();
33+
segment = null;
34+
}
35+
if(delegate != null) {
36+
delegate.resumeWith(o);
37+
}
38+
}
39+
40+
@Override
41+
public @NotNull CoroutineContext getContext() {
42+
return delegate.getContext();
43+
}
44+
45+
@Override
46+
public boolean isActive() {
47+
return delegate.isActive();
48+
}
49+
50+
@Override
51+
public boolean isCompleted() {
52+
return delegate.isCompleted();
53+
}
54+
55+
@Override
56+
public boolean isCancelled() {
57+
return delegate.isCancelled();
58+
}
59+
60+
@Override
61+
public @Nullable Object tryResume(T t, @Nullable Object o) {
62+
if(segment != null) {
63+
segment.end();
64+
segment = null;
65+
}
66+
return delegate.tryResume(t,o);
67+
}
68+
69+
@Override
70+
public @Nullable Object tryResume(T t, @Nullable Object o, @Nullable Function1<? super Throwable, Unit> function1) {
71+
if(segment != null) {
72+
segment.end();
73+
segment = null;
74+
}
75+
return delegate.tryResume(t,o, function1);
76+
}
77+
78+
@Override
79+
public @Nullable Object tryResumeWithException(@NotNull Throwable throwable) {
80+
NewRelic.noticeError(throwable);
81+
if(segment != null) {
82+
segment.end();
83+
segment = null;
84+
}
85+
return delegate.tryResumeWithException(throwable);
86+
}
87+
88+
@Override
89+
public void completeResume(@NotNull Object o) {
90+
if(segment != null) {
91+
segment.end();
92+
segment = null;
93+
}
94+
delegate.completeResume(o);
95+
}
96+
97+
@Override
98+
public void initCancellability() {
99+
delegate.initCancellability();
100+
}
101+
102+
@Override
103+
public boolean cancel(@Nullable Throwable throwable) {
104+
if(segment != null) {
105+
segment.end();
106+
segment = null;
107+
}
108+
return delegate.cancel(throwable);
109+
}
110+
111+
@Override
112+
public void invokeOnCancellation(@NotNull Function1<? super Throwable, Unit> function1) {
113+
if(segment != null) {
114+
segment.end();
115+
segment = null;
116+
}
117+
delegate.invokeOnCancellation(function1);
118+
}
119+
120+
@Override
121+
public void resumeUndispatched(@NotNull CoroutineDispatcher coroutineDispatcher, T t) {
122+
if(segment != null) {
123+
segment.end();
124+
segment = null;
125+
}
126+
delegate.resumeUndispatched(coroutineDispatcher, t);
127+
}
128+
129+
@Override
130+
public void resumeUndispatchedWithException(@NotNull CoroutineDispatcher coroutineDispatcher, @NotNull Throwable throwable) {
131+
if(segment != null) {
132+
segment.end();
133+
segment = null;
134+
}
135+
delegate.resumeUndispatchedWithException(coroutineDispatcher, throwable);
136+
}
137+
138+
@Override
139+
public void resume(T t, @Nullable Function1<? super Throwable, Unit> function1) {
140+
if(segment != null) {
141+
segment.end();
142+
segment = null;
143+
}
144+
delegate.resume(t,function1);
145+
}
146+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_14;
2+
3+
import com.newrelic.api.agent.NewRelic;
4+
import com.newrelic.api.agent.Segment;
5+
import kotlin.coroutines.Continuation;
6+
import kotlin.coroutines.CoroutineContext;
7+
import kotlinx.coroutines.CancellableContinuation;
8+
import org.jetbrains.annotations.NotNull;
9+
10+
public class NRDelayContinuation<T> implements Continuation<T> {
11+
12+
private final Continuation<T> delegate;
13+
private final Segment segment;
14+
15+
public NRDelayContinuation(Continuation<T> delegate, String type) {
16+
this.delegate = delegate;
17+
String name = Utils.getContinuationString(delegate);
18+
segment = NewRelic.getAgent().getTransaction().startSegment(type);
19+
segment.addCustomAttribute("Continuation", name);
20+
boolean isCancellable = delegate instanceof CancellableContinuation;
21+
segment.addCustomAttribute("isCancellable", isCancellable);
22+
}
23+
24+
@Override
25+
public void resumeWith(@NotNull Object o) {
26+
if(segment != null) {
27+
segment.end();
28+
}
29+
if(delegate != null) {
30+
delegate.resumeWith(o);
31+
}
32+
}
33+
34+
@Override
35+
public @NotNull CoroutineContext getContext() {
36+
return delegate.getContext();
37+
}
38+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines_14;
2+
3+
import com.newrelic.agent.bridge.AgentBridge;
4+
import com.newrelic.api.agent.NewRelic;
5+
import kotlin.coroutines.Continuation;
6+
import kotlin.jvm.functions.Function1;
7+
8+
/*
9+
* Used to wrap a suspend function that was used as an input as a parameter to the
10+
* Coroutine functions runBlocking, async, invoke, launch and withContext
11+
*/
12+
public class NRFunction1SuspendWrapper<P1, R> implements Function1<P1, R> {
13+
14+
private Function1<P1, R> delegate = null;
15+
private static boolean isTransformed = false;
16+
17+
public NRFunction1SuspendWrapper(Function1<P1,R> d) {
18+
delegate = d;
19+
if(!isTransformed) {
20+
AgentBridge.instrumentation.retransformUninstrumentedClass(getClass());
21+
isTransformed = true;
22+
}
23+
}
24+
25+
@Override
26+
public R invoke(P1 p1) {
27+
if(p1 instanceof Continuation) {
28+
Continuation<?> cont = (Continuation<?>)p1;
29+
30+
String cont_string = Utils.getContinuationString(cont);
31+
if(cont_string != null) {
32+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction",cont_string);
33+
} else {
34+
NewRelic.getAgent().getTracedMethod().setMetricName("Custom","Block","SuspendFunction","UnknownSource");
35+
36+
}
37+
}
38+
return delegate != null ? delegate.invoke(p1) : null;
39+
}
40+
41+
}

0 commit comments

Comments
 (0)