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