markap14 commented on code in PR #11164:
URL: https://github.com/apache/nifi/pull/11164#discussion_r3229385417


##########
nifi-framework-api/src/main/java/org/apache/nifi/diagnostics/ThreadDumpTask.java:
##########
@@ -16,116 +16,51 @@
  */
 package org.apache.nifi.diagnostics;
 
-import java.lang.management.LockInfo;
+import com.sun.management.HotSpotDiagnosticMXBean;
+
+import java.io.IOException;
 import java.lang.management.ManagementFactory;
-import java.lang.management.MonitorInfo;
-import java.lang.management.ThreadInfo;
-import java.lang.management.ThreadMXBean;
-import java.util.ArrayList;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
 
+/**
+ * Captures a textual dump of every thread in the JVM. Uses
+ * {@link HotSpotDiagnosticMXBean#dumpThreads(String, 
HotSpotDiagnosticMXBean.ThreadDumpFormat)}
+ * so that virtual threads are included in the dump alongside platform threads.
+ */
 public class ThreadDumpTask implements DiagnosticTask {
-    @Override
-    public DiagnosticsDumpElement captureDump(boolean verbose) {
-        final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
-
-        final ThreadInfo[] infos = mbean.dumpAllThreads(true, true);
-        final long[] deadlockedThreadIds = mbean.findDeadlockedThreads();
-        final long[] monitorDeadlockThreadIds = 
mbean.findMonitorDeadlockedThreads();
-
-        final List<ThreadInfo> sortedInfos = new ArrayList<>(infos.length);
-        Collections.addAll(sortedInfos, infos);
-        sortedInfos.sort(new Comparator<>() {
-            @Override
-            public int compare(ThreadInfo o1, ThreadInfo o2) {
-                return 
o1.getThreadName().toLowerCase().compareTo(o2.getThreadName().toLowerCase());
-            }
-        });
-
-        final StringBuilder sb = new StringBuilder();
-        for (final ThreadInfo info : sortedInfos) {
-            sb.append("\n");
-            sb.append("\"").append(info.getThreadName()).append("\" Id=");
-            sb.append(info.getThreadId()).append(" ");
-            sb.append(info.getThreadState().toString()).append(" ");
-
-            switch (info.getThreadState()) {
-                case BLOCKED:
-                case TIMED_WAITING:
-                case WAITING:
-                    sb.append(" on ");
-                    sb.append(info.getLockInfo());
-                    break;
-                default:
-                    break;
-            }
-
-            if (info.isSuspended()) {
-                sb.append(" (suspended)");
-            }
-            if (info.isInNative()) {
-                sb.append(" (in native code)");
-            }
-
-            if (deadlockedThreadIds != null) {
-                for (final long id : deadlockedThreadIds) {
-                    if (id == info.getThreadId()) {
-                        sb.append(" ** DEADLOCKED THREAD **");
-                    }
-                }
-            }
 
-            if (monitorDeadlockThreadIds != null) {
-                for (final long id : monitorDeadlockThreadIds) {
-                    if (id == info.getThreadId()) {
-                        sb.append(" ** MONITOR-DEADLOCKED THREAD **");
-                    }
-                }
-            }
-
-            final StackTraceElement[] stackTraces = info.getStackTrace();
-            for (final StackTraceElement element : stackTraces) {
-                sb.append("\n\tat ").append(element);
-
-                final MonitorInfo[] monitors = info.getLockedMonitors();
-                for (final MonitorInfo monitor : monitors) {
-                    if (Objects.equals(monitor.getLockedStackFrame(), 
element)) {
-                        sb.append("\n\t- waiting on ").append(monitor);
-                    }
-                }
+    @Override
+    public DiagnosticsDumpElement captureDump(final boolean verbose) {
+        String threadDump;
+
+        Path tempDirectory = null;
+        try {
+            final HotSpotDiagnosticMXBean diagnosticMXBean = 
ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
+            // dumpThreads requires that the destination file does not already 
exist. Creating a private
+            // temporary directory and writing to a fresh filename inside it 
avoids a time-of-check to
+            // time-of-use race that would exist if we created a temp file and 
then deleted it before the
+            // dumpThreads call.
+            tempDirectory = Files.createTempDirectory("nifi-thread-dump-");
+            final Path tempFile = tempDirectory.resolve("thread-dump.txt");
+            try {
+                diagnosticMXBean.dumpThreads(tempFile.toString(), 
HotSpotDiagnosticMXBean.ThreadDumpFormat.TEXT_PLAIN);
+                threadDump = Files.readString(tempFile);
+            } finally {
+                Files.deleteIfExists(tempFile);
             }
-
-            final LockInfo[] lockInfos = info.getLockedSynchronizers();
-            if (lockInfos.length > 0) {
-                sb.append("\n\t");
-                sb.append("Number of Locked Synchronizers: 
").append(lockInfos.length);
-                for (final LockInfo lockInfo : lockInfos) {
-                    sb.append("\n\t- ").append(lockInfo.toString());
+        } catch (final IOException e) {
+            threadDump = "Failed to capture thread dump: " + e.getMessage();
+        } finally {
+            if (tempDirectory != null) {
+                try {
+                    Files.deleteIfExists(tempDirectory);
+                } catch (final IOException ignored) {

Review Comment:
   Sure, that's reasonable.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to