1 /* 2 * Copyright (c) 2025, 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 package jdk.internal.vm; 26 27 import java.util.Arrays; 28 import java.util.stream.Stream; 29 30 /** 31 * Represents a snapshot of information about a Thread. 32 */ 33 class ThreadSnapshot { 34 private static final StackTraceElement[] EMPTY_STACK = new StackTraceElement[0]; 35 private static final ThreadLock[] EMPTY_LOCKS = new ThreadLock[0]; 36 37 private String name; 38 private int threadStatus; 39 private StackTraceElement[] stackTrace; 40 private ThreadLock[] locks; 41 42 // called by the VM 43 private ThreadSnapshot(StackTraceElement[] stackTrace, 44 ThreadLock[] locks, 45 //String name, 46 int threadStatus) { 47 this.stackTrace = stackTrace; 48 this.locks = locks; 49 this.name = name; 50 this.threadStatus = threadStatus; 51 } 52 53 /** 54 * Take a snapshot of a Thread to get all information about the thread. 55 */ 56 static ThreadSnapshot of(Thread thread) { 57 ThreadSnapshot snapshot = create(thread, true); 58 if (snapshot.stackTrace == null) { 59 snapshot.stackTrace = EMPTY_STACK; 60 } 61 if (snapshot.locks == null) { 62 snapshot.locks = EMPTY_LOCKS; 63 } 64 if (snapshot.name == null) { 65 snapshot.name = thread.getName(); // temp 66 } 67 return snapshot; 68 } 69 70 /** 71 * Returns the thread name. 72 */ 73 String threadName() { 74 return name; 75 } 76 77 /** 78 * Returns the thread state. 79 */ 80 Thread.State threadState() { 81 // is this valid for virtual threads 82 return jdk.internal.misc.VM.toThreadState(threadStatus); 83 } 84 85 /** 86 * Returns the thread stack trace. 87 */ 88 StackTraceElement[] stackTrace() { 89 return stackTrace; 90 } 91 92 /** 93 * Returns the thread's parkBlocker. 94 */ 95 Object parkBlocker() { 96 return findLockObject(0, LockType.PARKING_TO_WAIT) 97 .findAny() 98 .orElse(null); 99 } 100 101 /** 102 * Returns the object that the thread is blocked on. 103 * @throws IllegalStateException if not in the blocked state 104 */ 105 Object blockedOn() { 106 if (threadState() != Thread.State.BLOCKED) { 107 throw new IllegalStateException(); 108 } 109 return findLockObject(0, LockType.WAITING_TO_LOCK) 110 .findAny() 111 .orElse(null); 112 } 113 114 /** 115 * Returns the object that the thread is waiting on. 116 * @throws IllegalStateException if not in the waiting state 117 */ 118 Object waitingOn() { 119 if (threadState() != Thread.State.WAITING 120 && threadState() != Thread.State.TIMED_WAITING) { 121 throw new IllegalStateException(); 122 } 123 return findLockObject(0, LockType.WAITING_ON) 124 .findAny() 125 .orElse(null); 126 } 127 128 /** 129 * Returns true if the thread owns any object monitors. 130 */ 131 boolean ownsMonitors() { 132 return Arrays.stream(locks) 133 .anyMatch(lock -> lock.type == LockType.LOCKED); 134 } 135 136 /** 137 * Returns the objects that the thread locked at the given depth. 138 */ 139 Stream<Object> ownedMonitorsAt(int depth) { 140 return findLockObject(depth, LockType.LOCKED); 141 } 142 143 private Stream<Object> findLockObject(int depth, LockType type) { 144 return Arrays.stream(locks) 145 .filter(lock -> lock.depth == depth 146 && lock.type() == type 147 && lock.lockObject() != null) 148 .map(ThreadLock::lockObject); 149 } 150 151 /** 152 * Represents information about a locking operation. 153 */ 154 private enum LockType { 155 // Park blocker 156 PARKING_TO_WAIT, 157 // Lock object is a class of the eliminated monitor 158 ELEMINATED_SCALAR_REPLACED, 159 ELEMINATED_MONITOR, 160 LOCKED, 161 WAITING_TO_LOCK, 162 WAITING_ON, 163 WAITING_TO_RELOCK, 164 // No corresponding stack frame, depth is always == -1 165 OWNABLE_SYNCHRONIZER 166 } 167 168 /** 169 * Represents a locking operation of a thread at a specific stack depth. 170 */ 171 private record ThreadLock(int depth, LockType type, Object obj) { 172 private static final LockType[] lockTypeValues = LockType.values(); // cache 173 174 // called by the VM 175 private ThreadLock(int depth, int typeOrdinal, Object obj) { 176 this(depth, lockTypeValues[typeOrdinal], obj); 177 } 178 179 Object lockObject() { 180 if (type == LockType.ELEMINATED_SCALAR_REPLACED) { 181 // we have no lock object, lock contains lock class 182 return null; 183 } 184 return obj; 185 } 186 } 187 188 private static native ThreadSnapshot create(Thread thread, boolean withLocks); 189 }