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     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         return dumpThreads0(ids, lockedMonitors, lockedSynchronizers,
 467                             Integer.MAX_VALUE);
 468     }
 469 
 470     public ThreadInfo[] getThreadInfo(long[] ids,
 471                                       boolean lockedMonitors,
 472                                       boolean lockedSynchronizers,
 473                                       int maxDepth) {
 474         if (maxDepth < 0) {
 475             throw new IllegalArgumentException(
 476                     "Invalid maxDepth parameter: " + maxDepth);
 477         }
 478         verifyThreadIds(ids);
 479         // ids has been verified to be non-null
 480         // an empty array of ids should return an empty array of ThreadInfos
 481         if (ids.length == 0) return new ThreadInfo[0];
 482 
 483         verifyDumpThreads(lockedMonitors, lockedSynchronizers);
 484         return dumpThreads0(ids, lockedMonitors, lockedSynchronizers, maxDepth);
 485     }
 486 
 487     @Override
 488     public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
 489                                        boolean lockedSynchronizers) {
 490         return dumpAllThreads(lockedMonitors, lockedSynchronizers,
 491                               Integer.MAX_VALUE);
 492     }
 493 
 494     public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
 495                                        boolean lockedSynchronizers,
 496                                        int maxDepth) {
 497         if (maxDepth < 0) {
 498             throw new IllegalArgumentException(
 499                     "Invalid maxDepth parameter: " + maxDepth);
 500         }
 501         verifyDumpThreads(lockedMonitors, lockedSynchronizers);
 502         return dumpThreads0(null, lockedMonitors, lockedSynchronizers, maxDepth);
 503     }
 504 
 505     // VM support where maxDepth == -1 to request entire stack dump
 506     private static native Thread[] getThreads();
 507     private static native void getThreadInfo1(long[] ids,
 508                                               int maxDepth,
 509                                               ThreadInfo[] result);
 510     private static native long getThreadTotalCpuTime0(long id);
 511     private static native void getThreadTotalCpuTime1(long[] ids, long[] result);
 512     private static native long getThreadUserCpuTime0(long id);
 513     private static native void getThreadUserCpuTime1(long[] ids, long[] result);
 514     private static native void getThreadAllocatedMemory1(long[] ids, long[] result);
 515     private static native void setThreadCpuTimeEnabled0(boolean enable);
 516     private static native void setThreadAllocatedMemoryEnabled0(boolean enable);
 517     private static native void setThreadContentionMonitoringEnabled0(boolean enable);
 518     private static native Thread[] findMonitorDeadlockedThreads0();
 519     private static native Thread[] findDeadlockedThreads0();
 520     private static native void resetPeakThreadCount0();
 521     private static native ThreadInfo[] dumpThreads0(long[] ids,
 522                                                     boolean lockedMonitors,
 523                                                     boolean lockedSynchronizers,
 524                                                     int maxDepth);
 525 
 526     // tid == 0 to reset contention times for all threads
 527     private static native void resetContentionTimes0(long tid);
 528 
 529     @Override
 530     public ObjectName getObjectName() {
 531         return Util.newObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
 532     }
 533 
 534 }