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 }