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