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