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 }