diff --git a/dd-java-agent/instrumentation/websocket/javax-websocket-1.0/src/main/java/datadog/trace/instrumentation/websocket/jsr256/TracingSendHandler.java b/dd-java-agent/instrumentation/websocket/javax-websocket-1.0/src/main/java/datadog/trace/instrumentation/websocket/jsr256/TracingSendHandler.java index c5a582859a3..83519ad8521 100644 --- a/dd-java-agent/instrumentation/websocket/javax-websocket-1.0/src/main/java/datadog/trace/instrumentation/websocket/jsr256/TracingSendHandler.java +++ b/dd-java-agent/instrumentation/websocket/javax-websocket-1.0/src/main/java/datadog/trace/instrumentation/websocket/jsr256/TracingSendHandler.java @@ -21,6 +21,10 @@ public TracingSendHandler(SendHandler delegate, HandlerContext handlerContext) { @Override public void onResult(SendResult sendResult) { final AgentSpan wsSpan = handlerContext.getWebsocketSpan(); + if (wsSpan == null) { + delegate.onResult(sendResult); + return; + } try (final ContextScope ignored = activateSpan(wsSpan)) { delegate.onResult(sendResult); } finally { diff --git a/dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScopeManager.java b/dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScopeManager.java index 7f0e1a92c1e..e0ac7ea14fa 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScopeManager.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScopeManager.java @@ -112,6 +112,11 @@ private ContextScope activate( final byte source, final boolean overrideAsyncPropagation, final boolean isAsyncPropagating) { + if (span == null) { + log.debug(SEND_TELEMETRY, "Attempted to activate a null span. Returning NoopScope."); + return INVALID_SCOPE; + } + ScopeStack scopeStack = scopeStack(); final ContinuableScope top = scopeStack.top; @@ -130,8 +135,6 @@ private ContextScope activate( } } - assert span != null; - // Inherit the async propagation from the active scope unless the value is overridden boolean asyncPropagation = overrideAsyncPropagation diff --git a/dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerForkedTest.java b/dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerForkedTest.java index 78dbe9d3a7c..4a1eda54ac2 100644 --- a/dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerForkedTest.java +++ b/dd-trace-core/src/test/java/datadog/trace/core/scopemanager/ScopeManagerForkedTest.java @@ -35,6 +35,7 @@ import datadog.trace.api.scopemanager.ScopeListener; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import datadog.trace.bootstrap.instrumentation.api.NoopScope; import datadog.trace.bootstrap.instrumentation.api.ProfilingContextIntegration; import datadog.trace.common.writer.ListWriter; import datadog.trace.core.CoreTracer; @@ -123,6 +124,27 @@ void nonDdspanActivationResultsInAContinuableScope() { assertNull(scopeManager.active()); } + @Test + void activatingNullSpanReturnsNoopScopeAndDoesNotCorruptStack() { + ContextScope nullScope = scopeManager.activateSpan(null); + + assertInstanceOf(NoopScope.class, nullScope); + assertNull(scopeManager.active()); + + nullScope.close(); + + // a subsequent activation on the same thread must not NPE, even though the noop + // activation above never pushed a scope with a null context onto the stack + AgentSpan span = tracer.buildSpan("test", "test").start(); + ContextScope scope = tracer.activateSpan(span); + + assertSame(scope, scopeManager.active()); + assertSame(span, AgentSpan.fromContext(scope.context())); + + scope.close(); + span.finish(); + } + @Test void noScopeIsActiveBeforeActivation() throws Exception { tracer.buildSpan("test", "test").start();