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