1 /*
  2  * Copyright (c) 2003, 2017, 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.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package sun.management;
 27 
 28 import java.lang.management.ManagementFactory;
 29 import java.lang.management.ThreadInfo;
 30 import java.lang.management.ThreadMXBean;
 31 import javax.management.ObjectName;
 32 import java.util.Objects;
 33 
 34 /**
 35  * Implementation for java.lang.management.ThreadMXBean as well as providing the
 36  * supporting method for com.sun.management.ThreadMXBean.
 37  * The supporting method for com.sun.management.ThreadMXBean can be moved to
 38  * jdk.management in the future.
 39  */
 40 
 41 public class ThreadImpl implements ThreadMXBean {
 42     private final VMManagement jvm;
 43 
 44     // default for thread contention monitoring is disabled.
 45     private boolean contentionMonitoringEnabled = false;
 46     private boolean cpuTimeEnabled;
 47     private boolean allocatedMemoryEnabled;
 48 
 49     /**
 50      * Constructor of ThreadImpl class.
 51      */
 52     protected ThreadImpl(VMManagement vm) {
 53         this.jvm = vm;
 54         this.cpuTimeEnabled = jvm.isThreadCpuTimeEnabled();
 55         this.allocatedMemoryEnabled = jvm.isThreadAllocatedMemoryEnabled();
 56     }
 57 
 58     @Override
 59     public int getThreadCount() {
 60         return jvm.getLiveThreadCount();
 61     }
 62 
 63     @Override
 64     public int getPeakThreadCount() {
 65         return jvm.getPeakThreadCount();
 66     }
 67 
 68     @Override
 69     public long getTotalStartedThreadCount() {
 70         return jvm.getTotalThreadCount();
 71     }
 72 
 73     @Override
 74     public int getDaemonThreadCount() {
 75         return jvm.getDaemonThreadCount();
 76     }
 77 
 78     @Override
 79     public boolean isThreadContentionMonitoringSupported() {
 80         return jvm.isThreadContentionMonitoringSupported();
 81     }
 82 
 83     @Override
 84     public synchronized boolean isThreadContentionMonitoringEnabled() {
 85        if (!isThreadContentionMonitoringSupported()) {
 86             throw new UnsupportedOperationException(
 87                 "Thread contention monitoring is not supported.");
 88         }
 89         return contentionMonitoringEnabled;
 90     }
 91 
 92     @Override
 93     public boolean isThreadCpuTimeSupported() {
 94         return jvm.isOtherThreadCpuTimeSupported();
 95     }
 96 
 97     @Override
 98     public boolean isCurrentThreadCpuTimeSupported() {
 99         return jvm.isCurrentThreadCpuTimeSupported();
100     }
101 
102     protected boolean isThreadAllocatedMemorySupported() {
103         return jvm.isThreadAllocatedMemorySupported();
104     }
105 
106     @Override
107     public boolean isThreadCpuTimeEnabled() {
108         if (!isThreadCpuTimeSupported() &&
109             !isCurrentThreadCpuTimeSupported()) {
110             throw new UnsupportedOperationException(
111                 "Thread CPU time measurement is not supported");
112         }
113         return cpuTimeEnabled;
114     }
115 
116     private void ensureThreadAllocatedMemorySupported() {
117         if (!isThreadAllocatedMemorySupported()) {
118             throw new UnsupportedOperationException(
119                 "Thread allocated memory measurement is not supported.");
120         }
121     }
122 
123     protected boolean isThreadAllocatedMemoryEnabled() {
124         ensureThreadAllocatedMemorySupported();
125         return allocatedMemoryEnabled;
126     }
127 
128     @Override
129     public long[] getAllThreadIds() {
130         Util.checkMonitorAccess();
131 
132         Thread[] threads = getThreads();
133         int length = threads.length;
134         long[] ids = new long[length];
135         for (int i = 0; i < length; i++) {
136             Thread t = threads[i];
137             ids[i] = t.getId();
138         }
139         return ids;
140     }
141 
142     @Override
143     public ThreadInfo getThreadInfo(long id) {
144         long[] ids = new long[1];
145         ids[0] = id;
146         final ThreadInfo[] infos = getThreadInfo(ids, 0);
147         return infos[0];
148     }
149 
150     @Override
151     public ThreadInfo getThreadInfo(long id, int maxDepth) {
152         long[] ids = new long[1];
153         ids[0] = id;
154         final ThreadInfo[] infos = getThreadInfo(ids, maxDepth);
155         return infos[0];
156     }
157 
158     @Override
159     public ThreadInfo[] getThreadInfo(long[] ids) {
160         return getThreadInfo(ids, 0);
161     }
162 
163     private void verifyThreadId(long id) {
164         if (id <= 0) {
165             throw new IllegalArgumentException(
166                 "Invalid thread ID parameter: " + id);
167         }
168     }
169 
170     private void verifyThreadIds(long[] ids) {
171         Objects.requireNonNull(ids);
172 
173         for (int i = 0; i < ids.length; i++) {
174             verifyThreadId(ids[i]);
175         }
176     }
177 
178     @Override
179     public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth) {
180         verifyThreadIds(ids);
181 
182         if (maxDepth < 0) {
183             throw new IllegalArgumentException(
184                 "Invalid maxDepth parameter: " + maxDepth);
185         }
186 
187         // ids has been verified to be non-null
188         // an empty array of ids should return an empty array of ThreadInfos
189         if (ids.length == 0) return new ThreadInfo[0];
190 
191         Util.checkMonitorAccess();
192 
193         ThreadInfo[] infos = new ThreadInfo[ids.length]; // nulls
194         if (maxDepth == Integer.MAX_VALUE) {
195             getThreadInfo1(ids, -1, infos);
196         } else {
197             getThreadInfo1(ids, maxDepth, infos);
198         }
199         return infos;
200     }
201 
202     @Override
203     public void setThreadContentionMonitoringEnabled(boolean enable) {
204         if (!isThreadContentionMonitoringSupported()) {
205             throw new UnsupportedOperationException(
206                 "Thread contention monitoring is not supported");
207         }
208 
209         Util.checkControlAccess();
210 
211         synchronized (this) {
212             if (contentionMonitoringEnabled != enable) {
213                 if (enable) {
214                     // if reeabled, reset contention time statistics
215                     // for all threads
216                     resetContentionTimes0(0);
217                 }
218 
219                 // update the VM of the state change
220                 setThreadContentionMonitoringEnabled0(enable);
221 
222                 contentionMonitoringEnabled = enable;
223             }
224         }
225     }
226 
227     private boolean verifyCurrentThreadCpuTime() {
228         // check if Thread CPU time measurement is supported.
229         if (!isCurrentThreadCpuTimeSupported()) {
230             throw new UnsupportedOperationException(
231                 "Current thread CPU time measurement is not supported.");
232         }
233         return isThreadCpuTimeEnabled();
234     }
235 
236     @Override
237     public long getCurrentThreadCpuTime() {
238         if (verifyCurrentThreadCpuTime()) {
239             return getThreadTotalCpuTime0(0);
240         }
241         return -1;
242     }
243 
244     @Override
245     public long getThreadCpuTime(long id) {
246         long[] ids = new long[1];
247         ids[0] = id;
248         final long[] times = getThreadCpuTime(ids);
249         return times[0];
250     }
251 
252     private boolean verifyThreadCpuTime(long[] ids) {
253         verifyThreadIds(ids);
254 
255         // check if Thread CPU time measurement is supported.
256         if (!isThreadCpuTimeSupported() &&
257             !isCurrentThreadCpuTimeSupported()) {
258             throw new UnsupportedOperationException(
259                 "Thread CPU time measurement is not supported.");
260         }
261 
262         if (!isThreadCpuTimeSupported()) {
263             // support current thread only
264             for (int i = 0; i < ids.length; i++) {
265                 if (ids[i] != Thread.currentThread().getId()) {
266                     throw new UnsupportedOperationException(
267                         "Thread CPU time measurement is only supported" +
268                         " for the current thread.");
269                 }
270             }
271         }
272 
273         return isThreadCpuTimeEnabled();
274     }
275 
276     protected long[] getThreadCpuTime(long[] ids) {
277         boolean verified = verifyThreadCpuTime(ids);
278 
279         int length = ids.length;
280         long[] times = new long[length];
281         java.util.Arrays.fill(times, -1);
282 
283         if (verified) {
284             if (length == 1) {
285                 long id = ids[0];
286                 if (id == Thread.currentThread().getId()) {
287                     id = 0;
288                 }
289                 times[0] = getThreadTotalCpuTime0(id);
290             } else {
291                 getThreadTotalCpuTime1(ids, times);
292             }
293         }
294         return times;
295     }
296 
297     @Override
298     public long getCurrentThreadUserTime() {
299         if (verifyCurrentThreadCpuTime()) {
300             return getThreadUserCpuTime0(0);
301         }
302         return -1;
303     }
304 
305     @Override
306     public long getThreadUserTime(long id) {
307         long[] ids = new long[1];
308         ids[0] = id;
309         final long[] times = getThreadUserTime(ids);
310         return times[0];
311     }
312 
313     protected long[] getThreadUserTime(long[] ids) {
314         boolean verified = verifyThreadCpuTime(ids);
315 
316         int length = ids.length;
317         long[] times = new long[length];
318         java.util.Arrays.fill(times, -1);
319 
320         if (verified) {
321             if (length == 1) {
322                 long id = ids[0];
323                 if (id == Thread.currentThread().getId()) {
324                     id = 0;
325                 }
326                 times[0] = getThreadUserCpuTime0(id);
327             } else {
328                 getThreadUserCpuTime1(ids, times);
329             }
330         }
331         return times;
332     }
333 
334     @Override
335     public void setThreadCpuTimeEnabled(boolean enable) {
336         if (!isThreadCpuTimeSupported() &&
337             !isCurrentThreadCpuTimeSupported()) {
338             throw new UnsupportedOperationException(
339                 "Thread CPU time measurement is not supported");
340         }
341 
342         Util.checkControlAccess();
343         synchronized (this) {
344             if (cpuTimeEnabled != enable) {
345                 // notify VM of the state change
346                 setThreadCpuTimeEnabled0(enable);
347                 cpuTimeEnabled = enable;
348             }
349         }
350     }
351 
352     protected long getCurrentThreadAllocatedBytes() {
353         if (isThreadAllocatedMemoryEnabled()) {
354             return getThreadAllocatedMemory0(0);
355         }
356         return -1;
357     }
358 
359     private boolean verifyThreadAllocatedMemory(long id) {
360         verifyThreadId(id);
361         return isThreadAllocatedMemoryEnabled();
362     }
363 
364     protected long getThreadAllocatedBytes(long id) {
365         boolean verified = verifyThreadAllocatedMemory(id);
366 
367         if (verified) {
368             return getThreadAllocatedMemory0(
369                 Thread.currentThread().getId() == id ? 0 : id);
370         }
371         return -1;
372     }
373 
374     private boolean verifyThreadAllocatedMemory(long[] ids) {
375         verifyThreadIds(ids);
376         return isThreadAllocatedMemoryEnabled();
377     }
378 
379     protected long[] getThreadAllocatedBytes(long[] ids) {
380         Objects.requireNonNull(ids);
381 
382         if (ids.length == 1) {
383             long size = getThreadAllocatedBytes(ids[0]);
384             return new long[] { size };
385         }
386 
387         boolean verified = verifyThreadAllocatedMemory(ids);
388 
389         long[] sizes = new long[ids.length];
390         java.util.Arrays.fill(sizes, -1);
391 
392         if (verified) {
393             getThreadAllocatedMemory1(ids, sizes);
394         }
395         return sizes;
396     }
397 
398     protected void setThreadAllocatedMemoryEnabled(boolean enable) {
399         ensureThreadAllocatedMemorySupported();
400 
401         Util.checkControlAccess();
402         synchronized (this) {
403             if (allocatedMemoryEnabled != enable) {
404                 // notify VM of the state change
405                 setThreadAllocatedMemoryEnabled0(enable);
406                 allocatedMemoryEnabled = enable;
407             }
408         }
409     }
410 
411     @Override
412     public long[] findMonitorDeadlockedThreads() {
413         Util.checkMonitorAccess();
414 
415         Thread[] threads = findMonitorDeadlockedThreads0();
416         if (threads == null) {
417             return null;
418         }
419 
420         long[] ids = new long[threads.length];
421         for (int i = 0; i < threads.length; i++) {
422             Thread t = threads[i];
423             ids[i] = t.getId();
424         }
425         return ids;
426     }
427 
428     @Override
429     public long[] findDeadlockedThreads() {
430         if (!isSynchronizerUsageSupported()) {
431             throw new UnsupportedOperationException(
432                 "Monitoring of Synchronizer Usage is not supported.");
433         }
434 
435         Util.checkMonitorAccess();
436 
437         Thread[] threads = findDeadlockedThreads0();
438         if (threads == null) {
439             return null;
440         }
441 
442         long[] ids = new long[threads.length];
443         for (int i = 0; i < threads.length; i++) {
444             Thread t = threads[i];
445             ids[i] = t.getId();
446         }
447         return ids;
448     }
449 
450     @Override
451     public void resetPeakThreadCount() {
452         Util.checkControlAccess();
453         resetPeakThreadCount0();
454     }
455 
456     @Override
457     public boolean isObjectMonitorUsageSupported() {
458         return jvm.isObjectMonitorUsageSupported();
459     }
460 
461     @Override
462     public boolean isSynchronizerUsageSupported() {
463         return jvm.isSynchronizerUsageSupported();
464     }
465 
466     private void verifyDumpThreads(boolean lockedMonitors,
467                                    boolean lockedSynchronizers) {
468         if (lockedMonitors && !isObjectMonitorUsageSupported()) {
469             throw new UnsupportedOperationException(
470                 "Monitoring of Object Monitor Usage is not supported.");
471         }
472 
473         if (lockedSynchronizers && !isSynchronizerUsageSupported()) {
474             throw new UnsupportedOperationException(
475                 "Monitoring of Synchronizer Usage is not supported.");
476         }
477 
478         Util.checkMonitorAccess();
479     }
480 
481     @Override
482     public ThreadInfo[] getThreadInfo(long[] ids,
483                                       boolean lockedMonitors,
484                                       boolean lockedSynchronizers) {
485         return dumpThreads0(ids, lockedMonitors, lockedSynchronizers,
486                             Integer.MAX_VALUE);
487     }
488 
489     public ThreadInfo[] getThreadInfo(long[] ids,
490                                       boolean lockedMonitors,
491                                       boolean lockedSynchronizers,
492                                       int maxDepth) {
493         if (maxDepth < 0) {
494             throw new IllegalArgumentException(
495                     "Invalid maxDepth parameter: " + maxDepth);
496         }
497         verifyThreadIds(ids);
498         // ids has been verified to be non-null
499         // an empty array of ids should return an empty array of ThreadInfos
500         if (ids.length == 0) return new ThreadInfo[0];
501 
502         verifyDumpThreads(lockedMonitors, lockedSynchronizers);
503         return dumpThreads0(ids, lockedMonitors, lockedSynchronizers, maxDepth);
504     }
505 
506     @Override
507     public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
508                                        boolean lockedSynchronizers) {
509         return dumpAllThreads(lockedMonitors, lockedSynchronizers,
510                               Integer.MAX_VALUE);
511     }
512 
513     public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
514                                        boolean lockedSynchronizers,
515                                        int maxDepth) {
516         if (maxDepth < 0) {
517             throw new IllegalArgumentException(
518                     "Invalid maxDepth parameter: " + maxDepth);
519         }
520         verifyDumpThreads(lockedMonitors, lockedSynchronizers);
521         return dumpThreads0(null, lockedMonitors, lockedSynchronizers, maxDepth);
522     }
523 
524     // VM support where maxDepth == -1 to request entire stack dump
525     private static native Thread[] getThreads();
526     private static native void getThreadInfo1(long[] ids,
527                                               int maxDepth,
528                                               ThreadInfo[] result);
529     private static native long getThreadTotalCpuTime0(long id);
530     private static native void getThreadTotalCpuTime1(long[] ids, long[] result);
531     private static native long getThreadUserCpuTime0(long id);
532     private static native void getThreadUserCpuTime1(long[] ids, long[] result);
533     private static native long getThreadAllocatedMemory0(long id);
534     private static native void getThreadAllocatedMemory1(long[] ids, long[] result);
535     private static native void setThreadCpuTimeEnabled0(boolean enable);
536     private static native void setThreadAllocatedMemoryEnabled0(boolean enable);
537     private static native void setThreadContentionMonitoringEnabled0(boolean enable);
538     private static native Thread[] findMonitorDeadlockedThreads0();
539     private static native Thread[] findDeadlockedThreads0();
540     private static native void resetPeakThreadCount0();
541     private static native ThreadInfo[] dumpThreads0(long[] ids,
542                                                     boolean lockedMonitors,
543                                                     boolean lockedSynchronizers,
544                                                     int maxDepth);
545 
546     // tid == 0 to reset contention times for all threads
547     private static native void resetContentionTimes0(long tid);
548 
549     @Override
550     public ObjectName getObjectName() {
551         return Util.newObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
552     }
553 
554 }