Skip to content

Commit f6f3414

Browse files
Merge pull request DataDog#6261 from DataDog/rgs/jfr-trace-context
JFR based profiling context
2 parents 4078e55 + d4f2456 commit f6f3414

18 files changed

Lines changed: 254 additions & 110 deletions

File tree

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -853,15 +853,29 @@ public void withTracer(TracerAPI tracer) {
853853
* on JFR.
854854
*/
855855
private static ProfilingContextIntegration createProfilingContextIntegration() {
856-
if (Config.get().isProfilingEnabled() && !Platform.isWindows()) {
857-
try {
858-
return (ProfilingContextIntegration)
859-
AGENT_CLASSLOADER
860-
.loadClass("com.datadog.profiling.ddprof.DatadogProfilingIntegration")
861-
.getDeclaredConstructor()
862-
.newInstance();
863-
} catch (Throwable t) {
864-
log.debug("Profiling context labeling not available. {}", t.getMessage());
856+
if (Config.get().isProfilingEnabled()) {
857+
if (Config.get().isDatadogProfilerEnabled() && !Platform.isWindows()) {
858+
try {
859+
return (ProfilingContextIntegration)
860+
AGENT_CLASSLOADER
861+
.loadClass("com.datadog.profiling.ddprof.DatadogProfilingIntegration")
862+
.getDeclaredConstructor()
863+
.newInstance();
864+
} catch (Throwable t) {
865+
log.debug("ddprof-based profiling context labeling not available. {}", t.getMessage());
866+
}
867+
}
868+
if (Config.get().isProfilingTimelineEventsEnabled()) {
869+
// important: note that this will not initialise JFR until onStart is called
870+
try {
871+
return (ProfilingContextIntegration)
872+
AGENT_CLASSLOADER
873+
.loadClass("com.datadog.profiling.controller.openjdk.JFREventContextIntegration")
874+
.getDeclaredConstructor()
875+
.newInstance();
876+
} catch (Throwable t) {
877+
log.debug("JFR event-based profiling context labeling not available. {}", t.getMessage());
878+
}
865879
}
866880
}
867881
return ProfilingContextIntegration.NoOp.INSTANCE;
@@ -922,6 +936,7 @@ public void withTracer(TracerAPI tracer) {
922936
.getDeclaredConstructor()
923937
.newInstance());
924938
}
939+
tracer.getProfilingContext().onStart();
925940
} catch (Throwable e) {
926941
if (e instanceof InvocationTargetException) {
927942
e = e.getCause();

dd-java-agent/agent-profiling/profiling-controller-jfr/src/main/resources/jfr/dd.jfp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,5 @@ datadog.ExceptionSample#enabled=true
248248
datadog.ExceptionCount#enabled=true
249249
datadog.ProfilerSetting#enabled=true
250250
datadog.ContextInterval#enabled=true
251+
datadog.Timeline#enabled=true
252+
datadog.Timeline#threshold=10 ms
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.datadog.profiling.controller.openjdk;
2+
3+
import com.datadog.profiling.controller.openjdk.events.TimelineEvent;
4+
import com.datadog.profiling.utils.ExcludedVersions;
5+
import datadog.trace.api.Stateful;
6+
import datadog.trace.bootstrap.instrumentation.api.ProfilerContext;
7+
import datadog.trace.bootstrap.instrumentation.api.ProfilingContextIntegration;
8+
9+
public class JFREventContextIntegration implements ProfilingContextIntegration {
10+
11+
public JFREventContextIntegration() {
12+
ExcludedVersions.checkVersionExclusion();
13+
}
14+
15+
private volatile boolean isStarted = false;
16+
17+
@Override
18+
public void onStart() {
19+
// avoid initialising JFR until called
20+
isStarted = true;
21+
}
22+
23+
@Override
24+
public Stateful newScopeState(ProfilerContext profilerContext) {
25+
if (!isStarted) {
26+
return Stateful.DEFAULT;
27+
}
28+
return new TimelineEvent(
29+
profilerContext.getRootSpanId(),
30+
profilerContext.getSpanId(),
31+
String.valueOf(profilerContext.getOperationName()));
32+
}
33+
34+
@Override
35+
public String name() {
36+
return "jfr";
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.datadog.profiling.controller.openjdk.events;
2+
3+
import datadog.trace.api.Stateful;
4+
import jdk.jfr.Category;
5+
import jdk.jfr.Description;
6+
import jdk.jfr.Event;
7+
import jdk.jfr.Label;
8+
import jdk.jfr.Name;
9+
import jdk.jfr.StackTrace;
10+
11+
@Name("datadog.Timeline")
12+
@Label("Profiler Timeline Event")
13+
@Description("Datadog profiler timeline event")
14+
@Category("Datadog")
15+
@StackTrace(false)
16+
public class TimelineEvent extends Event implements Stateful {
17+
18+
@Label("Local Root Span Id")
19+
private final long localRootSpanId;
20+
21+
@Label("Span Id")
22+
private final long spanId;
23+
24+
@Label("Span Name")
25+
@Name("_dd.trace.operation")
26+
private final String operation;
27+
28+
public TimelineEvent(long localRootSpanId, long spanId, String operation) {
29+
this.localRootSpanId = localRootSpanId;
30+
this.spanId = spanId;
31+
this.operation = operation;
32+
begin();
33+
}
34+
35+
@Override
36+
public void close() {
37+
end();
38+
if (shouldCommit()) {
39+
commit();
40+
}
41+
}
42+
43+
@Override
44+
public void activate(Object context) {
45+
// nothing to do, either we get an event or we don't
46+
}
47+
}

dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilingIntegration.java

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.datadog.profiling.ddprof;
22

3+
import datadog.trace.api.Stateful;
34
import datadog.trace.api.profiling.ProfilingContextAttribute;
45
import datadog.trace.api.profiling.ProfilingScope;
56
import datadog.trace.bootstrap.instrumentation.api.ProfilerContext;
@@ -17,6 +18,29 @@ public class DatadogProfilingIntegration implements ProfilingContextIntegration
1718
private static final boolean WALLCLOCK_ENABLED =
1819
DatadogProfilerConfig.isWallClockProfilerEnabled();
1920

21+
private final Stateful contextManager =
22+
new Stateful() {
23+
@Override
24+
public void close() {
25+
// this implementation is stateless so nothing to do here
26+
}
27+
28+
@Override
29+
public void activate(Object context) {
30+
if (context instanceof ProfilerContext) {
31+
ProfilerContext profilerContext = (ProfilerContext) context;
32+
DDPROF.setSpanContext(profilerContext.getSpanId(), profilerContext.getRootSpanId());
33+
DDPROF.setContextValue(SPAN_NAME_INDEX, profilerContext.getEncodedOperationName());
34+
DDPROF.setContextValue(RESOURCE_NAME_INDEX, profilerContext.getEncodedResourceName());
35+
}
36+
}
37+
};
38+
39+
@Override
40+
public Stateful newScopeState(ProfilerContext profilerContext) {
41+
return contextManager;
42+
}
43+
2044
@Override
2145
public void onAttach() {
2246
if (WALLCLOCK_ENABLED) {
@@ -26,6 +50,7 @@ public void onAttach() {
2650

2751
@Override
2852
public void onDetach() {
53+
clearContext();
2954
if (WALLCLOCK_ENABLED) {
3055
DDPROF.removeThread();
3156
}
@@ -57,25 +82,12 @@ public String name() {
5782
return "ddprof";
5883
}
5984

60-
@Override
61-
public void setContext(ProfilerContext profilerContext) {
62-
DDPROF.setSpanContext(profilerContext.getSpanId(), profilerContext.getRootSpanId());
63-
DDPROF.setContextValue(SPAN_NAME_INDEX, profilerContext.getEncodedOperationName());
64-
DDPROF.setContextValue(RESOURCE_NAME_INDEX, profilerContext.getEncodedResourceName());
65-
}
66-
67-
@Override
6885
public void clearContext() {
6986
DDPROF.clearSpanContext();
7087
DDPROF.clearContextValue(SPAN_NAME_INDEX);
7188
DDPROF.clearContextValue(RESOURCE_NAME_INDEX);
7289
}
7390

74-
@Override
75-
public void setContext(long rootSpanId, long spanId) {
76-
DDPROF.setSpanContext(spanId, rootSpanId);
77-
}
78-
7991
@Override
8092
public ProfilingContextAttribute createContextAttribute(String attribute) {
8193
return new DatadogProfilerContextSetter(attribute, DDPROF);

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

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package datadog.trace.agent.test
22

33
import datadog.trace.api.profiling.ProfilingContextAttribute
44
import datadog.trace.api.profiling.ProfilingScope
5-
import datadog.trace.bootstrap.instrumentation.api.ProfilerContext
65
import datadog.trace.bootstrap.instrumentation.api.ProfilingContextIntegration
76

87
import java.util.concurrent.atomic.AtomicInteger
@@ -20,18 +19,6 @@ class TestProfilingContextIntegration implements ProfilingContextIntegration {
2019
detachments.incrementAndGet()
2120
}
2221

23-
@Override
24-
void setContext(ProfilerContext profilerContext) {
25-
}
26-
27-
@Override
28-
void clearContext() {
29-
}
30-
31-
@Override
32-
void setContext(long rootSpanId, long spanId) {
33-
}
34-
3522
@Override
3623
String name() {
3724
return "test"

dd-trace-api/src/main/java/datadog/trace/api/config/ProfilingConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,5 +193,9 @@ public final class ProfilingConfig {
193193
public static final String PROFILING_HEAP_HISTOGRAM_MODE = "profiling.heap.histogram.mode";
194194
public static final String PROFILING_HEAP_HISTOGRAM_MODE_DEFAULT = "aftergc";
195195

196+
public static final String PROFILING_TIMELINE_EVENTS_ENABLED =
197+
"profiling.timeline.events.enabled";
198+
public static final boolean PROFILING_TIMELINE_EVENTS_ENABLED_DEFAULT = false;
199+
196200
private ProfilingConfig() {}
197201
}

dd-trace-api/src/main/java/datadog/trace/api/profiling/Profiling.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ static Profiling get() {
2020
*
2121
* @return a profiling scope which can be closed to restore the current state.
2222
*/
23-
ProfilingScope newScope();
23+
default ProfilingScope newScope() {
24+
return ProfilingScope.NO_OP;
25+
}
2426

2527
/**
2628
* Creates a decorator for the attribute, which can be used to set and clear contexts slightly
@@ -29,20 +31,11 @@ static Profiling get() {
2931
* @param attribute the name of the attribute
3032
* @return a setter which can be used to set and clear profiling context
3133
*/
32-
ProfilingContextAttribute createContextAttribute(String attribute);
34+
default ProfilingContextAttribute createContextAttribute(String attribute) {
35+
return ProfilingContextAttribute.NoOp.INSTANCE;
36+
}
3337

3438
final class NoOp implements Profiling {
35-
3639
public static final NoOp INSTANCE = new NoOp();
37-
38-
@Override
39-
public ProfilingContextAttribute createContextAttribute(String attribute) {
40-
return ProfilingContextAttribute.NoOp.INSTANCE;
41-
}
42-
43-
@Override
44-
public ProfilingScope newScope() {
45-
return ProfilingScope.NO_OP;
46-
}
4740
}
4841
}

dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScope.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.core.scopemanager;
22

3+
import datadog.trace.api.Stateful;
34
import datadog.trace.api.scopemanager.ExtendedScopeListener;
45
import datadog.trace.api.scopemanager.ScopeListener;
56
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
@@ -25,15 +26,19 @@ class ContinuableScope implements AgentScope, AttachableWrapper {
2526
private static final AtomicReferenceFieldUpdater<ContinuableScope, Object> WRAPPER_FIELD_UPDATER =
2627
AtomicReferenceFieldUpdater.newUpdater(ContinuableScope.class, Object.class, "wrapper");
2728

29+
private final Stateful scopeState;
30+
2831
ContinuableScope(
2932
final ContinuableScopeManager scopeManager,
3033
final AgentSpan span,
3134
final byte source,
32-
final boolean isAsyncPropagating) {
35+
final boolean isAsyncPropagating,
36+
final Stateful scopeState) {
3337
this.scopeManager = scopeManager;
3438
this.span = span;
3539
this.flags = source;
3640
this.isAsyncPropagating = isAsyncPropagating;
41+
this.scopeState = scopeState;
3742
}
3843

3944
@Override
@@ -59,6 +64,7 @@ public final void close() {
5964
if (!alive) {
6065
cleanup(scopeStack);
6166
}
67+
scopeState.close();
6268
}
6369

6470
void cleanup(final ScopeStack scopeStack) {
@@ -152,6 +158,15 @@ public final String toString() {
152158
return super.toString() + "->" + span;
153159
}
154160

161+
public final void beforeActivated() {
162+
try {
163+
scopeState.activate(span.context());
164+
} catch (Throwable e) {
165+
ContinuableScopeManager.ratelimitedLog.warn(
166+
"ScopeState {} threw exception in beforeActivated()", scopeState.getClass(), e);
167+
}
168+
}
169+
155170
public final void afterActivated() {
156171
for (final ScopeListener listener : scopeManager.scopeListeners) {
157172
try {

0 commit comments

Comments
 (0)