From 3f71147d0aa9823975ec63ed6329d8af40023960 Mon Sep 17 00:00:00 2001 From: Almas Abdrazak Date: Wed, 1 Apr 2026 10:11:57 -0700 Subject: [PATCH 1/3] JAVA-6155 add thread dumps to failed test cases --- .../org.junit.jupiter.api.extension.Extension | 1 + .../test/resources/junit-platform.properties | 1 + .../ThreadDumpOnFailureExtension.java | 57 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 driver-core/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension create mode 100644 driver-core/src/test/resources/junit-platform.properties create mode 100644 driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java diff --git a/driver-core/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/driver-core/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 00000000000..f862b0ebe84 --- /dev/null +++ b/driver-core/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +com.mongodb.internal.diagnostics.ThreadDumpOnFailureExtension diff --git a/driver-core/src/test/resources/junit-platform.properties b/driver-core/src/test/resources/junit-platform.properties new file mode 100644 index 00000000000..6efc0d5e85c --- /dev/null +++ b/driver-core/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.extensions.autodetection.enabled=true diff --git a/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java b/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java new file mode 100644 index 00000000000..a0f465c2f96 --- /dev/null +++ b/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java @@ -0,0 +1,57 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.internal.diagnostics; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + + +import com.mongodb.internal.diagnostics.logging.Logger; +import com.mongodb.internal.diagnostics.logging.Loggers; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestWatcher; + + +/** + * A JUnit 5 extension that prints a thread dump to the log when a test fails. + */ +public final class ThreadDumpOnFailureExtension implements TestWatcher { + + private static final Logger LOGGER = Loggers.getLogger(ThreadDumpOnFailureExtension.class.getName()); + + @Override + public void testFailed(final ExtensionContext context, final Throwable cause) { + String testName = context.getDisplayName(); + String threadDump = getAllThreadsDump(); + LOGGER.error("Test failed: " + testName + "\nThread dump:\n" + threadDump); + } + + private static String getAllThreadsDump() { + final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads( + threadMXBean.isObjectMonitorUsageSupported(), + threadMXBean.isSynchronizerUsageSupported() + ); + StringBuilder sb = new StringBuilder(1024); + for (ThreadInfo info : threadInfos) { + sb.append(info); + } + return sb.toString(); + } +} From 8102cec236271a815a86e96d5ca94b31d22ff1a5 Mon Sep 17 00:00:00 2001 From: Almas Abdrazak Date: Wed, 1 Apr 2026 12:00:42 -0700 Subject: [PATCH 2/3] address copilot review --- .../ThreadDumpOnFailureExtension.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java b/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java index a0f465c2f96..99e586cdb1c 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java +++ b/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java @@ -33,7 +33,7 @@ */ public final class ThreadDumpOnFailureExtension implements TestWatcher { - private static final Logger LOGGER = Loggers.getLogger(ThreadDumpOnFailureExtension.class.getName()); + private static final Logger LOGGER = Loggers.getLogger(ThreadDumpOnFailureExtension.class.getSimpleName()); @Override public void testFailed(final ExtensionContext context, final Throwable cause) { @@ -43,15 +43,19 @@ public void testFailed(final ExtensionContext context, final Throwable cause) { } private static String getAllThreadsDump() { - final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); - ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads( - threadMXBean.isObjectMonitorUsageSupported(), - threadMXBean.isSynchronizerUsageSupported() - ); - StringBuilder sb = new StringBuilder(1024); - for (ThreadInfo info : threadInfos) { - sb.append(info); + try { + final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads( + threadMXBean.isObjectMonitorUsageSupported(), + threadMXBean.isSynchronizerUsageSupported() + ); + StringBuilder sb = new StringBuilder(1024); + for (ThreadInfo info : threadInfos) { + sb.append(info); + } + return sb.toString(); + } catch (final SecurityException exc) { + return "Unable to get thread dump due to security manager restrictions: " + exc.getMessage(); } - return sb.toString(); } } From 8a9f055c6923896c99fd3409bd0d63290b6b8e98 Mon Sep 17 00:00:00 2001 From: Almas Abdrazak Date: Tue, 7 Jul 2026 21:20:03 -0700 Subject: [PATCH 3/3] thread dump can be disabled --- .evergreen/run-csfle-tests-with-mongocryptd.sh | 1 + .evergreen/run-tests.sh | 1 + .../diagnostics/ThreadDumpOnFailureExtension.java | 10 ++++++++++ 3 files changed, 12 insertions(+) diff --git a/.evergreen/run-csfle-tests-with-mongocryptd.sh b/.evergreen/run-csfle-tests-with-mongocryptd.sh index a5322955ff7..8979c1759e9 100755 --- a/.evergreen/run-csfle-tests-with-mongocryptd.sh +++ b/.evergreen/run-csfle-tests-with-mongocryptd.sh @@ -67,6 +67,7 @@ echo "Running tests with Java ${JAVA_VERSION}" ./gradlew -PjavaVersion=${JAVA_VERSION} -Dorg.mongodb.test.uri=${MONGODB_URI} \ ${GRADLE_EXTRA_VARS} \ -Dorg.mongodb.test.fle.on.demand.credential.test.failure.enabled=true \ + -Dorg.mongodb.test.diagnostics.thread.dump.enabled=true \ --stacktrace --info --continue \ driver-sync:test \ --tests "*.Client*Encryption*" \ diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index 0ee14ed01dc..f97ff5863a2 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -142,6 +142,7 @@ echo "Running tests with Java ${JAVA_VERSION}" ${MULTI_MONGOS_URI_SYSTEM_PROPERTY} ${API_VERSION} ${GRADLE_EXTRA_VARS} \ ${JAVA_SYSPROP_ASYNC_TRANSPORT} ${JAVA_SYSPROP_NETTY_SSL_PROVIDER} \ -Dorg.mongodb.test.fle.on.demand.credential.test.failure.enabled=true \ + -Dorg.mongodb.test.diagnostics.thread.dump.enabled=true \ --stacktrace --info --continue ${TESTS} | tee -a logs.txt if grep -q 'LEAK:' logs.txt ; then diff --git a/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java b/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java index 99e586cdb1c..6fc1036d413 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java +++ b/driver-core/src/test/unit/com/mongodb/internal/diagnostics/ThreadDumpOnFailureExtension.java @@ -30,13 +30,23 @@ /** * A JUnit 5 extension that prints a thread dump to the log when a test fails. + * + *

The extension is auto-detected on every test run, but is inert unless explicitly enabled via the + * {@value #ENABLED_PROPERTY} system property. This keeps local runs quiet while allowing Evergreen (which sets the + * property) — or a developer who opts in with {@code -Dorg.mongodb.test.diagnostics.thread.dump.enabled=true} — to + * capture thread dumps for failing tests. */ public final class ThreadDumpOnFailureExtension implements TestWatcher { + static final String ENABLED_PROPERTY = "org.mongodb.test.diagnostics.thread.dump.enabled"; + private static final Logger LOGGER = Loggers.getLogger(ThreadDumpOnFailureExtension.class.getSimpleName()); @Override public void testFailed(final ExtensionContext context, final Throwable cause) { + if (!Boolean.getBoolean(ENABLED_PROPERTY)) { + return; + } String testName = context.getDisplayName(); String threadDump = getAllThreadsDump(); LOGGER.error("Test failed: " + testName + "\nThread dump:\n" + threadDump);