1 /*
   2  * Copyright (c) 2004, 2018, 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.ThreadInfo;
  29 import java.lang.management.MonitorInfo;
  30 import java.lang.management.LockInfo;
  31 import java.util.Arrays;
  32 import java.util.HashMap;
  33 import java.util.Map;
  34 import java.util.stream.Stream;
  35 import javax.management.openmbean.ArrayType;
  36 import javax.management.openmbean.CompositeType;
  37 import javax.management.openmbean.CompositeData;
  38 import javax.management.openmbean.CompositeDataSupport;
  39 import javax.management.openmbean.OpenDataException;
  40 import javax.management.openmbean.OpenType;
  41 
  42 /**
  43  * A CompositeData for ThreadInfo for the local management support.
  44  * This class avoids the performance penalty paid to the
  45  * construction of a CompositeData use in the local case.
  46  */
  47 public class ThreadInfoCompositeData extends LazyCompositeData {
  48     private final ThreadInfo threadInfo;
  49     private final CompositeData cdata;
  50 
  51     private ThreadInfoCompositeData(ThreadInfo ti) {
  52         this.threadInfo = ti;
  53         this.cdata = null;
  54     }
  55 
  56     private ThreadInfoCompositeData(CompositeData cd) {
  57         this.threadInfo = null;
  58         this.cdata = cd;
  59     }
  60 
  61     public ThreadInfo getThreadInfo() {
  62         return threadInfo;
  63     }
  64 
  65     public static ThreadInfoCompositeData getInstance(CompositeData cd) {
  66         validateCompositeData(cd);
  67         return new ThreadInfoCompositeData(cd);
  68     }
  69 
  70     public static CompositeData toCompositeData(ThreadInfo ti) {
  71         ThreadInfoCompositeData ticd = new ThreadInfoCompositeData(ti);
  72         return ticd.getCompositeData();
  73     }
  74 
  75     protected CompositeData getCompositeData() {
  76         // Convert StackTraceElement[] to CompositeData[]
  77         StackTraceElement[] stackTrace = threadInfo.getStackTrace();
  78         CompositeData[] stackTraceData =
  79             new CompositeData[stackTrace.length];
  80         for (int i = 0; i < stackTrace.length; i++) {
  81             StackTraceElement ste = stackTrace[i];
  82             stackTraceData[i] = StackTraceElementCompositeData.toCompositeData(ste);
  83         }
  84 
  85         // Convert MonitorInfo[] and LockInfo[] to CompositeData[]
  86         CompositeData lockInfoData =
  87             LockInfoCompositeData.toCompositeData(threadInfo.getLockInfo());
  88 
  89         // Convert LockInfo[] and MonitorInfo[] to CompositeData[]
  90         LockInfo[] lockedSyncs = threadInfo.getLockedSynchronizers();
  91         CompositeData[] lockedSyncsData =
  92             new CompositeData[lockedSyncs.length];
  93         for (int i = 0; i < lockedSyncs.length; i++) {
  94             LockInfo li = lockedSyncs[i];
  95             lockedSyncsData[i] = LockInfoCompositeData.toCompositeData(li);
  96         }
  97 
  98         MonitorInfo[] lockedMonitors = threadInfo.getLockedMonitors();
  99         CompositeData[] lockedMonitorsData =
 100             new CompositeData[lockedMonitors.length];
 101         for (int i = 0; i < lockedMonitors.length; i++) {
 102             MonitorInfo mi = lockedMonitors[i];
 103             lockedMonitorsData[i] = MonitorInfoCompositeData.toCompositeData(mi);
 104         }
 105 
 106         // CONTENTS OF THIS ARRAY MUST BE SYNCHRONIZED WITH
 107         // THREAD_INFO_ATTRIBUTES!
 108         final Object[] threadInfoItemValues = {
 109             threadInfo.getThreadId(),
 110             threadInfo.getThreadName(),
 111             threadInfo.getThreadState().name(),
 112             threadInfo.getBlockedTime(),
 113             threadInfo.getBlockedCount(),
 114             threadInfo.getWaitedTime(),
 115             threadInfo.getWaitedCount(),
 116             lockInfoData,
 117             threadInfo.getLockName(),
 118             threadInfo.getLockOwnerId(),
 119             threadInfo.getLockOwnerName(),
 120             stackTraceData,
 121             threadInfo.isSuspended(),
 122             threadInfo.isInNative(),
 123             lockedMonitorsData,
 124             lockedSyncsData,
 125             threadInfo.isDaemon(),
 126             threadInfo.getPriority(),
 127         };
 128 
 129         try {
 130             return new CompositeDataSupport(compositeType(),
 131                                             THREAD_INFO_ATTRIBTUES,
 132                                             threadInfoItemValues);
 133         } catch (OpenDataException e) {
 134             // Should never reach here
 135             throw new AssertionError(e);
 136         }
 137     }
 138 
 139     // Attribute names
 140     private static final String THREAD_ID       = "threadId";
 141     private static final String THREAD_NAME     = "threadName";
 142     private static final String THREAD_STATE    = "threadState";
 143     private static final String BLOCKED_TIME    = "blockedTime";
 144     private static final String BLOCKED_COUNT   = "blockedCount";
 145     private static final String WAITED_TIME     = "waitedTime";
 146     private static final String WAITED_COUNT    = "waitedCount";
 147     private static final String LOCK_INFO       = "lockInfo";
 148     private static final String LOCK_NAME       = "lockName";
 149     private static final String LOCK_OWNER_ID   = "lockOwnerId";
 150     private static final String LOCK_OWNER_NAME = "lockOwnerName";
 151     private static final String STACK_TRACE     = "stackTrace";
 152     private static final String SUSPENDED       = "suspended";
 153     private static final String IN_NATIVE       = "inNative";
 154     private static final String DAEMON          = "daemon";
 155     private static final String PRIORITY        = "priority";
 156     private static final String LOCKED_MONITORS = "lockedMonitors";
 157     private static final String LOCKED_SYNCS    = "lockedSynchronizers";
 158 
 159     private static final String[] V5_ATTRIBUTES = {
 160         THREAD_ID,
 161         THREAD_NAME,
 162         THREAD_STATE,
 163         BLOCKED_TIME,
 164         BLOCKED_COUNT,
 165         WAITED_TIME,
 166         WAITED_COUNT,
 167         LOCK_NAME,
 168         LOCK_OWNER_ID,
 169         LOCK_OWNER_NAME,
 170         STACK_TRACE,
 171         SUSPENDED,
 172         IN_NATIVE
 173     };
 174 
 175     private static final String[] V6_ATTRIBUTES = {
 176         LOCK_INFO,
 177         LOCKED_MONITORS,
 178         LOCKED_SYNCS,
 179     };
 180 
 181     private static final String[] V9_ATTRIBUTES = {
 182         DAEMON,
 183         PRIORITY,
 184     };
 185 
 186     private static final String[] THREAD_INFO_ATTRIBTUES =
 187         Stream.of(V5_ATTRIBUTES, V6_ATTRIBUTES, V9_ATTRIBUTES)
 188               .flatMap(Arrays::stream).toArray(String[]::new);
 189 
 190     public long threadId() {
 191         return getLong(cdata, THREAD_ID);
 192     }
 193 
 194     public String threadName() {
 195         // The ThreadName item cannot be null so we check that
 196         // it is present with a non-null value.
 197         String name = getString(cdata, THREAD_NAME);
 198         if (name == null) {
 199             throw new IllegalArgumentException("Invalid composite data: " +
 200                 "Attribute " + THREAD_NAME + " has null value");
 201         }
 202         return name;
 203     }
 204 
 205     public Thread.State threadState() {
 206         return Thread.State.valueOf(getString(cdata, THREAD_STATE));
 207     }
 208 
 209     public long blockedTime() {
 210         return getLong(cdata, BLOCKED_TIME);
 211     }
 212 
 213     public long blockedCount() {
 214         return getLong(cdata, BLOCKED_COUNT);
 215     }
 216 
 217     public long waitedTime() {
 218         return getLong(cdata, WAITED_TIME);
 219     }
 220 
 221     public long waitedCount() {
 222         return getLong(cdata, WAITED_COUNT);
 223     }
 224 
 225     public String lockName() {
 226         // The LockName and LockOwnerName can legitimately be null,
 227         // we don't bother to check the value
 228         return getString(cdata, LOCK_NAME);
 229     }
 230 
 231     public long lockOwnerId() {
 232         return getLong(cdata, LOCK_OWNER_ID);
 233     }
 234 
 235     public String lockOwnerName() {
 236         return getString(cdata, LOCK_OWNER_NAME);
 237     }
 238 
 239     public boolean suspended() {
 240         return getBoolean(cdata, SUSPENDED);
 241     }
 242 
 243     public boolean inNative() {
 244         return getBoolean(cdata, IN_NATIVE);
 245     }
 246 
 247     /*
 248      * if daemon attribute is not present, default to false.
 249      */
 250     public boolean isDaemon() {
 251         return cdata.containsKey(DAEMON) ? getBoolean(cdata, DAEMON) : false;
 252     }
 253 
 254     /*
 255      * if priority attribute is not present, default to norm priority.
 256      */
 257     public int getPriority(){
 258         return cdata.containsKey(PRIORITY) ? getInt(cdata, PRIORITY) : Thread.NORM_PRIORITY;
 259     }
 260 
 261     public StackTraceElement[] stackTrace() {
 262         CompositeData[] stackTraceData =
 263             (CompositeData[]) cdata.get(STACK_TRACE);
 264 
 265         // The StackTrace item cannot be null, but if it is we will get
 266         // a NullPointerException when we ask for its length.
 267         StackTraceElement[] stackTrace =
 268             new StackTraceElement[stackTraceData.length];
 269         for (int i = 0; i < stackTraceData.length; i++) {
 270             CompositeData cdi = stackTraceData[i];
 271             stackTrace[i] = StackTraceElementCompositeData.from(cdi);
 272         }
 273         return stackTrace;
 274     }
 275 
 276     /*
 277      * lockInfo is a new attribute added in JDK 6 ThreadInfo
 278      * If cd is a 5.0 version, construct the LockInfo object
 279      * from the lockName value.
 280      */
 281     public LockInfo lockInfo() {
 282         if (cdata.containsKey(LOCK_INFO)) {
 283             CompositeData lockInfoData = (CompositeData) cdata.get(LOCK_INFO);
 284             return LockInfo.from(lockInfoData);
 285         } else {
 286             String lockName = lockName();
 287             LockInfo lock = null;
 288             if (lockName != null) {
 289                 String result[] = lockName.split("@");
 290                 if (result.length == 2) {
 291                     int identityHashCode = Integer.parseInt(result[1], 16);
 292                     lock = new LockInfo(result[0], identityHashCode);
 293                 }
 294             }
 295             return lock;
 296         }
 297     }
 298 
 299     /**
 300      * Returns an empty array if locked_monitors attribute is not present.
 301      */
 302     public MonitorInfo[] lockedMonitors() {
 303         if (!cdata.containsKey(LOCKED_MONITORS)) {
 304             return new MonitorInfo[0];
 305         }
 306 
 307         CompositeData[] lockedMonitorsData =
 308             (CompositeData[]) cdata.get(LOCKED_MONITORS);
 309 
 310         // The LockedMonitors item cannot be null, but if it is we will get
 311         // a NullPointerException when we ask for its length.
 312         MonitorInfo[] monitors =
 313             new MonitorInfo[lockedMonitorsData.length];
 314         for (int i = 0; i < lockedMonitorsData.length; i++) {
 315             CompositeData cdi = lockedMonitorsData[i];
 316             monitors[i] = MonitorInfo.from(cdi);
 317         }
 318         return monitors;
 319     }
 320 
 321     /**
 322      * Returns an empty array if locked_monitors attribute is not present.
 323      */
 324     public LockInfo[] lockedSynchronizers() {
 325         if (!cdata.containsKey(LOCKED_SYNCS)) {
 326             return new LockInfo[0];
 327         }
 328 
 329         CompositeData[] lockedSyncsData =
 330             (CompositeData[]) cdata.get(LOCKED_SYNCS);
 331 
 332         // The LockedSynchronizers item cannot be null, but if it is we will
 333         // get a NullPointerException when we ask for its length.
 334         LockInfo[] locks = new LockInfo[lockedSyncsData.length];
 335         for (int i = 0; i < lockedSyncsData.length; i++) {
 336             CompositeData cdi = lockedSyncsData[i];
 337             locks[i] = LockInfo.from(cdi);
 338         }
 339         return locks;
 340     }
 341 
 342     /**
 343      * Validate if the input CompositeData has the expected
 344      * CompositeType (i.e. contain all attributes with expected
 345      * names and types).
 346      */
 347     public static void validateCompositeData(CompositeData cd) {
 348         if (cd == null) {
 349             throw new NullPointerException("Null CompositeData");
 350         }
 351 
 352         CompositeType type = cd.getCompositeType();
 353         int version;
 354         if (Arrays.stream(V9_ATTRIBUTES).anyMatch(type::containsKey)) {
 355             version = Runtime.version().feature();
 356         } else if (Arrays.stream(V6_ATTRIBUTES).anyMatch(type::containsKey)) {
 357             version = 6;
 358         } else {
 359             version = 5;
 360         }
 361 
 362         if (!isTypeMatched(ThreadInfoCompositeTypes.ofVersion(version), type)) {
 363             throw new IllegalArgumentException(
 364                 "Unexpected composite type for ThreadInfo of version " + version);
 365         }
 366     }
 367 
 368     public static CompositeType compositeType() {
 369         return ThreadInfoCompositeTypes.compositeTypes.get(0);
 370     }
 371 
 372     static class ThreadInfoCompositeTypes {
 373         static final int CURRENT =  Runtime.version().feature();
 374         static final Map<Integer, CompositeType> compositeTypes = initCompositeTypes();
 375         /*
 376          * Returns CompositeType of the given runtime version
 377          */
 378         static CompositeType ofVersion(int version) {
 379             return compositeTypes.get(version);
 380         }
 381 
 382         static Map<Integer, CompositeType> initCompositeTypes() {
 383             Map<Integer, CompositeType> types = new HashMap<>();
 384             CompositeType ctype = initCompositeType();
 385             types.put(CURRENT, ctype);
 386             types.put(5, initV5CompositeType(ctype));
 387             types.put(6, initV6CompositeType(ctype));
 388             return types;
 389         }
 390 
 391         static CompositeType initCompositeType() {
 392             try {
 393                 return (CompositeType)MappedMXBeanType.toOpenType(ThreadInfo.class);
 394             } catch (OpenDataException e) {
 395                 // Should never reach here
 396                 throw new AssertionError(e);
 397             }
 398         }
 399 
 400         static CompositeType initV5CompositeType(CompositeType threadInfoCompositeType) {
 401             try {
 402                 OpenType<?>[] v5Types = new OpenType<?>[V5_ATTRIBUTES.length];
 403                 for (int i = 0; i < v5Types.length; i++) {
 404                     String name = V5_ATTRIBUTES[i];
 405                     v5Types[i] = name.equals(STACK_TRACE)
 406                         ? new ArrayType<>(1, StackTraceElementCompositeData.v5CompositeType())
 407                         : threadInfoCompositeType.getType(name);
 408                 }
 409                 return new CompositeType("ThreadInfo",
 410                                          "JDK 5 ThreadInfo",
 411                                          V5_ATTRIBUTES,
 412                                          V5_ATTRIBUTES,
 413                                          v5Types);
 414             } catch (OpenDataException e) {
 415                 // Should never reach here
 416                 throw new AssertionError(e);
 417             }
 418         }
 419 
 420         static CompositeType initV6CompositeType(CompositeType threadInfoCompositeType) {
 421             try {
 422                 String[] v6Names = Stream.of(V5_ATTRIBUTES, V6_ATTRIBUTES)
 423                     .flatMap(Arrays::stream).toArray(String[]::new);
 424                 OpenType<?>[] v6Types = new OpenType<?>[v6Names.length];
 425                 for (int i = 0; i < v6Names.length; i++) {
 426                     String name = v6Names[i];
 427                     OpenType<?> ot = threadInfoCompositeType.getType(name);
 428                     if (name.equals(STACK_TRACE)) {
 429                         ot = new ArrayType<>(1, StackTraceElementCompositeData.v5CompositeType());
 430                     } else if (name.equals(LOCKED_MONITORS)) {
 431                         ot = new ArrayType<>(1, MonitorInfoCompositeData.v6CompositeType());
 432                     }
 433                     v6Types[i] = ot;
 434                 }
 435                 return new CompositeType("ThreadInfo",
 436                                          "JDK 6 ThreadInfo",
 437                                          v6Names,
 438                                          v6Names,
 439                                          v6Types);
 440             } catch (OpenDataException e) {
 441                 // Should never reach here
 442                 throw new AssertionError(e);
 443             }
 444         }
 445     }
 446     private static final long serialVersionUID = 2464378539119753175L;
 447 }