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