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 } 152 virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class); 153 virtualConstructor.addMapping("ServiceThread", ServiceThread.class); 154 virtualConstructor.addMapping("MonitorDeflationThread", MonitorDeflationThread.class); 155 virtualConstructor.addMapping("NotificationThread", NotificationThread.class); 156 virtualConstructor.addMapping("StringDedupThread", StringDedupThread.class); 157 virtualConstructor.addMapping("AttachListenerThread", AttachListenerThread.class); 158 } 159 160 public Threads() { 161 _list = VMObjectFactory.newObject(ThreadsList.class, threadListField.getValue()); 162 } 163 164 /** NOTE: this returns objects of type JavaThread, CompilerThread, 165 JvmtiAgentThread, NotificationThread, MonitorDeflationThread, 166 StringDedupThread, AttachListenerThread and ServiceThread. 167 The latter seven subclasses of the former. Most operations 168 (fetching the top frame, etc.) are only allowed to be performed on 169 a "pure" JavaThread. For this reason, {@link 170 sun.jvm.hotspot.runtime.JavaThread#isJavaThread} has been 171 changed from the definition in the VM (which returns true for 172 all of these thread types) to return true for JavaThreads and 173 false for the seven subclasses. FIXME: should reconsider the 174 inheritance hierarchy; see {@link 175 sun.jvm.hotspot.runtime.JavaThread#isJavaThread}. */ 176 public JavaThread getJavaThreadAt(int i) { 177 if (i < _list.length()) { 178 return createJavaThreadWrapper(_list.getJavaThreadAddressAt(i)); 179 } 180 return null; 181 } 182 183 public int getNumberOfThreads() { 184 return (int) _list.length(); 185 } 186 187 /** Routine for instantiating appropriately-typed wrapper for a 188 JavaThread. Currently needs to be public for OopUtilities to 189 access it. */ 190 public JavaThread createJavaThreadWrapper(Address threadAddr) { 191 try { 192 JavaThread thread = (JavaThread)virtualConstructor.instantiateWrapperFor(threadAddr); 193 thread.setThreadPDAccess(access); 194 return thread; 195 } catch (Exception e) { 196 throw new RuntimeException("Unable to deduce type of thread from address " + threadAddr + 197 " (expected type JavaThread, CompilerThread, MonitorDeflationThread, AttachListenerThread," + 198 " StringDedupThread, NotificationThread, ServiceThread or JvmtiAgentThread)", e); 199 } 200 } 201 202 /** Memory operations */ 203 public void oopsDo(AddressVisitor oopVisitor) { 204 // FIXME: add more of VM functionality 205 Threads threads = VM.getVM().getThreads(); 206 for (int i = 0; i < threads.getNumberOfThreads(); i++) { 207 JavaThread thread = threads.getJavaThreadAt(i); 208 thread.oopsDo(oopVisitor); 209 } 210 } 211 212 private JavaThread owningThreadFromMonitor(Address o) { 213 if (o == null) return null; 214 for (int i = 0; i < getNumberOfThreads(); i++) { 215 JavaThread thread = getJavaThreadAt(i); 216 if (o.equals(thread.getLockId())) { 217 return thread; 218 } 219 } 220 return null; 221 } 222 223 public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) { 224 if (monitor.isOwnedAnonymous()) { 225 if (VM.getVM().getCommandLineFlag("LockingMode").getInt() == LockingMode.getLightweight()) { 226 OopHandle object = monitor.object(); 227 for (int i = 0; i < getNumberOfThreads(); i++) { 228 JavaThread thread = getJavaThreadAt(i); 229 if (thread.isLockOwned(object)) { 230 return thread; 231 } 232 } 233 // We should have found the owner, however, as the VM could be in any state, including the middle 234 // of performing GC, it is not always possible to do so. Just return null if we can't locate it. 235 System.out.println("Warning: We failed to find a thread that owns an anonymous lock. This is likely"); 236 System.out.println("due to the JVM currently running a GC. Locking information may not be accurate."); 237 return null; 238 } else { 239 assert(VM.getVM().getCommandLineFlag("LockingMode").getInt() == LockingMode.getLegacy()); 240 Address o = (Address)monitor.stackLocker(); 241 for (int i = 0; i < getNumberOfThreads(); i++) { 242 JavaThread thread = getJavaThreadAt(i); 243 if (thread.isLockOwned(o)) 244 return thread; 245 } 246 return null; 247 } 248 } else { 249 return owningThreadFromMonitor(monitor.owner()); 250 } 251 } 252 253 // refer to Threads::get_pending_threads 254 // Get list of Java threads that are waiting to enter the specified monitor. 255 public List<JavaThread> getPendingThreads(ObjectMonitor monitor) { 256 List<JavaThread> pendingThreads = new ArrayList<>(); 257 for (int i = 0; i < getNumberOfThreads(); i++) { 258 JavaThread thread = getJavaThreadAt(i); 259 if (thread.isCompilerThread() || thread.isCodeCacheSweeperThread()) { 260 continue; 261 } 262 ObjectMonitor pending = thread.getCurrentPendingMonitor(); 263 if (monitor.equals(pending)) { 264 pendingThreads.add(thread); 265 } 266 } 267 return pendingThreads; 268 } 269 270 // Get list of Java threads that have called Object.wait on the specified monitor. 271 public List<JavaThread> getWaitingThreads(ObjectMonitor monitor) { 272 List<JavaThread> pendingThreads = new ArrayList<>(); 273 for (int i = 0; i < getNumberOfThreads(); i++) { 274 JavaThread thread = getJavaThreadAt(i); 275 ObjectMonitor waiting = thread.getCurrentWaitingMonitor(); 276 if (monitor.equals(waiting)) { 277 pendingThreads.add(thread); 278 } 279 } 280 return pendingThreads; 281 } 282 283 // FIXME: add other accessors 284 }