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 // get top-level thread group
61 ThreadGroup top = Thread.currentThread().getThreadGroup();
62 ThreadGroup parent;
63 do {
64 parent = top.getParent();
65 if (parent != null) top = parent;
66 } while (parent != null);
67
68 // get the thread count
69 int tgActiveCount = top.activeCount();
70
71 // Now enumerate to see if we find any extras yet.
72 // Ensure array is big enough for a few extras.
73 Thread[] tgThreads = new Thread[tgActiveCount * 2];
74 int tgNewCount = top.enumerate(tgThreads);
75 Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
76
77 if (tgNewCount != tgActiveCount) {
78 System.out.println("Found different Thread Group thread count after enumeration: tgActiveCount="
79 + tgActiveCount + " enumerated=" + tgNewCount);
80 }
81 if (tgNewCount != stackTraces.size()) {
82 System.out.println("Found difference in counts: thread group new count="
83 + tgNewCount + " stackTraces.size()=" + stackTraces.size());
84 }
85 System.out.println("Initial set of enumerated threads:");
86 for (int i = 0; i < tgNewCount; i++) {
87 System.out.println(" - Thread: " + tgThreads[i].getName());
88 }
89
90 // Get Threads from MXBean. Retry to ensure count and id count match.
91 ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
92 int threadCountBean = 0;
93 long[] threadIdsBean = null;
94 do {
95 System.out.println("Gathering Thread info from MXBean...");
96 threadCountBean = threadBean.getThreadCount();
97 threadIdsBean = threadBean.getAllThreadIds();
98 } while (threadCountBean != threadIdsBean.length);
99
100 System.out.println("ThreadGroup: " + tgActiveCount + " active thread(s)");
101 System.out.println("Thread.getAllStackTraces: " + stackTraces.size() + " stack trace(s) returned");
102 System.out.println("ThreadMXBean: " + threadCountBean + " live threads(s)");
103 System.out.println("ThreadMXBean: " + threadIdsBean.length + " thread Id(s)");
104
105 if (threadIdsBean.length > tgActiveCount) {
106 // Find the new Threads: some Thead names are permitted to appear: ignore them.
107 Set<Long> seenTids = new TreeSet<>();
108 for (Thread t : stackTraces.keySet()) {
109 if (t != null) {
110 seenTids.add(t.getId());
111 }
112 }
113 for (long tid : threadIdsBean) {
114 if (!seenTids.contains(tid)) {
115 // New Thread from MBean, compared to Thread Group:
116 ThreadInfo threadInfo = threadBean.getThreadInfo(tid);
117 if (threadInfo != null && isPermittedNewThread(threadInfo.getThreadName())) {
118 System.out.print("New thread permitted: " + threadInfo);
119 threadCountBean--;
120 }
121 }
122 }
123 }
124
125 // check results are consistent
126 boolean failed = false;
127 if (tgActiveCount != stackTraces.size()) failed = true;
128 if (tgActiveCount != threadCountBean) failed = true;
129 // We know threadCountBean == threadIdsBean.length
130
131 if (failed) {
132 System.out.println("Failed.");
133 System.out.println("Set of Threads from getAllStackTraces:");
134 for (Thread t : stackTraces.keySet()) {
135 System.out.println(" - Thread: " +
136 (t != null ? t.getName() : "null!"));
137 }
138 System.out.println("Set of Thread IDs from MXBean:");
139 for (long tid : threadIdsBean) {
140 System.out.print(tid + " ");
141 ThreadInfo threadInfo = threadBean.getThreadInfo(tid);
142 System.out.println(threadInfo != null ? threadInfo.getThreadName() : "");
143 }
144 throw new RuntimeException("inconsistent results");
145 }
146 }
147 }