Skip to content

Commit a481d0b

Browse files
add instrumentation for making the current thread eligible for wallclock profiling (#6941)
1 parent ae1c4c9 commit a481d0b

File tree

8 files changed

+228
-117
lines changed

8 files changed

+228
-117
lines changed

dd-java-agent/instrumentation/netty-transport-4/build.gradle renamed to dd-java-agent/instrumentation/enable-wallclock-profiling/build.gradle

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,19 @@ muzzle {
1313
versions = "[,]"
1414
assertInverse = false
1515
}
16+
pass {
17+
coreJdk()
18+
}
1619
}
1720

1821
addTestSuiteForDir('latestDep4Test', 'test')
1922
addTestSuiteForDir('latestDepTest', 'test')
2023

21-
dependencies {
22-
compileOnly group: 'io.netty', name: 'netty-transport', version: '4.0.56.Final'
2324

24-
testImplementation group: 'io.netty', name: 'netty-transport', version: '4.1.0.Final'
25+
dependencies {
26+
testImplementation project(':dd-java-agent:instrumentation:trace-annotation')
27+
testImplementation group: 'io.netty', name: 'netty-all', version: '4.1.108.Final'
28+
testImplementation group: 'io.netty', name: 'netty-transport', version: '4.1.108.Final'
2529

2630
latestDep4TestImplementation group: 'io.netty', name: 'netty-transport', version: '4.+'
2731
latestDepTestImplementation group: 'io.netty', name: 'netty-transport', version: '+'

dd-java-agent/instrumentation/netty-transport-4/gradle.lockfile renamed to dd-java-agent/instrumentation/enable-wallclock-profiling/gradle.lockfile

Lines changed: 48 additions & 27 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package datadog.trace.instrumentation.wallclock;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
4+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf;
5+
import static datadog.trace.api.config.ProfilingConfig.PROFILING_DATADOG_PROFILER_WALL_ENABLED;
6+
import static datadog.trace.api.config.ProfilingConfig.PROFILING_DATADOG_PROFILER_WALL_ENABLED_DEFAULT;
7+
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
8+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
9+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
10+
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
11+
12+
import com.google.auto.service.AutoService;
13+
import datadog.trace.agent.tooling.Instrumenter;
14+
import datadog.trace.agent.tooling.InstrumenterModule;
15+
import datadog.trace.api.InstrumenterConfig;
16+
import datadog.trace.bootstrap.config.provider.ConfigProvider;
17+
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
18+
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
19+
import java.util.Arrays;
20+
import net.bytebuddy.asm.Advice;
21+
22+
@AutoService(InstrumenterModule.class)
23+
public class EnableWallclockProfilingInstrumentation extends InstrumenterModule.Profiling
24+
implements Instrumenter.ForKnownTypes {
25+
26+
public EnableWallclockProfilingInstrumentation() {
27+
super("wallclock");
28+
}
29+
30+
private static final String[] RUNNABLE_EVENT_LOOPS = {
31+
// regular netty
32+
"io.netty.channel.ThreadPerChannelEventLoop",
33+
"io.netty.channel.nio.NioEventLoop",
34+
"io.netty.channel.epoll.EPollEventLoop",
35+
"io.netty.channel.kqueue.KQueueEventLoop",
36+
// gRPC shades the same classes
37+
"io.grpc.netty.shaded.io.netty.channel.ThreadPerChannelEventLoop",
38+
"io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop",
39+
"io.grpc.netty.shaded.io.netty.channel.epoll.EPollEventLoop",
40+
"io.grpc.netty.shaded.io.netty.channel.kqueue.KQueueEventLoop"
41+
};
42+
43+
@Override
44+
public boolean isEnabled() {
45+
// only needed if wallclock profiling is enabled, which requires tracing
46+
return super.isEnabled()
47+
&& ConfigProvider.getInstance()
48+
.getBoolean(
49+
PROFILING_DATADOG_PROFILER_WALL_ENABLED,
50+
PROFILING_DATADOG_PROFILER_WALL_ENABLED_DEFAULT)
51+
&& InstrumenterConfig.get().isTraceEnabled();
52+
}
53+
54+
@Override
55+
public void methodAdvice(MethodTransformer transformer) {
56+
String adviceClassName = getClass().getName() + "$EnableWallclockSampling";
57+
transformer.applyAdvice(
58+
isMethod()
59+
.and(
60+
named("run")
61+
.and(isDeclaredBy(namedOneOf(RUNNABLE_EVENT_LOOPS)))
62+
.and(takesNoArguments())),
63+
adviceClassName);
64+
transformer.applyAdvice(
65+
isMethod()
66+
.and(named("dowait"))
67+
.and(takesArguments(boolean.class, long.class))
68+
.and(isDeclaredBy(named("java.util.concurrent.CyclicBarrier"))),
69+
adviceClassName);
70+
transformer.applyAdvice(
71+
isMethod()
72+
.and(named("await"))
73+
.and(isDeclaredBy(named("java.util.concurrent.CountDownLatch"))),
74+
adviceClassName);
75+
}
76+
77+
@Override
78+
public String[] knownMatchingTypes() {
79+
String[] all = Arrays.copyOf(RUNNABLE_EVENT_LOOPS, RUNNABLE_EVENT_LOOPS.length + 2);
80+
all[RUNNABLE_EVENT_LOOPS.length] = "java.util.concurrent.CyclicBarrier";
81+
all[RUNNABLE_EVENT_LOOPS.length + 1] = "java.util.concurrent.CountDownLatch";
82+
return all;
83+
}
84+
85+
public static final class EnableWallclockSampling {
86+
87+
@Advice.OnMethodEnter(suppress = Throwable.class)
88+
public static boolean before() {
89+
AgentScope active = AgentTracer.activeScope();
90+
if (active == null) {
91+
AgentTracer.get().getProfilingContext().onAttach();
92+
return true;
93+
}
94+
return false;
95+
}
96+
97+
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
98+
public static void after(@Advice.Enter boolean wasDisabled) {
99+
if (wasDisabled) {
100+
AgentTracer.get().getProfilingContext().onDetach();
101+
}
102+
}
103+
}
104+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import datadog.trace.agent.test.AgentTestRunner
2+
import io.netty.channel.nio.NioEventLoopGroup
3+
4+
import java.util.concurrent.CountDownLatch
5+
import java.util.concurrent.CyclicBarrier
6+
import java.util.concurrent.ExecutorService
7+
import java.util.concurrent.Executors
8+
import java.util.concurrent.TimeUnit
9+
10+
class RegistrationForkedTest extends AgentTestRunner {
11+
12+
@Override
13+
protected void configurePreAgent() {
14+
injectSysConfig("dd.profiling.enabled", "true")
15+
super.configurePreAgent()
16+
}
17+
18+
def "test thread filter updates"() {
19+
setup:
20+
NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1)
21+
CyclicBarrier barrier = new CyclicBarrier(2)
22+
CountDownLatch latch = new CountDownLatch(2)
23+
ExecutorService executorService = Executors.newFixedThreadPool(2)
24+
25+
when: "run nio event loop"
26+
TEST_PROFILING_CONTEXT_INTEGRATION.clear()
27+
nioEventLoopGroup.execute {}
28+
boolean shutdownGracefully = nioEventLoopGroup.shutdownGracefully().await(5, TimeUnit.SECONDS)
29+
30+
then:
31+
shutdownGracefully
32+
TEST_PROFILING_CONTEXT_INTEGRATION.attachments.get() == 1
33+
TEST_PROFILING_CONTEXT_INTEGRATION.detachments.get() == 1
34+
35+
when: "await cyclic barrier"
36+
TEST_PROFILING_CONTEXT_INTEGRATION.clear()
37+
def f1 = executorService.submit {barrier.await()}
38+
def f2 = executorService.submit {barrier.await()}
39+
f1.get(5, TimeUnit.SECONDS)
40+
f2.get(5, TimeUnit.SECONDS)
41+
42+
then:
43+
TEST_PROFILING_CONTEXT_INTEGRATION.attachments.get() == 2
44+
TEST_PROFILING_CONTEXT_INTEGRATION.detachments.get() == 2
45+
46+
when: "await countdown latch"
47+
TEST_PROFILING_CONTEXT_INTEGRATION.clear()
48+
def f3 = executorService.submit {latch.await()}
49+
def f4 = executorService.submit {latch.await()}
50+
latch.countDown()
51+
latch.countDown()
52+
f3.get(5, TimeUnit.SECONDS)
53+
f4.get(5, TimeUnit.SECONDS)
54+
55+
then:
56+
TEST_PROFILING_CONTEXT_INTEGRATION.attachments.get() == 2
57+
TEST_PROFILING_CONTEXT_INTEGRATION.detachments.get() == 2
58+
59+
cleanup:
60+
executorService.shutdownNow()
61+
TEST_PROFILING_CONTEXT_INTEGRATION.clear()
62+
}
63+
}

dd-java-agent/instrumentation/netty-transport-4/src/main/java/datadog/trace/instrumentation/netty40/transport/EventLoopInstrumentation.java

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

dd-java-agent/instrumentation/netty-transport-4/src/test/groovy/RegistrationTest.groovy

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

dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/TestProfilingContextIntegration.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ class TestProfilingContextIntegration implements ProfilingContextIntegration {
3434
detachments.incrementAndGet()
3535
}
3636

37+
void clear() {
38+
attachments.set(0)
39+
detachments.set(0)
40+
}
41+
3742
@Override
3843
String name() {
3944
return "test"

settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ include ':dd-java-agent:instrumentation:elasticsearch:transport-5'
205205
include ':dd-java-agent:instrumentation:elasticsearch:transport-5.3'
206206
include ':dd-java-agent:instrumentation:elasticsearch:transport-6'
207207
include ':dd-java-agent:instrumentation:elasticsearch:transport-7.3'
208+
include ':dd-java-agent:instrumentation:enable-wallclock-profiling'
208209
include ':dd-java-agent:instrumentation:exception-profiling'
209210
include ':dd-java-agent:instrumentation:finatra-2.9'
210211
include ':dd-java-agent:instrumentation:freemarker'
@@ -333,7 +334,6 @@ include ':dd-java-agent:instrumentation:netty-4.1'
333334
include ':dd-java-agent:instrumentation:netty-buffer-4'
334335
include ':dd-java-agent:instrumentation:netty-concurrent-4'
335336
include ':dd-java-agent:instrumentation:netty-promise-4'
336-
include ':dd-java-agent:instrumentation:netty-transport-4'
337337
include ':dd-java-agent:instrumentation:okhttp-2'
338338
include ':dd-java-agent:instrumentation:okhttp-3'
339339
include ':dd-java-agent:instrumentation:ognl-appsec'

0 commit comments

Comments
 (0)