1 /* 2 * Copyright (c) 2000, 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. 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 AddressField threadListField; 76 private static VirtualConstructor virtualConstructor; 77 private static JavaThreadPDAccess access; 78 private static ThreadsList _list; 79 80 static { 81 VM.registerVMInitializedObserver(new Observer() { 82 public void update(Observable o, Object data) { 83 initialize(VM.getVM().getTypeDataBase()); 84 } 85 }); 86 } 87 88 private static synchronized void initialize(TypeDataBase db) { 89 Type type = db.lookupType("ThreadsSMRSupport"); 90 threadListField = type.getAddressField("_java_thread_list"); 91 92 String os = VM.getVM().getOS(); 93 String cpu = VM.getVM().getCPU(); 94 95 access = null; 96 // FIXME: find the platform specific PD class by reflection? 97 if (os.equals("win32")) { 98 if (cpu.equals("amd64")) { 99 access = new Win32AMD64JavaThreadPDAccess(); 100 } else if (cpu.equals("aarch64")) { 101 access = new Win32AARCH64JavaThreadPDAccess(); 102 } 103 } else if (os.equals("linux")) { 104 if (cpu.equals("x86")) { 105 access = new LinuxX86JavaThreadPDAccess(); 106 } else if (cpu.equals("amd64")) { 107 access = new LinuxAMD64JavaThreadPDAccess(); 108 } else if (cpu.equals("ppc64")) { 109 access = new LinuxPPC64JavaThreadPDAccess(); 110 } else if (cpu.equals("aarch64")) { 111 access = new LinuxAARCH64JavaThreadPDAccess(); 112 } else if (cpu.equals("riscv64")) { 113 access = new LinuxRISCV64JavaThreadPDAccess(); 114 } else { 115 try { 116 access = (JavaThreadPDAccess) 117 Class.forName("sun.jvm.hotspot.runtime.linux_" + 118 cpu.toLowerCase() + ".Linux" + cpu.toUpperCase() + 119 "JavaThreadPDAccess").getDeclaredConstructor().newInstance(); 120 } catch (Exception e) { 121 throw new RuntimeException("OS/CPU combination " + os + "/" + cpu + 122 " not yet supported"); 123 } 124 } 125 } else if (os.equals("bsd")) { 126 if (cpu.equals("x86")) { 127 access = new BsdX86JavaThreadPDAccess(); 128 } else if (cpu.equals("amd64") || cpu.equals("x86_64")) { 129 access = new BsdAMD64JavaThreadPDAccess(); 130 } 131 } else if (os.equals("darwin")) { 132 if (cpu.equals("amd64") || cpu.equals("x86_64")) { 133 access = new BsdAMD64JavaThreadPDAccess(); 134 } else if (cpu.equals("aarch64")) { 135 access = new BsdAARCH64JavaThreadPDAccess(); 136 } 137 } 138 139 if (access == null) { 140 throw new RuntimeException("OS/CPU combination " + os + "/" + cpu + 141 " not yet supported"); 142 } 143 144 virtualConstructor = new VirtualConstructor(db); 145 146 /* 147 * Add mappings for JavaThread types 148 */ 149 150 virtualConstructor.addMapping("JavaThread", JavaThread.class); 151 152 if (!VM.getVM().isCore()) { 153 virtualConstructor.addMapping("CompilerThread", CompilerThread.class); 154 } 155 156 // These are all the visible JavaThread subclasses that execute java code. 157 virtualConstructor.addMapping("JvmtiAgentThread", JavaThread.class); 158 virtualConstructor.addMapping("NotificationThread", JavaThread.class); 159 virtualConstructor.addMapping("AttachListenerThread", JavaThread.class); 160 161 // These are all the hidden JavaThread subclasses that don't execute java code. 162 virtualConstructor.addMapping("StringDedupThread", HiddenJavaThread.class); 163 virtualConstructor.addMapping("ServiceThread", HiddenJavaThread.class); 164 virtualConstructor.addMapping("MonitorDeflationThread", HiddenJavaThread.class); 165 // Only add DeoptimizeObjectsALotThread if it is actually present in the type database. 166 if (db.lookupType("DeoptimizeObjectsALotThread", false) != null) { 167 virtualConstructor.addMapping("DeoptimizeObjectsALotThread", HiddenJavaThread.class); 168 } 169 } 170 171 public Threads() { 172 _list = VMObjectFactory.newObject(ThreadsList.class, threadListField.getValue()); 173 } 174 175 public JavaThread getJavaThreadAt(int i) { 176 if (i < _list.length()) { 177 return createJavaThreadWrapper(_list.getJavaThreadAddressAt(i)); 178 } 179 return null; 180 } 181 182 public int getNumberOfThreads() { 183 return (int) _list.length(); 184 } 185 186 /** Routine for instantiating appropriately-typed wrapper for a 187 JavaThread. Currently needs to be public for OopUtilities to 188 access it. */ 189 public JavaThread createJavaThreadWrapper(Address threadAddr) { 190 try { 191 JavaThread thread = (JavaThread)virtualConstructor.instantiateWrapperFor(threadAddr); 192 thread.setThreadPDAccess(access); 193 return thread; 194 } catch (Exception e) { 195 throw new RuntimeException("Unable to deduce type of thread from address " + threadAddr + 196 " (expected type JavaThread, CompilerThread, MonitorDeflationThread, AttachListenerThread," + 197 " DeoptimizeObjectsALotThread, StringDedupThread, NotificationThread, ServiceThread or JvmtiAgentThread)", e); 198 } 199 } 200 201 /** Memory operations */ 202 public void oopsDo(AddressVisitor oopVisitor) { 203 // FIXME: add more of VM functionality 204 Threads threads = VM.getVM().getThreads(); 205 for (int i = 0; i < threads.getNumberOfThreads(); i++) { 206 JavaThread thread = threads.getJavaThreadAt(i); 207 thread.oopsDo(oopVisitor); 208 } 209 } 210 211 private JavaThread owningThreadFromMonitor(Address o) { 212 if (o == null) return null; 213 for (int i = 0; i < getNumberOfThreads(); i++) { 214 JavaThread thread = getJavaThreadAt(i); 215 if (o.equals(thread.getMonitorOwnerID())) { 216 return thread; 217 } 218 } 219 return null; 220 } 221 222 public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) { 223 if (monitor.isOwnedAnonymous()) { 224 if (VM.getVM().getCommandLineFlag("LockingMode").getInt() == LockingMode.getLightweight()) { 225 OopHandle object = monitor.object(); 226 for (int i = 0; i < getNumberOfThreads(); i++) { 227 JavaThread thread = getJavaThreadAt(i); 228 if (thread.isLockOwned(object)) { 229 return thread; 230 } 231 } 232 // We should have found the owner, however, as the VM could be in any state, including the middle 233 // of performing GC, it is not always possible to do so. Just return null if we can't locate it. 234 System.out.println("Warning: We failed to find a thread that owns an anonymous lock. This is likely"); 235 System.out.println("due to the JVM currently running a GC. Locking information may not be accurate."); 236 return null; 237 } else { 238 assert(VM.getVM().getCommandLineFlag("LockingMode").getInt() == LockingMode.getLegacy()); 239 Address o = (Address)monitor.stackLocker(); 240 for (int i = 0; i < getNumberOfThreads(); i++) { 241 JavaThread thread = getJavaThreadAt(i); 242 if (thread.isLockOwned(o)) 243 return thread; 244 } 245 return null; 246 } 247 } else { 248 return owningThreadFromMonitor(monitor.owner()); 249 } 250 } 251 252 // refer to Threads::get_pending_threads 253 // Get list of Java threads that are waiting to enter the specified monitor. 254 public List<JavaThread> getPendingThreads(ObjectMonitor monitor) { 255 List<JavaThread> pendingThreads = new ArrayList<>(); 256 for (int i = 0; i < getNumberOfThreads(); i++) { 257 JavaThread thread = getJavaThreadAt(i); 258 if (thread.isHiddenFromExternalView()) { 259 continue; 260 } 261 ObjectMonitor pending = thread.getCurrentPendingMonitor(); 262 if (monitor.equals(pending)) { 263 pendingThreads.add(thread); 264 } 265 } 266 return pendingThreads; 267 } 268 269 // Get list of Java threads that have called Object.wait on the specified monitor. 270 public List<JavaThread> getWaitingThreads(ObjectMonitor monitor) { 271 List<JavaThread> pendingThreads = new ArrayList<>(); 272 for (int i = 0; i < getNumberOfThreads(); i++) { 273 JavaThread thread = getJavaThreadAt(i); 274 ObjectMonitor waiting = thread.getCurrentWaitingMonitor(); 275 if (monitor.equals(waiting)) { 276 pendingThreads.add(thread); 277 } 278 } 279 return pendingThreads; 280 } 281 282 // FIXME: add other accessors 283 }