1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.spf4j.base;
33
34 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
35 import java.io.IOException;
36 import java.io.PrintStream;
37 import java.io.UncheckedIOException;
38 import java.lang.invoke.MethodHandle;
39 import java.lang.invoke.MethodHandles;
40 import java.security.AccessController;
41 import java.security.PrivilegedAction;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.concurrent.ThreadLocalRandom;
45 import java.util.logging.Level;
46 import java.util.logging.Logger;
47 import javax.annotation.Nullable;
48
49
50
51
52
53
54 public final class Threads {
55
56 public static final Thread[] EMPTY_ARRAY = new Thread[0];
57
58 private static final ThreadInfoSupplier TI_SUPP;
59
60 static {
61 ThreadInfoSupplier supp;
62 try {
63 supp = new OracleJdkThreadInfoSupplier();
64 } catch (ExceptionInInitializerError ex) {
65 Logger logger = Logger.getLogger(Threads.class.getName());
66 logger.warning("Optimized stack trace access not available,"
67 + " profiling overhead will be higher");
68 logger.log(Level.FINE, "Exception detail", ex);
69 supp = new SlowThreadInfoSupplierImpl();
70 }
71 TI_SUPP = supp;
72 }
73
74 interface ThreadInfoSupplier {
75
76 Thread[] getThreads();
77
78 StackTraceElement[][] getStackTraces(Thread... threads);
79 }
80
81 private static class OracleJdkThreadInfoSupplier implements ThreadInfoSupplier {
82
83 private static final MethodHandle GET_THREADS;
84 private static final MethodHandle DUMP_THREADS;
85
86 static {
87 final java.lang.reflect.Method getThreads;
88 final java.lang.reflect.Method dumpThreads;
89 try {
90
91 getThreads = Thread.class.getDeclaredMethod("getThreads");
92 dumpThreads = Thread.class.getDeclaredMethod("dumpThreads", Thread[].class);
93 } catch (NoSuchMethodException ex) {
94 throw new ExceptionInInitializerError(ex);
95 }
96 AccessController.doPrivileged((PrivilegedAction) () -> {
97 getThreads.setAccessible(true);
98 dumpThreads.setAccessible(true);
99 return null;
100 });
101 MethodHandles.Lookup lookup = MethodHandles.lookup();
102 try {
103 GET_THREADS = lookup.unreflect(getThreads);
104 DUMP_THREADS = lookup.unreflect(dumpThreads);
105 } catch (IllegalAccessException ex) {
106 throw new ExceptionInInitializerError(ex);
107 }
108
109 }
110
111 @Override
112 @SuppressFBWarnings("EXS_EXCEPTION_SOFTENING_NO_CHECKED")
113 public Thread[] getThreads() {
114 try {
115 return (Thread[]) GET_THREADS.invokeExact();
116 } catch (RuntimeException | Error ex) {
117 throw ex;
118 } catch (Throwable ex) {
119 throw new UncheckedExecutionException(ex);
120 }
121 }
122
123 @Override
124 @SuppressFBWarnings("EXS_EXCEPTION_SOFTENING_NO_CHECKED")
125 public StackTraceElement[][] getStackTraces(final Thread... threads) {
126 StackTraceElement[][] stackDump;
127 try {
128 stackDump = (StackTraceElement[][]) DUMP_THREADS.invokeExact(threads);
129 } catch (RuntimeException | Error ex) {
130 throw ex;
131 } catch (Throwable ex) {
132 throw new UncheckedExecutionException(ex);
133 }
134 return stackDump;
135 }
136
137 }
138
139 private Threads() {
140 }
141
142 public static Thread[] getThreads() {
143 return TI_SUPP.getThreads();
144 }
145
146
147
148
149
150
151
152 @SuppressFBWarnings("PREDICTABLE_RANDOM")
153 public static int randomFirst(final int nr, final Thread[] threads) {
154 int length = threads.length;
155 if (nr >= length) {
156 return length;
157 }
158 ThreadLocalRandom rnd = ThreadLocalRandom.current();
159 for (int i = 0; i < nr; i++) {
160 int nextInt = rnd.nextInt(i, length);
161 if (nextInt != i) {
162 Thread t = threads[i];
163 threads[i] = threads[nextInt];
164 threads[nextInt] = t;
165 }
166 }
167 Arrays.fill(threads, nr, length, null);
168 return nr;
169 }
170
171 @Nullable
172 public static Thread getThreadByName(final String name) {
173 for (Thread t : getThreads()) {
174 if (name.equals(t.getName())) {
175 return t;
176 }
177 }
178 return null;
179 }
180
181 @Nullable
182 public static Thread getThreadById(final long id) {
183 for (Thread t : getThreads()) {
184 if (id == t.getId()) {
185 return t;
186 }
187 }
188 return null;
189 }
190
191 public static StackTraceElement[][] getStackTraces(final Thread... threads) {
192 return TI_SUPP.getStackTraces(threads);
193 }
194
195 public static void dumpTo(final Appendable stream) throws IOException {
196 Thread[] threads = getThreads();
197 StackTraceElement[][] stackTraces = getStackTraces(threads);
198 for (int i = 0; i < threads.length; i++) {
199 StackTraceElement[] stackTrace = stackTraces[i];
200 if (stackTrace != null && stackTrace.length > 0) {
201 Thread thread = threads[i];
202 stream.append("Thread ").append(thread.getName()).append('\n');
203 Throwables.writeTo(stackTrace, stream, Throwables.PackageDetail.SHORT, true);
204 }
205 }
206 }
207
208 public static void dumpToPrintStream(final PrintStream stream) {
209 StringBuilder sb = new StringBuilder(1024);
210 try {
211 dumpTo(sb);
212 } catch (IOException ex) {
213 throw new UncheckedIOException(ex);
214 }
215 stream.append(sb);
216 }
217
218 private static class SlowThreadInfoSupplierImpl implements ThreadInfoSupplier {
219
220 @Override
221 public Thread[] getThreads() {
222 Set<Thread> keySet = Thread.getAllStackTraces().keySet();
223 return keySet.toArray(new Thread[keySet.size()]);
224 }
225
226 @Override
227 public StackTraceElement[][] getStackTraces(final Thread... threads) {
228 Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
229 StackTraceElement[][] result = new StackTraceElement[threads.length][];
230 for (int i = 0; i < threads.length; i++) {
231 result[i] = allStackTraces.get(threads[i]);
232 }
233 return result;
234 }
235 }
236
237 }