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 virtualConstructor.addMapping("TrainingReplayThread", TrainingReplayThread.class); 155 } 156 157 // These are all the visible JavaThread subclasses that execute java code. 158 virtualConstructor.addMapping("JvmtiAgentThread", JavaThread.class); 159 virtualConstructor.addMapping("NotificationThread", JavaThread.class); 160 virtualConstructor.addMapping("AttachListenerThread", JavaThread.class); 161 162 // These are all the hidden JavaThread subclasses that don't execute java code. 163 virtualConstructor.addMapping("StringDedupThread", HiddenJavaThread.class); 164 virtualConstructor.addMapping("ServiceThread", HiddenJavaThread.class); 165 virtualConstructor.addMapping("MonitorDeflationThread", HiddenJavaThread.class); 166 // Only add DeoptimizeObjectsALotThread if it is actually present in the type database. 167 if (db.lookupType("DeoptimizeObjectsALotThread", false) != null) { 168 virtualConstructor.addMapping("DeoptimizeObjectsALotThread", HiddenJavaThread.class); 169 } 170 } 171 172 public Threads() { 173 _list = VMObjectFactory.newObject(ThreadsList.class, threadListField.getValue()); 174 } 175 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 " DeoptimizeObjectsALotThread, 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.getMonitorOwnerID())) { 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.isHiddenFromExternalView()) { 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 }