Skip to content

Commit

Permalink
add instrumentation for making the current thread eligible for wallcl…
Browse files Browse the repository at this point in the history
…ock profiling
  • Loading branch information
richardstartin committed Apr 26, 2024
1 parent e971604 commit a06db62
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ muzzle {
versions = "[,]"
assertInverse = false
}
pass {
coreJdk()
}
}

addTestSuiteForDir('latestDep4Test', 'test')
addTestSuiteForDir('latestDepTest', 'test')

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

testImplementation group: 'io.netty', name: 'netty-transport', version: '4.1.0.Final'
dependencies {
testImplementation project(':dd-java-agent:instrumentation:trace-annotation')
testImplementation group: 'io.netty', name: 'netty-all', version: '4.1.108.Final'
testImplementation group: 'io.netty', name: 'netty-transport', version: '4.1.108.Final'

latestDep4TestImplementation group: 'io.netty', name: 'netty-transport', version: '4.+'
latestDepTestImplementation group: 'io.netty', name: 'netty-transport', version: '+'
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package datadog.trace.instrumentation.wallclock;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf;
import static datadog.trace.api.config.ProfilingConfig.PROFILING_DATADOG_PROFILER_WALL_ENABLED;
import static datadog.trace.api.config.ProfilingConfig.PROFILING_DATADOG_PROFILER_WALL_ENABLED_DEFAULT;
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;
import datadog.trace.api.InstrumenterConfig;
import datadog.trace.bootstrap.config.provider.ConfigProvider;
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import java.util.Arrays;
import net.bytebuddy.asm.Advice;

@AutoService(InstrumenterModule.class)
public class EnableWallclockProfilingInstrumentation extends InstrumenterModule.Profiling
implements Instrumenter.ForKnownTypes {

public EnableWallclockProfilingInstrumentation() {
super("wallclock");
}

private static final String[] RUNNABLE_EVENT_LOOPS = {
// regular netty
"io.netty.channel.ThreadPerChannelEventLoop",
"io.netty.channel.nio.NioEventLoop",
"io.netty.channel.epoll.EPollEventLoop",
"io.netty.channel.kqueue.KQueueEventLoop",
// gRPC shades the same classes
"io.grpc.netty.shaded.io.netty.channel.ThreadPerChannelEventLoop",
"io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop",
"io.grpc.netty.shaded.io.netty.channel.epoll.EPollEventLoop",
"io.grpc.netty.shaded.io.netty.channel.kqueue.KQueueEventLoop"
};

@Override
public boolean isEnabled() {
// only needed if wallclock profiling is enabled, which requires tracing
return super.isEnabled()
&& ConfigProvider.getInstance()
.getBoolean(
PROFILING_DATADOG_PROFILER_WALL_ENABLED,
PROFILING_DATADOG_PROFILER_WALL_ENABLED_DEFAULT)
&& InstrumenterConfig.get().isTraceEnabled();
}

@Override
public void methodAdvice(MethodTransformer transformer) {
String adviceClassName = getClass().getName() + "$EnableWallclockSampling";
transformer.applyAdvice(
isMethod()
.and(
named("run")
.and(isDeclaredBy(namedOneOf(RUNNABLE_EVENT_LOOPS)))
.and(takesNoArguments())),
adviceClassName);
transformer.applyAdvice(
isMethod()
.and(named("dowait"))
.and(takesArguments(boolean.class, long.class))
.and(isDeclaredBy(named("java.util.concurrent.CyclicBarrier"))),
adviceClassName);
transformer.applyAdvice(
isMethod()
.and(named("await"))
.and(isDeclaredBy(named("java.util.concurrent.CountDownLatch"))),
adviceClassName);
}

@Override
public String[] knownMatchingTypes() {
String[] all = Arrays.copyOf(RUNNABLE_EVENT_LOOPS, RUNNABLE_EVENT_LOOPS.length + 2);
all[RUNNABLE_EVENT_LOOPS.length] = "java.util.concurrent.CyclicBarrier";
all[RUNNABLE_EVENT_LOOPS.length + 1] = "java.util.concurrent.CountDownLatch";
return all;
}

public static final class EnableWallclockSampling {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static boolean before() {
AgentScope active = AgentTracer.activeScope();
if (active == null) {
AgentTracer.get().getProfilingContext().onAttach();
return true;
}
return false;
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void after(@Advice.Enter boolean wasDisabled) {
if (wasDisabled) {
AgentTracer.get().getProfilingContext().onDetach();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import datadog.trace.agent.test.AgentTestRunner
import io.netty.channel.nio.NioEventLoopGroup

import java.util.concurrent.CountDownLatch
import java.util.concurrent.CyclicBarrier
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

class RegistrationForkedTest extends AgentTestRunner {

@Override
protected void configurePreAgent() {
injectSysConfig("dd.profiling.enabled", "true")
super.configurePreAgent()
}

def "test thread filter updates"() {
setup:
NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1)
CyclicBarrier barrier = new CyclicBarrier(2)
CountDownLatch latch = new CountDownLatch(2)
ExecutorService executorService = Executors.newFixedThreadPool(2)

when: "run nio event loop"
TEST_PROFILING_CONTEXT_INTEGRATION.clear()
nioEventLoopGroup.execute {}
boolean shutdownGracefully = nioEventLoopGroup.shutdownGracefully().await(5, TimeUnit.SECONDS)

then:
shutdownGracefully
TEST_PROFILING_CONTEXT_INTEGRATION.attachments.get() == 1
TEST_PROFILING_CONTEXT_INTEGRATION.detachments.get() == 1

when: "await cyclic barrier"
TEST_PROFILING_CONTEXT_INTEGRATION.clear()
def f1 = executorService.submit {barrier.await()}
def f2 = executorService.submit {barrier.await()}
f1.get(5, TimeUnit.SECONDS)
f2.get(5, TimeUnit.SECONDS)

then:
TEST_PROFILING_CONTEXT_INTEGRATION.attachments.get() == 2
TEST_PROFILING_CONTEXT_INTEGRATION.detachments.get() == 2

when: "await countdown latch"
TEST_PROFILING_CONTEXT_INTEGRATION.clear()
def f3 = executorService.submit {latch.await()}
def f4 = executorService.submit {latch.await()}
latch.countDown()
latch.countDown()
f3.get(5, TimeUnit.SECONDS)
f4.get(5, TimeUnit.SECONDS)

then:
TEST_PROFILING_CONTEXT_INTEGRATION.attachments.get() == 2
TEST_PROFILING_CONTEXT_INTEGRATION.detachments.get() == 2

cleanup:
executorService.shutdownNow()
TEST_PROFILING_CONTEXT_INTEGRATION.clear()
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ class TestProfilingContextIntegration implements ProfilingContextIntegration {
detachments.incrementAndGet()
}

void clear() {
attachments.set(0)
detachments.set(0)
}

@Override
String name() {
return "test"
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ include ':dd-java-agent:instrumentation:elasticsearch:transport-5'
include ':dd-java-agent:instrumentation:elasticsearch:transport-5.3'
include ':dd-java-agent:instrumentation:elasticsearch:transport-6'
include ':dd-java-agent:instrumentation:elasticsearch:transport-7.3'
include ':dd-java-agent:instrumentation:enable-wallclock-profiling'
include ':dd-java-agent:instrumentation:exception-profiling'
include ':dd-java-agent:instrumentation:finatra-2.9'
include ':dd-java-agent:instrumentation:freemarker'
Expand Down Expand Up @@ -333,7 +334,6 @@ include ':dd-java-agent:instrumentation:netty-4.1'
include ':dd-java-agent:instrumentation:netty-buffer-4'
include ':dd-java-agent:instrumentation:netty-concurrent-4'
include ':dd-java-agent:instrumentation:netty-promise-4'
include ':dd-java-agent:instrumentation:netty-transport-4'
include ':dd-java-agent:instrumentation:okhttp-2'
include ':dd-java-agent:instrumentation:okhttp-3'
include ':dd-java-agent:instrumentation:ognl-appsec'
Expand Down

0 comments on commit a06db62

Please sign in to comment.