View Javadoc
1   /*
2    * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this program; if not, write to the Free Software
16   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17   *
18   * Additionally licensed with:
19   *
20   * Licensed under the Apache License, Version 2.0 (the "License");
21   * you may not use this file except in compliance with the License.
22   * You may obtain a copy of the License at
23   *
24   *      http://www.apache.org/licenses/LICENSE-2.0
25   *
26   * Unless required by applicable law or agreed to in writing, software
27   * distributed under the License is distributed on an "AS IS" BASIS,
28   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29   * See the License for the specific language governing permissions and
30   * limitations under the License.
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   * Thread utilities.
51   *
52   * @author Zoltan Farkas
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; // nothing to return
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    * get a random selection of nr Threads from the array, the first nr location in the array will
148    * contain the random set,m the rest will be null.
149    * @param nr number of threads to randomly select.
150    * @param threads the array of threads to select from
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 }