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 
  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 
  40 public 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     private void throwIfThreadAllocatedMemoryNotSupported() {
 116         if (!isThreadAllocatedMemorySupported()) {
 117             throw new UnsupportedOperationException(
 118                 "Thread allocated memory measurement is not supported.");
 119         }
 120     }
 121 
 122     protected boolean isThreadAllocatedMemoryEnabled() {
 123         throwIfThreadAllocatedMemoryNotSupported();
 124         return allocatedMemoryEnabled;
 125     }
 126 
 127     @Override
 128     public long[] getAllThreadIds() {
 129         Util.checkMonitorAccess();
 130 
 131         Thread[] threads = getThreads();
 132         int length = threads.length;
 133         long[] ids = new long[length];
 134         for (int i = 0; i < length; i++) {
 135             Thread t = threads[i];
 136             ids[i] = t.getId();
 137         }
 138         return ids;
 139     }
 140 
 141     @Override
 142     public ThreadInfo getThreadInfo(long id) {
 143         long[] ids = new long[1];
 144         ids[0] = id;
 145         final ThreadInfo[] infos = getThreadInfo(ids, 0);
 146         return infos[0];
 147     }
 148 
 149     @Override
 150     public ThreadInfo getThreadInfo(long id, int maxDepth) {
 151         long[] ids = new long[1];
 152         ids[0] = id;
 153         final ThreadInfo[] infos = getThreadInfo(ids, maxDepth);
 154         return infos[0];
 155     }
 156 
 157     @Override
 158     public ThreadInfo[] getThreadInfo(long[] ids) {
 159         return getThreadInfo(ids, 0);
 160     }
 161 
 162     private void throwIfNullThreadIds(long[] ids) {
 163         if (ids == null) {
 164             throw new NullPointerException("Null ids parameter.");
 165         }
 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         throwIfNullThreadIds(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 (!isCurrentThreadCpuTimeSupported()) {
 235             throw new UnsupportedOperationException(
 236                 "Current thread CPU time measurement is not supported.");
 237         }
 238         return isThreadCpuTimeEnabled();
 239     }
 240 
 241     @Override
 242     public long getCurrentThreadCpuTime() {
 243         if (verifyCurrentThreadCpuTime()) {
 244             return getThreadTotalCpuTime0(0);
 245         }
 246         return -1;
 247     }
 248 
 249     @Override
 250     public long getThreadCpuTime(long id) {
 251         long[] ids = new long[1];
 252         ids[0] = id;
 253         final long[] times = getThreadCpuTime(ids);
 254         return times[0];
 255     }
 256 
 257     private boolean verifyThreadCpuTime(long[] ids) {
 258         verifyThreadIds(ids);
 259 
 260         // check if Thread CPU time measurement is supported.
 261         if (!isThreadCpuTimeSupported() &&
 262             !isCurrentThreadCpuTimeSupported()) {
 263             throw new UnsupportedOperationException(
 264                 "Thread CPU time measurement is not supported.");
 265         }
 266 
 267         if (!isThreadCpuTimeSupported()) {
 268             // support current thread only
 269             for (int i = 0; i < ids.length; i++) {
 270                 if (ids[i] != Thread.currentThread().getId()) {
 271                     throw new UnsupportedOperationException(
 272                         "Thread CPU time measurement is only supported" +
 273                         " for the current thread.");
 274                 }
 275             }
 276         }
 277 
 278         return isThreadCpuTimeEnabled();
 279     }
 280 
 281     protected long[] getThreadCpuTime(long[] ids) {
 282         boolean verified = verifyThreadCpuTime(ids);
 283 
 284         int length = ids.length;
 285         long[] times = new long[length];
 286         java.util.Arrays.fill(times, -1);
 287 
 288         if (verified) {
 289             if (length == 1) {
 290                 long id = ids[0];
 291                 if (id == Thread.currentThread().getId()) {
 292                     id = 0;
 293                 }
 294                 times[0] = getThreadTotalCpuTime0(id);
 295             } else {
 296                 getThreadTotalCpuTime1(ids, times);
 297             }
 298         }
 299         return times;
 300     }
 301 
 302     @Override
 303     public long getCurrentThreadUserTime() {
 304         if (verifyCurrentThreadCpuTime()) {
 305             return getThreadUserCpuTime0(0);
 306         }
 307         return -1;
 308     }
 309 
 310     @Override
 311     public long getThreadUserTime(long id) {
 312         long[] ids = new long[1];
 313         ids[0] = id;
 314         final long[] times = getThreadUserTime(ids);
 315         return times[0];
 316     }
 317 
 318     protected long[] getThreadUserTime(long[] ids) {
 319         boolean verified = verifyThreadCpuTime(ids);
 320 
 321         int length = ids.length;
 322         long[] times = new long[length];
 323         java.util.Arrays.fill(times, -1);
 324 
 325         if (verified) {
 326             if (length == 1) {
 327                 long id = ids[0];
 328                 if (id == Thread.currentThread().getId()) {
 329                     id = 0;
 330                 }
 331                 times[0] = getThreadUserCpuTime0(id);
 332             } else {
 333                 getThreadUserCpuTime1(ids, times);
 334             }
 335         }
 336         return times;
 337     }
 338 
 339     @Override
 340     public void setThreadCpuTimeEnabled(boolean enable) {
 341         if (!isThreadCpuTimeSupported() &&
 342             !isCurrentThreadCpuTimeSupported()) {
 343             throw new UnsupportedOperationException(
 344                 "Thread CPU time measurement is not supported");
 345         }
 346 
 347         Util.checkControlAccess();
 348         synchronized (this) {
 349             if (cpuTimeEnabled != enable) {
 350                 // notify VM of the state change
 351                 setThreadCpuTimeEnabled0(enable);
 352                 cpuTimeEnabled = enable;
 353             }
 354         }
 355     }
 356 
 357     protected long getCurrentThreadAllocatedBytes() {
 358         if (isThreadAllocatedMemoryEnabled()) {
 359             return getThreadAllocatedMemory0(0);
 360         }
 361         return -1;
 362     }
 363 
 364     private boolean verifyThreadAllocatedMemory(long id) {
 365         verifyThreadId(id);
 366         return isThreadAllocatedMemoryEnabled();
 367     }
 368 
 369     protected long getThreadAllocatedBytes(long id) {
 370         boolean verified = verifyThreadAllocatedMemory(id);
 371 
 372         if (verified) {
 373             return getThreadAllocatedMemory0(
 374                 Thread.currentThread().getId() == id ? 0 : id);
 375         }
 376         return -1;
 377     }
 378 
 379     private boolean verifyThreadAllocatedMemory(long[] ids) {
 380         verifyThreadIds(ids);
 381         return isThreadAllocatedMemoryEnabled();
 382     }
 383 
 384     protected long[] getThreadAllocatedBytes(long[] ids) {
 385         throwIfNullThreadIds(ids);
 386 
 387         if (ids.length == 1) {
 388             long size = getThreadAllocatedBytes(ids[0]);
 389             return new long[] { size };
 390         }
 391 
 392         boolean verified = verifyThreadAllocatedMemory(ids);
 393 
 394         long[] sizes = new long[ids.length];
 395         java.util.Arrays.fill(sizes, -1);
 396 
 397         if (verified) {
 398             getThreadAllocatedMemory1(ids, sizes);
 399         }
 400         return sizes;
 401     }
 402 
 403     protected void setThreadAllocatedMemoryEnabled(boolean enable) {
 404         throwIfThreadAllocatedMemoryNotSupported();
 405 
 406         Util.checkControlAccess();
 407         synchronized (this) {
 408             if (allocatedMemoryEnabled != enable) {
 409                 // notify VM of the state change
 410                 setThreadAllocatedMemoryEnabled0(enable);
 411                 allocatedMemoryEnabled = enable;
 412             }
 413         }
 414     }
 415 
 416     @Override
 417     public long[] findMonitorDeadlockedThreads() {
 418         Util.checkMonitorAccess();
 419 
 420         Thread[] threads = findMonitorDeadlockedThreads0();
 421         if (threads == null) {
 422             return null;
 423         }
 424 
 425         long[] ids = new long[threads.length];
 426         for (int i = 0; i < threads.length; i++) {
 427             Thread t = threads[i];
 428             ids[i] = t.getId();
 429         }
 430         return ids;
 431     }
 432 
 433     @Override
 434     public long[] findDeadlockedThreads() {
 435         if (!isSynchronizerUsageSupported()) {
 436             throw new UnsupportedOperationException(
 437                 "Monitoring of Synchronizer Usage is not supported.");
 438         }
 439 
 440         Util.checkMonitorAccess();
 441 
 442         Thread[] threads = findDeadlockedThreads0();
 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 void resetPeakThreadCount() {
 457         Util.checkControlAccess();
 458         resetPeakThreadCount0();
 459     }
 460 
 461     @Override
 462     public boolean isObjectMonitorUsageSupported() {
 463         return jvm.isObjectMonitorUsageSupported();
 464     }
 465 
 466     @Override
 467     public boolean isSynchronizerUsageSupported() {
 468         return jvm.isSynchronizerUsageSupported();
 469     }
 470 
 471     private void verifyDumpThreads(boolean lockedMonitors,
 472                                    boolean lockedSynchronizers) {
 473         if (lockedMonitors && !isObjectMonitorUsageSupported()) {
 474             throw new UnsupportedOperationException(
 475                 "Monitoring of Object Monitor Usage is not supported.");
 476         }
 477 
 478         if (lockedSynchronizers && !isSynchronizerUsageSupported()) {
 479             throw new UnsupportedOperationException(
 480                 "Monitoring of Synchronizer Usage is not supported.");
 481         }
 482 
 483         Util.checkMonitorAccess();
 484     }
 485 
 486     @Override
 487     public ThreadInfo[] getThreadInfo(long[] ids,
 488                                       boolean lockedMonitors,
 489                                       boolean lockedSynchronizers) {
 490         return dumpThreads0(ids, lockedMonitors, lockedSynchronizers,
 491                             Integer.MAX_VALUE);
 492     }
 493 
 494     public ThreadInfo[] getThreadInfo(long[] ids,
 495                                       boolean lockedMonitors,
 496                                       boolean lockedSynchronizers,
 497                                       int maxDepth) {
 498         if (maxDepth < 0) {
 499             throw new IllegalArgumentException(
 500                     "Invalid maxDepth parameter: " + maxDepth);
 501         }
 502         verifyThreadIds(ids);
 503         // ids has been verified to be non-null
 504         // an empty array of ids should return an empty array of ThreadInfos
 505         if (ids.length == 0) return new ThreadInfo[0];
 506 
 507         verifyDumpThreads(lockedMonitors, lockedSynchronizers);
 508         return dumpThreads0(ids, lockedMonitors, lockedSynchronizers, maxDepth);
 509     }
 510 
 511     @Override
 512     public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
 513                                        boolean lockedSynchronizers) {
 514         return dumpAllThreads(lockedMonitors, lockedSynchronizers,
 515                               Integer.MAX_VALUE);
 516     }
 517 
 518     public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
 519                                        boolean lockedSynchronizers,
 520                                        int maxDepth) {
 521         if (maxDepth < 0) {
 522             throw new IllegalArgumentException(
 523                     "Invalid maxDepth parameter: " + maxDepth);
 524         }
 525         verifyDumpThreads(lockedMonitors, lockedSynchronizers);
 526         return dumpThreads0(null, lockedMonitors, lockedSynchronizers, maxDepth);
 527     }
 528 
 529     // VM support where maxDepth == -1 to request entire stack dump
 530     private static native Thread[] getThreads();
 531     private static native void getThreadInfo1(long[] ids,
 532                                               int maxDepth,
 533                                               ThreadInfo[] result);
 534     private static native long getThreadTotalCpuTime0(long id);
 535     private static native void getThreadTotalCpuTime1(long[] ids, long[] result);
 536     private static native long getThreadUserCpuTime0(long id);
 537     private static native void getThreadUserCpuTime1(long[] ids, long[] result);
 538     private static native long getThreadAllocatedMemory0(long id);
 539     private static native void getThreadAllocatedMemory1(long[] ids, long[] result);
 540     private static native void setThreadCpuTimeEnabled0(boolean enable);
 541     private static native void setThreadAllocatedMemoryEnabled0(boolean enable);
 542     private static native void setThreadContentionMonitoringEnabled0(boolean enable);
 543     private static native Thread[] findMonitorDeadlockedThreads0();
 544     private static native Thread[] findDeadlockedThreads0();
 545     private static native void resetPeakThreadCount0();
 546     private static native ThreadInfo[] dumpThreads0(long[] ids,
 547                                                     boolean lockedMonitors,
 548                                                     boolean lockedSynchronizers,
 549                                                     int maxDepth);
 550 
 551     // tid == 0 to reset contention times for all threads
 552     private static native void resetContentionTimes0(long tid);
 553 
 554     @Override
 555     public ObjectName getObjectName() {
 556         return Util.newObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
 557     }
 558 
 559 }