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 }