1 /*
2 * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 /*
25 * @test
26 * @bug 5047639 8132785
27 * @summary Check that the "java-level" APIs provide a consistent view of
28 * the thread list
29 * @comment Must run in othervm mode to avoid interference from other tests.
30 * @run main/othervm ThreadLists
31 */
32 import java.lang.management.ManagementFactory;
33 import java.lang.management.ThreadInfo;
34 import java.lang.management.ThreadMXBean;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.TreeSet;
38
39 public class ThreadLists {
40
41 // Thread names permitted to appear during test:
42 public static final String [] permittedThreadNames = { "ForkJoinPool", "JVMCI" };
43
44 public static boolean isPermittedNewThread(String name) {
45 for (String s : permittedThreadNames) {
46 if (name.contains(s)) {
47 return true;
48 }
49 }
50 return false;
51 }
52
53 public static void main(String args[]) {
54
55 // Bug id : JDK-8151797
56 // Use a lambda expression so that call-site cleaner thread is started
57 Runnable printLambda = () -> {System.out.println("Starting Test");};
58 printLambda.run();
59
60 // start a virutal thread to cause supporting threads to start
61 Thread.startVirtualThread(() -> { });
62
63 // get top-level thread group
64 ThreadGroup top = Thread.currentThread().getThreadGroup();
65 ThreadGroup parent;
66 do {
67 parent = top.getParent();
68 if (parent != null) top = parent;
69 } while (parent != null);
70
71 // get the thread count
72 int tgActiveCount = top.activeCount();
73
74 // Now enumerate to see if we find any extras yet.
75 // Ensure array is big enough for a few extras.
76 Thread[] tgThreads = new Thread[tgActiveCount * 2];
77 int tgNewCount = top.enumerate(tgThreads);
78 Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
79
80 if (tgNewCount != tgActiveCount) {
81 System.out.println("Found different Thread Group thread count after enumeration: tgActiveCount="
82 + tgActiveCount + " enumerated=" + tgNewCount);
83 }
84 if (tgNewCount != stackTraces.size()) {
85 System.out.println("Found difference in counts: thread group new count="
86 + tgNewCount + " stackTraces.size()=" + stackTraces.size());
87 }
88 System.out.println("Initial set of enumerated threads:");
89 for (int i = 0; i < tgNewCount; i++) {
90 System.out.println(" - Thread: " + tgThreads[i].getName());
91 }
92
93 // Get Threads from MXBean. Retry to ensure count and id count match.
94 ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
95 int threadCountBean = 0;
96 long[] threadIdsBean = null;
97 do {
98 System.out.println("Gathering Thread info from MXBean...");
99 threadCountBean = threadBean.getThreadCount();
100 threadIdsBean = threadBean.getAllThreadIds();
101 } while (threadCountBean != threadIdsBean.length);
102
103 System.out.println("ThreadGroup: " + tgActiveCount + " active thread(s)");
104 System.out.println("Thread.getAllStackTraces: " + stackTraces.size() + " stack trace(s) returned");
105 System.out.println("ThreadMXBean: " + threadCountBean + " live threads(s)");
106 System.out.println("ThreadMXBean: " + threadIdsBean.length + " thread Id(s)");
107
108 if (threadIdsBean.length > tgActiveCount) {
109 // Find the new Threads: some Thead names are permitted to appear: ignore them.
110 Set<Long> seenTids = new TreeSet<>();
111 for (Thread t : stackTraces.keySet()) {
112 if (t != null) {
113 seenTids.add(t.getId());
114 }
115 }
116 for (long tid : threadIdsBean) {
117 if (!seenTids.contains(tid)) {
118 // New Thread from MBean, compared to Thread Group:
119 ThreadInfo threadInfo = threadBean.getThreadInfo(tid);
120 if (threadInfo != null && isPermittedNewThread(threadInfo.getThreadName())) {
121 System.out.print("New thread permitted: " + threadInfo);
122 threadCountBean--;
123 }
124 }
125 }
126 }
127
128 // check results are consistent
129 boolean failed = false;
130 if (tgActiveCount != stackTraces.size()) failed = true;
131 if (tgActiveCount != threadCountBean) failed = true;
132 // We know threadCountBean == threadIdsBean.length
133
134 if (failed) {
135 System.out.println("Failed.");
136 System.out.println("Set of Threads from getAllStackTraces:");
137 for (Thread t : stackTraces.keySet()) {
138 System.out.println(" - Thread: " +
139 (t != null ? t.getName() : "null!"));
140 }
141 System.out.println("Set of Thread IDs from MXBean:");
142 for (long tid : threadIdsBean) {
143 System.out.print(tid + " ");
144 ThreadInfo threadInfo = threadBean.getThreadInfo(tid);
145 System.out.println(threadInfo != null ? threadInfo.getThreadName() : "");
146 }
147 throw new RuntimeException("inconsistent results");
148 }
149 }
150 }