Skip to content

Commit dfbe1cb

Browse files
committed
merged with suspend_fixes
1 parent e06c126 commit dfbe1cb

File tree

8 files changed

+262
-4
lines changed

8 files changed

+262
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 SUSPENDSIGNORECONFIG = "Coroutines.ignores.suspends";
14+
15+
static {
16+
Config config = NewRelic.getAgent().getConfig();
17+
String value = config.getValue(SUSPENDSIGNORECONFIG);
18+
init(value);
19+
20+
}
21+
22+
private static void init(String value) {
23+
if(value != null && !value.isEmpty()) {
24+
String[] ignores = value.split(",");
25+
for(String ignore : ignores) {
26+
addIgnore(ignore);
27+
}
28+
}
29+
}
30+
31+
public static void reset(Config config) {
32+
ignoredSuspends.clear();
33+
String value = config.getValue(SUSPENDSIGNORECONFIG);
34+
init(value);
35+
}
36+
37+
public static void addIgnore(String s) {
38+
if(!ignoredSuspends.contains(s)) {
39+
ignoredSuspends.add(s);
40+
NewRelic.getAgent().getLogger().log(Level.FINE, "Will ignore suspends named {0}", s);
41+
}
42+
}
43+
44+
public static boolean ignoreSuspend(Object obj) {
45+
return ignoredSuspends.contains(obj.toString()) || ignoredSuspends.contains(obj.getClass().getName());
46+
}
47+
}

Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/Utils.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ public class Utils implements AgentConfigListener {
1313
public static final String CREATEMETHOD2 = "Continuation at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$3";
1414
public static String sub = "createCoroutineFromSuspendFunction";
1515
private static final String CONT_LOC = "Continuation at";
16-
16+
1717
static {
1818
ServiceFactory.getConfigService().addIAgentConfigListener(INSTANCE);
1919
Config config = NewRelic.getAgent().getConfig();
2020
SuspendIgnores.reset(config);
2121
}
22-
22+
2323
@Override
2424
public void configChanged(String appName, AgentConfig agentConfig) {
2525
SuspendIgnores.reset(agentConfig);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines.tracing;
2+
3+
import java.lang.instrument.Instrumentation;
4+
import java.util.HashSet;
5+
import java.util.Set;
6+
import java.util.concurrent.Executors;
7+
import java.util.concurrent.ScheduledExecutorService;
8+
import java.util.concurrent.TimeUnit;
9+
import java.util.logging.Level;
10+
11+
import com.newrelic.agent.TracerService;
12+
import com.newrelic.agent.core.CoreService;
13+
import com.newrelic.agent.instrumentation.ClassTransformerService;
14+
import com.newrelic.agent.instrumentation.context.ClassMatchVisitorFactory;
15+
import com.newrelic.agent.instrumentation.context.InstrumentationContextManager;
16+
import com.newrelic.agent.service.ServiceFactory;
17+
import com.newrelic.api.agent.NewRelic;
18+
19+
public class CoroutinesPreMain {
20+
21+
private static int max_retries = 20;
22+
private static ScheduledExecutorService executor = null;
23+
24+
public static void premain(String args, Instrumentation inst) {
25+
boolean b = setup();
26+
if(!b) {
27+
executor = Executors.newSingleThreadScheduledExecutor();
28+
executor.schedule(new Setup(), 100L, TimeUnit.MILLISECONDS);
29+
}
30+
31+
}
32+
33+
public static boolean setup() {
34+
35+
TracerService tracerService = ServiceFactory.getTracerService();
36+
ClassTransformerService classTransformerService = ServiceFactory.getClassTransformerService();
37+
CoreService coreService = ServiceFactory.getCoreService();
38+
39+
if(tracerService != null && classTransformerService != null && coreService != null) {
40+
tracerService.registerTracerFactory(SuspendTracerFactory.TRACER_FACTORY_NAME, new SuspendTracerFactory());
41+
InstrumentationContextManager contextMgr = classTransformerService.getContextManager();
42+
43+
if(contextMgr != null) {
44+
SuspendClassTransformer suspendTransformer = new SuspendClassTransformer(contextMgr);
45+
SuspendClassAndMethod suspendMatcher = new SuspendClassAndMethod();
46+
ClassMatchVisitorFactory suspendMatchVistor = suspendTransformer.addMatcher(suspendMatcher);
47+
48+
Set<ClassMatchVisitorFactory> factories = new HashSet<>();
49+
factories.add(suspendMatchVistor);
50+
Class<?>[] allLoadedClasses = coreService.getInstrumentation().getAllLoadedClasses();
51+
52+
classTransformerService.retransformMatchingClassesImmediately(allLoadedClasses, factories);
53+
54+
return true;
55+
}
56+
}
57+
58+
return false;
59+
}
60+
61+
private static class Setup implements Runnable {
62+
63+
private static int count = 0;
64+
65+
@Override
66+
public void run() {
67+
count++;
68+
NewRelic.getAgent().getLogger().log(Level.FINE, "Call {0} to attempt setting up Suspend ClassTransformer",count);
69+
boolean b = setup();
70+
71+
if(!b) {
72+
if(count < max_retries) {
73+
executor.schedule(this, 2L, TimeUnit.SECONDS);
74+
} else {
75+
NewRelic.getAgent().getLogger().log(Level.FINE, "Failed to initiate Suspend Client Transformer after {0} tries", max_retries);
76+
executor.shutdownNow();
77+
}
78+
}
79+
80+
}
81+
82+
}
83+
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines.tracing;
2+
3+
import com.newrelic.agent.instrumentation.classmatchers.ClassAndMethodMatcher;
4+
import com.newrelic.agent.instrumentation.classmatchers.ClassMatcher;
5+
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;
6+
7+
public class SuspendClassAndMethod implements ClassAndMethodMatcher {
8+
9+
private ClassMatcher classMatcher;
10+
private MethodMatcher methodMatcher;
11+
12+
public SuspendClassAndMethod() {
13+
classMatcher = new SuspendClassMatcher();
14+
methodMatcher = new SuspendMethodMatcher();
15+
}
16+
17+
@Override
18+
public ClassMatcher getClassMatcher() {
19+
return classMatcher;
20+
}
21+
22+
@Override
23+
public MethodMatcher getMethodMatcher() {
24+
return methodMatcher;
25+
}
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines.tracing;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
6+
import com.newrelic.agent.deps.org.objectweb.asm.ClassReader;
7+
import com.newrelic.agent.instrumentation.classmatchers.ChildClassMatcher;
8+
import com.newrelic.agent.instrumentation.classmatchers.ClassMatcher;
9+
import com.newrelic.agent.instrumentation.classmatchers.ExactClassMatcher;
10+
import com.newrelic.agent.instrumentation.classmatchers.NotMatcher;
11+
12+
public class SuspendClassMatcher extends ClassMatcher {
13+
14+
ChildClassMatcher matcher;
15+
NotMatcher nrMatcher;
16+
17+
public SuspendClassMatcher() {
18+
matcher = new ChildClassMatcher("kotlin.coroutines.jvm.internal.BaseContinuationImpl",false);
19+
nrMatcher = new NotMatcher(new ExactClassMatcher("com.newrelic.instrumentation.kotlin.coroutines.NRWrappedSuspend"));
20+
}
21+
22+
@Override
23+
public boolean isMatch(ClassLoader loader, ClassReader cr) {
24+
return matcher.isMatch(loader, cr) && nrMatcher.isMatch(loader, cr);
25+
}
26+
27+
@Override
28+
public boolean isMatch(Class<?> clazz) {
29+
return matcher.isMatch(clazz) && nrMatcher.isMatch(clazz);
30+
}
31+
32+
@Override
33+
public Collection<String> getClassNames() {
34+
Collection<String> childClasses = matcher.getClassNames();
35+
Collection<String> nrClasses = nrMatcher.getClassNames();
36+
if(childClasses == null && nrClasses == null) return null;
37+
38+
ArrayList<String> list = new ArrayList<>();
39+
if(childClasses != null) {
40+
list.addAll(childClasses);
41+
}
42+
if(nrClasses != null) {
43+
list.addAll(nrClasses);
44+
}
45+
46+
return list;
47+
}
48+
49+
}

Kotlin-Coroutines-Suspends/src/main/java/com/newrelic/instrumentation/kotlin/coroutines/tracing/SuspendClassTransformer.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ public byte[] transform(ClassLoader loader, String className, Class<?> classBein
5454
for(ClassAndMethodMatcher matcher : match.getClassMatches().keySet()) {
5555
if (matcher.getMethodMatcher().matches(MethodMatcher.UNSPECIFIED_ACCESS, method.getName(),
5656
method.getDescriptor(), match.getMethodAnnotations(method))) {
57-
58-
// context.putTraceAnnotation(method, TraceDetailsBuilder.newBuilder().setTracerFactoryName(SuspendTracerFactory.TRACER_FACTORY_NAME).setDispatcher(true).setInstrumentationSourceName("CoroutinesCore").setInstrumentationType(InstrumentationType.TraceAnnotation).build());
57+
context.putTraceAnnotation(method, TraceDetailsBuilder.newBuilder().setTracerFactoryName(SuspendTracerFactory.TRACER_FACTORY_NAME).setDispatcher(true).setInstrumentationSourceName("CoroutinesCore").setInstrumentationType(InstrumentationType.TraceAnnotation).build());
5958
}
6059

6160
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines.tracing;
2+
3+
import java.util.Set;
4+
5+
import com.newrelic.agent.deps.org.objectweb.asm.commons.Method;
6+
import com.newrelic.agent.instrumentation.methodmatchers.MethodMatcher;
7+
import com.newrelic.agent.instrumentation.methodmatchers.NameMethodMatcher;
8+
9+
public class SuspendMethodMatcher implements MethodMatcher {
10+
11+
NameMethodMatcher matcher = null;
12+
13+
public SuspendMethodMatcher() {
14+
matcher = new NameMethodMatcher("invokeSuspend");
15+
}
16+
17+
@Override
18+
public boolean matches(int access, String name, String desc, Set<String> annotations) {
19+
return matcher.matches(access, name, desc, annotations);
20+
}
21+
22+
@Override
23+
public Method[] getExactMethods() {
24+
return matcher.getExactMethods();
25+
}
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.newrelic.instrumentation.kotlin.coroutines.tracing;
2+
3+
import com.newrelic.agent.Transaction;
4+
import com.newrelic.agent.tracers.ClassMethodSignature;
5+
import com.newrelic.agent.tracers.DefaultTracer;
6+
import com.newrelic.agent.tracers.Tracer;
7+
import com.newrelic.agent.tracers.TracerFactory;
8+
import com.newrelic.agent.tracers.metricname.SimpleMetricNameFormat;
9+
import com.newrelic.instrumentation.kotlin.coroutines.SuspendIgnores;
10+
import com.newrelic.instrumentation.kotlin.coroutines.Utils;
11+
12+
public class SuspendTracerFactory implements TracerFactory {
13+
14+
protected static final String TRACER_FACTORY_NAME = "SUSPEND_TRACER_FACTORY";
15+
16+
@Override
17+
public Tracer getTracer(Transaction transaction, ClassMethodSignature sig, Object object, Object[] args) {
18+
19+
if(SuspendIgnores.ignoreSuspend(object)) {
20+
return null;
21+
}
22+
return new DefaultTracer(transaction, sig, object, new SimpleMetricNameFormat("Custom/SuspendFunction/"+Utils.getSuspendString(object.toString(), object)));
23+
}
24+
25+
}

0 commit comments

Comments
 (0)