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