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 java.lang.reflect.InvocationTargetException;
 32 import java.lang.reflect.Method;
 33 import java.security.AccessController;
 34 import java.security.PrivilegedActionException;
 35 import java.security.PrivilegedExceptionAction;
 36 import javax.management.ObjectName;
 37 import java.util.Objects;
 38 
 39 /**
 40  * Implementation for java.lang.management.ThreadMXBean as well as providing the
 41  * supporting method for com.sun.management.ThreadMXBean.
 42  * The supporting method for com.sun.management.ThreadMXBean can be moved to
 43  * jdk.management in the future.
 44  */
 45 
 46 public class ThreadImpl implements ThreadMXBean {
 47     private final VMManagement jvm;
 48 
 49     // default for thread contention monitoring is disabled.
 50     private boolean contentionMonitoringEnabled = false;
 51     private boolean cpuTimeEnabled;
 52     private boolean allocatedMemoryEnabled;
 53 
 54     /**
 55      * Constructor of ThreadImpl class.
 56      */
 57     protected ThreadImpl(VMManagement vm) {
 58         this.jvm = vm;
 59         this.cpuTimeEnabled = jvm.isThreadCpuTimeEnabled();
 60         this.allocatedMemoryEnabled = jvm.isThreadAllocatedMemoryEnabled();
 61     }
 62 
 63     @Override
 64     public int getThreadCount() {
 65         return jvm.getLiveThreadCount();
 66     }
 67 
 68     @Override
 69     public int getPeakThreadCount() {
 70         return jvm.getPeakThreadCount();
 71     }
 72 
 73     @Override
 74     public long getTotalStartedThreadCount() {
 75         return jvm.getTotalThreadCount();
 76     }
 77 
 78     @Override
 79     public int getDaemonThreadCount() {
 80         return jvm.getDaemonThreadCount();
 81     }
 82 
 83     @Override
 84     public boolean isThreadContentionMonitoringSupported() {
 85         return jvm.isThreadContentionMonitoringSupported();
 86     }
 87 
 88     @Override
 89     public synchronized boolean isThreadContentionMonitoringEnabled() {
 90        if (!isThreadContentionMonitoringSupported()) {
 91             throw new UnsupportedOperationException(
 92                 "Thread contention monitoring is not supported.");
 93         }
 94         return contentionMonitoringEnabled;
 95     }
 96 
 97     @Override
 98     public boolean isThreadCpuTimeSupported() {
 99         return jvm.isOtherThreadCpuTimeSupported();
100     }
101 
102     @Override
103     public boolean isCurrentThreadCpuTimeSupported() {
104         return jvm.isCurrentThreadCpuTimeSupported();
105     }
106 
107     protected boolean isThreadAllocatedMemorySupported() {
108         return jvm.isThreadAllocatedMemorySupported();
109     }
110 
111     @Override
112     public boolean isThreadCpuTimeEnabled() {
113         if (!isThreadCpuTimeSupported() &&
114             !isCurrentThreadCpuTimeSupported()) {
115             throw new UnsupportedOperationException(
116                 "Thread CPU time measurement is not supported");
117         }
118         return cpuTimeEnabled;
119     }
120 
121     private void ensureThreadAllocatedMemorySupported() {
122         if (!isThreadAllocatedMemorySupported()) {
123             throw new UnsupportedOperationException(
124                 "Thread allocated memory measurement is not supported.");
125         }
126     }
127 
128     protected boolean isThreadAllocatedMemoryEnabled() {
129         ensureThreadAllocatedMemorySupported();
130         return allocatedMemoryEnabled;
131     }
132 
133     @Override
134     public long[] getAllThreadIds() {
135         Util.checkMonitorAccess();
136 
137         Thread[] threads = getThreads();
138         int length = threads.length;
139         long[] ids = new long[length];
140         for (int i = 0; i < length; i++) {
141             Thread t = threads[i];
142             ids[i] = t.getId();
143         }
144         return ids;
145     }
146 
147     @Override
148     public ThreadInfo getThreadInfo(long id) {
149         long[] ids = new long[1];
150         ids[0] = id;
151         final ThreadInfo[] infos = getThreadInfo(ids, 0);
152         return infos[0];
153     }
154 
155     @Override
156     public ThreadInfo getThreadInfo(long id, int maxDepth) {
157         long[] ids = new long[1];
158         ids[0] = id;
159         final ThreadInfo[] infos = getThreadInfo(ids, maxDepth);
160         return infos[0];
161     }
162 
163     @Override
164     public ThreadInfo[] getThreadInfo(long[] ids) {
165         return getThreadInfo(ids, 0);
166     }
167 
168     private void verifyThreadId(long id) {
169         if (id <= 0) {
170             throw new IllegalArgumentException(
171                 "Invalid thread ID parameter: " + id);
172         }
173     }
174 
175     private void verifyThreadIds(long[] ids) {
176         Objects.requireNonNull(ids);
177 
178         for (int i = 0; i < ids.length; i++) {
179             verifyThreadId(ids[i]);
180         }
181     }
182 
183     @Override
184     public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth) {
185         verifyThreadIds(ids);
186 
187         if (maxDepth < 0) {
188             throw new IllegalArgumentException(
189                 "Invalid maxDepth parameter: " + maxDepth);
190         }
191 
192         // ids has been verified to be non-null
193         // an empty array of ids should return an empty array of ThreadInfos
194         if (ids.length == 0) return new ThreadInfo[0];
195 
196         Util.checkMonitorAccess();
197 
198         ThreadInfo[] infos = new ThreadInfo[ids.length]; // nulls
199         if (maxDepth == Integer.MAX_VALUE) {
200             getThreadInfo1(ids, -1, infos);
201         } else {
202             getThreadInfo1(ids, maxDepth, infos);
203         }
204         return infos;
205     }
206 
207     @Override
208     public void setThreadContentionMonitoringEnabled(boolean enable) {
209         if (!isThreadContentionMonitoringSupported()) {
210             throw new UnsupportedOperationException(
211                 "Thread contention monitoring is not supported");
212         }
213 
214         Util.checkControlAccess();
215 
216         synchronized (this) {
217             if (contentionMonitoringEnabled != enable) {
218                 if (enable) {
219                     // if reeabled, reset contention time statistics
220                     // for all threads
221                     resetContentionTimes0(0);
222                 }
223 
224                 // update the VM of the state change
225                 setThreadContentionMonitoringEnabled0(enable);
226 
227                 contentionMonitoringEnabled = enable;
228             }
229         }
230     }
231 
232     private boolean verifyCurrentThreadCpuTime() {
233         // check if Thread CPU time measurement is supported.
234         if (isVirtual(Thread.currentThread())) {
235             throw new UnsupportedOperationException("Not supported by virtual threads");
236         }
237         if (!isCurrentThreadCpuTimeSupported()) {
238             throw new UnsupportedOperationException(
239                 "Current thread CPU time measurement is not supported.");
240         }
241         return isThreadCpuTimeEnabled();
242     }
243 
244     @Override
245     public long getCurrentThreadCpuTime() {
246         if (verifyCurrentThreadCpuTime()) {
247             return getThreadTotalCpuTime0(0);
248         }
249         return -1;
250     }
251 
252     @Override
253     public long getThreadCpuTime(long id) {
254         long[] ids = new long[1];
255         ids[0] = id;
256         final long[] times = getThreadCpuTime(ids);
257         return times[0];
258     }
259 
260     private boolean verifyThreadCpuTime(long[] ids) {
261         verifyThreadIds(ids);
262 
263         // check if Thread CPU time measurement is supported.
264         if (!isThreadCpuTimeSupported() &&
265             !isCurrentThreadCpuTimeSupported()) {
266             throw new UnsupportedOperationException(
267                 "Thread CPU time measurement is not supported.");
268         }
269 
270         if (!isThreadCpuTimeSupported()) {
271             // support current thread only
272             for (int i = 0; i < ids.length; i++) {
273                 if (ids[i] != Thread.currentThread().getId()) {
274                     throw new UnsupportedOperationException(
275                         "Thread CPU time measurement is only supported" +
276                         " for the current thread.");
277                 }
278             }
279         }
280 
281         return isThreadCpuTimeEnabled();
282     }
283 
284     protected long[] getThreadCpuTime(long[] ids) {
285         boolean verified = verifyThreadCpuTime(ids);
286 
287         int length = ids.length;
288         long[] times = new long[length];
289         java.util.Arrays.fill(times, -1);
290 
291         if (verified) {
292             if (length == 1) {
293                 long id = ids[0];
294                 Thread thread = Thread.currentThread();
295                 if (id == thread.getId()) {
296                     if (isVirtual(thread)) {
297                         times[0] = -1;
298                     } else {
299                         times[0] = getThreadTotalCpuTime0(0);
300                     }
301                 } else {
302                     times[0] = getThreadTotalCpuTime0(id);
303                 }

304             } else {
305                 getThreadTotalCpuTime1(ids, times);
306             }
307         }
308         return times;
309     }
310 
311     @Override
312     public long getCurrentThreadUserTime() {
313         if (verifyCurrentThreadCpuTime()) {
314             return getThreadUserCpuTime0(0);
315         }
316         return -1;
317     }
318 
319     @Override
320     public long getThreadUserTime(long id) {
321         long[] ids = new long[1];
322         ids[0] = id;
323         final long[] times = getThreadUserTime(ids);
324         return times[0];
325     }
326 
327     protected long[] getThreadUserTime(long[] ids) {
328         boolean verified = verifyThreadCpuTime(ids);
329 
330         int length = ids.length;
331         long[] times = new long[length];
332         java.util.Arrays.fill(times, -1);
333 
334         if (verified) {
335             if (length == 1) {
336                 long id = ids[0];
337                 Thread thread = Thread.currentThread();
338                 if (id == thread.getId()) {
339                     if (isVirtual(thread)) {
340                         times[0] = -1;
341                     } else {
342                         times[0] = getThreadUserCpuTime0(0);
343                     }
344                 } else {
345                     times[0] = getThreadUserCpuTime0(id);
346                 }

347             } else {
348                 getThreadUserCpuTime1(ids, times);
349             }
350         }
351         return times;
352     }
353 
354     @Override
355     public void setThreadCpuTimeEnabled(boolean enable) {
356         if (!isThreadCpuTimeSupported() &&
357             !isCurrentThreadCpuTimeSupported()) {
358             throw new UnsupportedOperationException(
359                 "Thread CPU time measurement is not supported");
360         }
361 
362         Util.checkControlAccess();
363         synchronized (this) {
364             if (cpuTimeEnabled != enable) {
365                 // notify VM of the state change
366                 setThreadCpuTimeEnabled0(enable);
367                 cpuTimeEnabled = enable;
368             }
369         }
370     }
371 
372     protected long getCurrentThreadAllocatedBytes() {
373         if (isThreadAllocatedMemoryEnabled() && !isVirtual(Thread.currentThread())) {
374             return getThreadAllocatedMemory0(0);
375         }
376         return -1;
377     }
378 
379     private boolean verifyThreadAllocatedMemory(long id) {
380         verifyThreadId(id);
381         return isThreadAllocatedMemoryEnabled();
382     }
383 
384     protected long getThreadAllocatedBytes(long id) {
385         boolean verified = verifyThreadAllocatedMemory(id);

386         if (verified) {
387             Thread thread = Thread.currentThread();
388             if (id == thread.getId()) {
389                 if (isVirtual(thread)) {
390                     return -1L;
391                 } else {
392                     return getThreadAllocatedMemory0(0);
393                 }
394             } else {
395                 return getThreadAllocatedMemory0(id);
396             }
397         }
398         return -1;
399     }
400 
401     private boolean verifyThreadAllocatedMemory(long[] ids) {
402         verifyThreadIds(ids);
403         return isThreadAllocatedMemoryEnabled();
404     }
405 
406     protected long[] getThreadAllocatedBytes(long[] ids) {
407         Objects.requireNonNull(ids);
408 
409         if (ids.length == 1) {
410             long size = getThreadAllocatedBytes(ids[0]);
411             return new long[] { size };
412         }
413 
414         boolean verified = verifyThreadAllocatedMemory(ids);
415 
416         long[] sizes = new long[ids.length];
417         java.util.Arrays.fill(sizes, -1);
418 
419         if (verified) {
420             getThreadAllocatedMemory1(ids, sizes);
421         }
422         return sizes;
423     }
424 
425     protected void setThreadAllocatedMemoryEnabled(boolean enable) {
426         ensureThreadAllocatedMemorySupported();
427 
428         Util.checkControlAccess();
429         synchronized (this) {
430             if (allocatedMemoryEnabled != enable) {
431                 // notify VM of the state change
432                 setThreadAllocatedMemoryEnabled0(enable);
433                 allocatedMemoryEnabled = enable;
434             }
435         }
436     }
437 
438     @Override
439     public long[] findMonitorDeadlockedThreads() {
440         Util.checkMonitorAccess();
441 
442         Thread[] threads = findMonitorDeadlockedThreads0();
443         if (threads == null) {
444             return null;
445         }
446 
447         long[] ids = new long[threads.length];
448         for (int i = 0; i < threads.length; i++) {
449             Thread t = threads[i];
450             ids[i] = t.getId();
451         }
452         return ids;
453     }
454 
455     @Override
456     public long[] findDeadlockedThreads() {
457         if (!isSynchronizerUsageSupported()) {
458             throw new UnsupportedOperationException(
459                 "Monitoring of Synchronizer Usage is not supported.");
460         }
461 
462         Util.checkMonitorAccess();
463 
464         Thread[] threads = findDeadlockedThreads0();
465         if (threads == null) {
466             return null;
467         }
468 
469         long[] ids = new long[threads.length];
470         for (int i = 0; i < threads.length; i++) {
471             Thread t = threads[i];
472             ids[i] = t.getId();
473         }
474         return ids;
475     }
476 
477     @Override
478     public void resetPeakThreadCount() {
479         Util.checkControlAccess();
480         resetPeakThreadCount0();
481     }
482 
483     @Override
484     public boolean isObjectMonitorUsageSupported() {
485         return jvm.isObjectMonitorUsageSupported();
486     }
487 
488     @Override
489     public boolean isSynchronizerUsageSupported() {
490         return jvm.isSynchronizerUsageSupported();
491     }
492 
493     private void verifyDumpThreads(boolean lockedMonitors,
494                                    boolean lockedSynchronizers) {
495         if (lockedMonitors && !isObjectMonitorUsageSupported()) {
496             throw new UnsupportedOperationException(
497                 "Monitoring of Object Monitor Usage is not supported.");
498         }
499 
500         if (lockedSynchronizers && !isSynchronizerUsageSupported()) {
501             throw new UnsupportedOperationException(
502                 "Monitoring of Synchronizer Usage is not supported.");
503         }
504 
505         Util.checkMonitorAccess();
506     }
507 
508     @Override
509     public ThreadInfo[] getThreadInfo(long[] ids,
510                                       boolean lockedMonitors,
511                                       boolean lockedSynchronizers) {
512         return dumpThreads0(ids, lockedMonitors, lockedSynchronizers,
513                             Integer.MAX_VALUE);
514     }
515 
516     public ThreadInfo[] getThreadInfo(long[] ids,
517                                       boolean lockedMonitors,
518                                       boolean lockedSynchronizers,
519                                       int maxDepth) {
520         if (maxDepth < 0) {
521             throw new IllegalArgumentException(
522                     "Invalid maxDepth parameter: " + maxDepth);
523         }
524         verifyThreadIds(ids);
525         // ids has been verified to be non-null
526         // an empty array of ids should return an empty array of ThreadInfos
527         if (ids.length == 0) return new ThreadInfo[0];
528 
529         verifyDumpThreads(lockedMonitors, lockedSynchronizers);
530         return dumpThreads0(ids, lockedMonitors, lockedSynchronizers, maxDepth);
531     }
532 
533     @Override
534     public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
535                                        boolean lockedSynchronizers) {
536         return dumpAllThreads(lockedMonitors, lockedSynchronizers,
537                               Integer.MAX_VALUE);
538     }
539 
540     public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
541                                        boolean lockedSynchronizers,
542                                        int maxDepth) {
543         if (maxDepth < 0) {
544             throw new IllegalArgumentException(
545                     "Invalid maxDepth parameter: " + maxDepth);
546         }
547         verifyDumpThreads(lockedMonitors, lockedSynchronizers);
548         return dumpThreads0(null, lockedMonitors, lockedSynchronizers, maxDepth);
549     }
550 
551     // VM support where maxDepth == -1 to request entire stack dump
552     private static native Thread[] getThreads();
553     private static native void getThreadInfo1(long[] ids,
554                                               int maxDepth,
555                                               ThreadInfo[] result);
556     private static native long getThreadTotalCpuTime0(long id);
557     private static native void getThreadTotalCpuTime1(long[] ids, long[] result);
558     private static native long getThreadUserCpuTime0(long id);
559     private static native void getThreadUserCpuTime1(long[] ids, long[] result);
560     private static native long getThreadAllocatedMemory0(long id);
561     private static native void getThreadAllocatedMemory1(long[] ids, long[] result);
562     private static native void setThreadCpuTimeEnabled0(boolean enable);
563     private static native void setThreadAllocatedMemoryEnabled0(boolean enable);
564     private static native void setThreadContentionMonitoringEnabled0(boolean enable);
565     private static native Thread[] findMonitorDeadlockedThreads0();
566     private static native Thread[] findDeadlockedThreads0();
567     private static native void resetPeakThreadCount0();
568     private static native ThreadInfo[] dumpThreads0(long[] ids,
569                                                     boolean lockedMonitors,
570                                                     boolean lockedSynchronizers,
571                                                     int maxDepth);
572 
573     // tid == 0 to reset contention times for all threads
574     private static native void resetContentionTimes0(long tid);
575 
576     @Override
577     public ObjectName getObjectName() {
578         return Util.newObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
579     }
580 
581     /**
582      * Returns true if the given Thread is a virutal thread.
583      */
584     private static boolean isVirtual(Thread thread) {
585         try {
586             return (boolean) IS_VIRTUAL.invoke(thread);
587         } catch (IllegalAccessException | InvocationTargetException e) {
588             throw new InternalError(e);
589         }
590     }
591 
592     static final Method IS_VIRTUAL;
593     static {
594         try {
595             PrivilegedExceptionAction<Method> pa = () -> Thread.class.getMethod("isVirtual");
596             @SuppressWarnings("removal")
597             Method m = AccessController.doPrivileged(pa);
598             IS_VIRTUAL = m;
599         } catch (PrivilegedActionException e) {
600             throw new InternalError(e);
601         }
602 
603     }
604 }
--- EOF ---