1 /* 2 * Copyright (c) 2000, 2024, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.runtime; 26 27 import java.util.*; 28 29 import sun.jvm.hotspot.debugger.*; 30 import sun.jvm.hotspot.types.*; 31 import sun.jvm.hotspot.runtime.win32_amd64.Win32AMD64JavaThreadPDAccess; 32 import sun.jvm.hotspot.runtime.win32_aarch64.Win32AARCH64JavaThreadPDAccess; 33 import sun.jvm.hotspot.runtime.linux_x86.LinuxX86JavaThreadPDAccess; 34 import sun.jvm.hotspot.runtime.linux_amd64.LinuxAMD64JavaThreadPDAccess; 35 import sun.jvm.hotspot.runtime.linux_aarch64.LinuxAARCH64JavaThreadPDAccess; 36 import sun.jvm.hotspot.runtime.linux_riscv64.LinuxRISCV64JavaThreadPDAccess; 37 import sun.jvm.hotspot.runtime.linux_ppc64.LinuxPPC64JavaThreadPDAccess; 38 import sun.jvm.hotspot.runtime.bsd_x86.BsdX86JavaThreadPDAccess; 39 import sun.jvm.hotspot.runtime.bsd_amd64.BsdAMD64JavaThreadPDAccess; 40 import sun.jvm.hotspot.runtime.bsd_aarch64.BsdAARCH64JavaThreadPDAccess; 41 import sun.jvm.hotspot.utilities.*; 42 import sun.jvm.hotspot.utilities.Observable; 43 import sun.jvm.hotspot.utilities.Observer; 44 45 class ThreadsList extends VMObject { 46 private static AddressField threadsField; 47 private static CIntegerField lengthField; 48 49 static { 50 VM.registerVMInitializedObserver((o, d) -> initialize(VM.getVM().getTypeDataBase())); 51 } 52 53 private static synchronized void initialize(TypeDataBase db) { 54 Type type = db.lookupType("ThreadsList"); 55 lengthField = type.getCIntegerField("_length"); 56 threadsField = type.getAddressField("_threads"); 57 } 58 59 public Address getJavaThreadAddressAt(int i) { 60 Address threadAddr = threadsField.getValue(addr); 61 Address at = threadAddr.getAddressAt(VM.getVM().getAddressSize() * i); 62 return at; 63 } 64 65 public long length() { 66 return lengthField.getValue(addr); 67 } 68 69 public ThreadsList(Address addr) { 70 super(addr); 71 } 72 } 73 74 public class Threads { 75 private static JavaThreadFactory threadFactory; 76 private static AddressField threadListField; 77 private static VirtualConstructor virtualConstructor; 78 private static JavaThreadPDAccess access; 79 private static ThreadsList _list; 80 81 static { 82 VM.registerVMInitializedObserver(new Observer() { 83 public void update(Observable o, Object data) { 84 initialize(VM.getVM().getTypeDataBase()); 85 } 86 }); 87 } 88 89 private static synchronized void initialize(TypeDataBase db) { 90 Type type = db.lookupType("ThreadsSMRSupport"); 91 threadListField = type.getAddressField("_java_thread_list"); 92 93 // Instantiate appropriate platform-specific JavaThreadFactory 94 String os = VM.getVM().getOS(); 95 String cpu = VM.getVM().getCPU(); 96 97 access = null; 98 // FIXME: find the platform specific PD class by reflection? 99 if (os.equals("win32")) { 100 if (cpu.equals("amd64")) { 101 access = new Win32AMD64JavaThreadPDAccess(); 102 } else if (cpu.equals("aarch64")) { 103 access = new Win32AARCH64JavaThreadPDAccess(); 104 } 105 } else if (os.equals("linux")) { 106 if (cpu.equals("x86")) { 107 access = new LinuxX86JavaThreadPDAccess(); 108 } else if (cpu.equals("amd64")) { 109 access = new LinuxAMD64JavaThreadPDAccess(); 110 } else if (cpu.equals("ppc64")) { 111 access = new LinuxPPC64JavaThreadPDAccess(); 112 } else if (cpu.equals("aarch64")) { 113 access = new LinuxAARCH64JavaThreadPDAccess(); 114 } else if (cpu.equals("riscv64")) { 115 access = new LinuxRISCV64JavaThreadPDAccess(); 116 } else { 117 try { 118 access = (JavaThreadPDAccess) 119 Class.forName("sun.jvm.hotspot.runtime.linux_" + 120 cpu.toLowerCase() + ".Linux" + cpu.toUpperCase() + 121 "JavaThreadPDAccess").getDeclaredConstructor().newInstance(); 122 } catch (Exception e) { 123 throw new RuntimeException("OS/CPU combination " + os + "/" + cpu + 124 " not yet supported"); 125 } 126 } 127 } else if (os.equals("bsd")) { 128 if (cpu.equals("x86")) { 129 access = new BsdX86JavaThreadPDAccess(); 130 } else if (cpu.equals("amd64") || cpu.equals("x86_64")) { 131 access = new BsdAMD64JavaThreadPDAccess(); 132 } 133 } else if (os.equals("darwin")) { 134 if (cpu.equals("amd64") || cpu.equals("x86_64")) { 135 access = new BsdAMD64JavaThreadPDAccess(); 136 } else if (cpu.equals("aarch64")) { 137 access = new BsdAARCH64JavaThreadPDAccess(); 138 } 139 } 140 141 if (access == null) { 142 throw new RuntimeException("OS/CPU combination " + os + "/" + cpu + 143 " not yet supported"); 144 } 145 146 virtualConstructor = new VirtualConstructor(db); 147 // Add mappings for all known thread types 148 virtualConstructor.addMapping("JavaThread", JavaThread.class); 149 if (!VM.getVM().isCore()) { 150 virtualConstructor.addMapping("CompilerThread", CompilerThread.class); 151 virtualConstructor.addMapping("TrainingReplayThread", TrainingReplayThread.class); 152 } 153 virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class); 154 virtualConstructor.addMapping("ServiceThread", ServiceThread.class); 155 virtualConstructor.addMapping("MonitorDeflationThread", MonitorDeflationThread.class); 156 virtualConstructor.addMapping("NotificationThread", NotificationThread.class); 157 virtualConstructor.addMapping("StringDedupThread", StringDedupThread.class); 158 virtualConstructor.addMapping("AttachListenerThread", AttachListenerThread.class); 159 } 160 161 public Threads() { 162 _list = VMObjectFactory.newObject(ThreadsList.class, threadListField.getValue()); 163 } 164 165 /** NOTE: this returns objects of type JavaThread, CompilerThread, 166 JvmtiAgentThread, NotificationThread, MonitorDeflationThread, 167 StringDedupThread, AttachListenerThread and ServiceThread. 168 The latter seven subclasses of the former. Most operations 169 (fetching the top frame, etc.) are only allowed to be performed on 170 a "pure" JavaThread. For this reason, {@link 171 sun.jvm.hotspot.runtime.JavaThread#isJavaThread} has been 172 changed from the definition in the VM (which returns true for 173 all of these thread types) to return true for JavaThreads and 174 false for the seven subclasses. FIXME: should reconsider the 175 inheritance hierarchy; see {@link 176 sun.jvm.hotspot.runtime.JavaThread#isJavaThread}. */ 177 public JavaThread getJavaThreadAt(int i) { 178 if (i < _list.length()) { 179 return createJavaThreadWrapper(_list.getJavaThreadAddressAt(i)); 180 } 181 return null; 182 } 183 184 public int getNumberOfThreads() { 185 return (int) _list.length(); 186 } 187 188 /** Routine for instantiating appropriately-typed wrapper for a 189 JavaThread. Currently needs to be public for OopUtilities to 190 access it. */ 191 public JavaThread createJavaThreadWrapper(Address threadAddr) { 192 try { 193 JavaThread thread = (JavaThread)virtualConstructor.instantiateWrapperFor(threadAddr); 194 thread.setThreadPDAccess(access); 195 return thread; 196 } catch (Exception e) { 197 throw new RuntimeException("Unable to deduce type of thread from address " + threadAddr + 198 " (expected type JavaThread, CompilerThread, MonitorDeflationThread, AttachListenerThread," + 199 " StringDedupThread, NotificationThread, ServiceThread or JvmtiAgentThread)", e); 200 } 201 } 202 203 /** Memory operations */ 204 public void oopsDo(AddressVisitor oopVisitor) { 205 // FIXME: add more of VM functionality 206 Threads threads = VM.getVM().getThreads(); 207 for (int i = 0; i < threads.getNumberOfThreads(); i++) { 208 JavaThread thread = threads.getJavaThreadAt(i); 209 thread.oopsDo(oopVisitor); 210 } 211 } 212 213 private JavaThread owningThreadFromMonitor(Address o) { 214 if (o == null) return null; 215 for (int i = 0; i < getNumberOfThreads(); i++) { 216 JavaThread thread = getJavaThreadAt(i); 217 if (o.equals(thread.getLockId())) { 218 return thread; 219 } 220 } 221 return null; 222 } 223 224 public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) { 225 if (monitor.isOwnedAnonymous()) { 226 if (VM.getVM().getCommandLineFlag("LockingMode").getInt() == LockingMode.getLightweight()) { 227 OopHandle object = monitor.object(); 228 for (int i = 0; i < getNumberOfThreads(); i++) { 229 JavaThread thread = getJavaThreadAt(i); 230 if (thread.isLockOwned(object)) { 231 return thread; 232 } 233 } 234 // We should have found the owner, however, as the VM could be in any state, including the middle 235 // of performing GC, it is not always possible to do so. Just return null if we can't locate it. 236 System.out.println("Warning: We failed to find a thread that owns an anonymous lock. This is likely"); 237 System.out.println("due to the JVM currently running a GC. Locking information may not be accurate."); 238 return null; 239 } else { 240 assert(VM.getVM().getCommandLineFlag("LockingMode").getInt() == LockingMode.getLegacy()); 241 Address o = (Address)monitor.stackLocker(); 242 for (int i = 0; i < getNumberOfThreads(); i++) { 243 JavaThread thread = getJavaThreadAt(i); 244 if (thread.isLockOwned(o)) 245 return thread; 246 } 247 return null; 248 } 249 } else { 250 return owningThreadFromMonitor(monitor.owner()); 251 } 252 } 253 254 // refer to Threads::get_pending_threads 255 // Get list of Java threads that are waiting to enter the specified monitor. 256 public List<JavaThread> getPendingThreads(ObjectMonitor monitor) { 257 List<JavaThread> pendingThreads = new ArrayList<>(); 258 for (int i = 0; i < getNumberOfThreads(); i++) { 259 JavaThread thread = getJavaThreadAt(i); 260 if (thread.isCompilerThread() || thread.isCodeCacheSweeperThread()) { 261 continue; 262 } 263 ObjectMonitor pending = thread.getCurrentPendingMonitor(); 264 if (monitor.equals(pending)) { 265 pendingThreads.add(thread); 266 } 267 } 268 return pendingThreads; 269 } 270 271 // Get list of Java threads that have called Object.wait on the specified monitor. 272 public List<JavaThread> getWaitingThreads(ObjectMonitor monitor) { 273 List<JavaThread> pendingThreads = new ArrayList<>(); 274 for (int i = 0; i < getNumberOfThreads(); i++) { 275 JavaThread thread = getJavaThreadAt(i); 276 ObjectMonitor waiting = thread.getCurrentWaitingMonitor(); 277 if (monitor.equals(waiting)) { 278 pendingThreads.add(thread); 279 } 280 } 281 return pendingThreads; 282 } 283 284 // FIXME: add other accessors 285 }