1 /*
   2  * Copyright (c) 2000, 2020, 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.solaris_sparc.SolarisSPARCJavaThreadPDAccess;
  32 import sun.jvm.hotspot.runtime.solaris_x86.SolarisX86JavaThreadPDAccess;
  33 import sun.jvm.hotspot.runtime.solaris_amd64.SolarisAMD64JavaThreadPDAccess;
  34 import sun.jvm.hotspot.runtime.win32_amd64.Win32AMD64JavaThreadPDAccess;
  35 import sun.jvm.hotspot.runtime.win32_x86.Win32X86JavaThreadPDAccess;
  36 import sun.jvm.hotspot.runtime.linux_x86.LinuxX86JavaThreadPDAccess;
  37 import sun.jvm.hotspot.runtime.linux_amd64.LinuxAMD64JavaThreadPDAccess;
  38 import sun.jvm.hotspot.runtime.linux_aarch64.LinuxAARCH64JavaThreadPDAccess;
  39 import sun.jvm.hotspot.runtime.linux_ppc64.LinuxPPC64JavaThreadPDAccess;
  40 import sun.jvm.hotspot.runtime.linux_sparc.LinuxSPARCJavaThreadPDAccess;
  41 import sun.jvm.hotspot.runtime.bsd_x86.BsdX86JavaThreadPDAccess;
  42 import sun.jvm.hotspot.runtime.bsd_amd64.BsdAMD64JavaThreadPDAccess;
  43 import sun.jvm.hotspot.utilities.*;
  44 import sun.jvm.hotspot.utilities.Observable;
  45 import sun.jvm.hotspot.utilities.Observer;
  46 
  47 class ThreadsList extends VMObject {
  48     private static AddressField  threadsField;
  49     private static CIntegerField lengthField;
  50 
  51     static {
  52         VM.registerVMInitializedObserver((o, d) -> initialize(VM.getVM().getTypeDataBase()));
  53     }
  54 
  55     private static synchronized void initialize(TypeDataBase db) {
  56         Type type = db.lookupType("ThreadsList");
  57         lengthField = type.getCIntegerField("_length");
  58         threadsField = type.getAddressField("_threads");
  59     }
  60 
  61     public Address getJavaThreadAddressAt(int i) {
  62       Address threadAddr = threadsField.getValue(addr);
  63       Address at = threadAddr.getAddressAt(VM.getVM().getAddressSize() * i);
  64       return at;
  65     }
  66 
  67     public long length() {
  68         return lengthField.getValue(addr);
  69     }
  70 
  71     public ThreadsList(Address addr) {
  72         super(addr);
  73     }
  74 }
  75 
  76 public class Threads {
  77     private static JavaThreadFactory threadFactory;
  78     private static AddressField      threadListField;
  79     private static VirtualConstructor virtualConstructor;
  80     private static JavaThreadPDAccess access;
  81     private static ThreadsList _list;
  82 
  83     static {
  84         VM.registerVMInitializedObserver(new Observer() {
  85             public void update(Observable o, Object data) {
  86                 initialize(VM.getVM().getTypeDataBase());
  87             }
  88         });
  89     }
  90 
  91     private static synchronized void initialize(TypeDataBase db) {
  92         Type type = db.lookupType("ThreadsSMRSupport");
  93         threadListField = type.getAddressField("_java_thread_list");
  94 
  95         // Instantiate appropriate platform-specific JavaThreadFactory
  96         String os  = VM.getVM().getOS();
  97         String cpu = VM.getVM().getCPU();
  98 
  99         access = null;
 100         // FIXME: find the platform specific PD class by reflection?
 101         if (os.equals("solaris")) {
 102             if (cpu.equals("sparc")) {
 103                 access = new SolarisSPARCJavaThreadPDAccess();
 104             } else if (cpu.equals("x86")) {
 105                 access = new SolarisX86JavaThreadPDAccess();
 106             } else if (cpu.equals("amd64")) {
 107                 access = new SolarisAMD64JavaThreadPDAccess();
 108             }
 109         } else if (os.equals("win32")) {
 110             if (cpu.equals("x86")) {
 111                 access =  new Win32X86JavaThreadPDAccess();
 112             } else if (cpu.equals("amd64")) {
 113                 access =  new Win32AMD64JavaThreadPDAccess();
 114             }
 115         } else if (os.equals("linux")) {
 116             if (cpu.equals("x86")) {
 117                 access = new LinuxX86JavaThreadPDAccess();
 118             } else if (cpu.equals("amd64")) {
 119                 access = new LinuxAMD64JavaThreadPDAccess();
 120             } else if (cpu.equals("sparc")) {
 121                 access = new LinuxSPARCJavaThreadPDAccess();
 122             } else if (cpu.equals("ppc64")) {
 123                 access = new LinuxPPC64JavaThreadPDAccess();
 124             } else if (cpu.equals("aarch64")) {
 125                 access = new LinuxAARCH64JavaThreadPDAccess();
 126             } else {
 127               try {
 128                 access = (JavaThreadPDAccess)
 129                   Class.forName("sun.jvm.hotspot.runtime.linux_" +
 130                      cpu.toLowerCase() + ".Linux" + cpu.toUpperCase() +
 131                      "JavaThreadPDAccess").getDeclaredConstructor().newInstance();
 132               } catch (Exception e) {
 133                 throw new RuntimeException("OS/CPU combination " + os + "/" + cpu +
 134                                            " not yet supported");
 135               }
 136             }
 137         } else if (os.equals("bsd")) {
 138             if (cpu.equals("x86")) {
 139                 access = new BsdX86JavaThreadPDAccess();
 140             } else if (cpu.equals("amd64") || cpu.equals("x86_64")) {
 141                 access = new BsdAMD64JavaThreadPDAccess();
 142             }
 143         } else if (os.equals("darwin")) {
 144             if (cpu.equals("amd64") || cpu.equals("x86_64")) {
 145                 access = new BsdAMD64JavaThreadPDAccess();
 146             }
 147         }
 148 
 149         if (access == null) {
 150             throw new RuntimeException("OS/CPU combination " + os + "/" + cpu +
 151             " not yet supported");
 152         }
 153 
 154         virtualConstructor = new VirtualConstructor(db);
 155         // Add mappings for all known thread types
 156         virtualConstructor.addMapping("JavaThread", JavaThread.class);
 157         if (!VM.getVM().isCore()) {
 158             virtualConstructor.addMapping("CompilerThread", CompilerThread.class);
 159             virtualConstructor.addMapping("CodeCacheSweeperThread", CodeCacheSweeperThread.class);
 160         }
 161         virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class);
 162         virtualConstructor.addMapping("ServiceThread", ServiceThread.class);
 163         virtualConstructor.addMapping("NotificationThread", NotificationThread.class);
 164     }
 165 
 166     public Threads() {
 167         _list = VMObjectFactory.newObject(ThreadsList.class, threadListField.getValue());
 168     }
 169 
 170     /** NOTE: this returns objects of type JavaThread, CompilerThread,
 171       JvmtiAgentThread, NotificationThread, and ServiceThread.
 172       The latter four are subclasses of the former. Most operations
 173       (fetching the top frame, etc.) are only allowed to be performed on
 174       a "pure" JavaThread. For this reason, {@link
 175       sun.jvm.hotspot.runtime.JavaThread#isJavaThread} has been
 176       changed from the definition in the VM (which returns true for
 177       all of these thread types) to return true for JavaThreads and
 178       false for the four subclasses. FIXME: should reconsider the
 179       inheritance hierarchy; see {@link
 180       sun.jvm.hotspot.runtime.JavaThread#isJavaThread}. */
 181     public JavaThread getJavaThreadAt(int i) {
 182         if (i < _list.length()) {
 183             return createJavaThreadWrapper(_list.getJavaThreadAddressAt(i));
 184         }
 185         return null;
 186     }
 187 
 188     public int getNumberOfThreads() {
 189         return (int) _list.length();
 190     }
 191 
 192     /** Routine for instantiating appropriately-typed wrapper for a
 193       JavaThread. Currently needs to be public for OopUtilities to
 194       access it. */
 195     public JavaThread createJavaThreadWrapper(Address threadAddr) {
 196         try {
 197             JavaThread thread = (JavaThread)virtualConstructor.instantiateWrapperFor(threadAddr);
 198             thread.setThreadPDAccess(access);
 199             return thread;
 200         } catch (Exception e) {
 201             throw new RuntimeException("Unable to deduce type of thread from address " + threadAddr +
 202             " (expected type JavaThread, CompilerThread, ServiceThread, JvmtiAgentThread or CodeCacheSweeperThread)", 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         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         return owningThreadFromMonitor(monitor.owner());
 236     }
 237 
 238     // refer to Threads::get_pending_threads
 239     // Get list of Java threads that are waiting to enter the specified monitor.
 240     public List<JavaThread> getPendingThreads(ObjectMonitor monitor) {
 241         List<JavaThread> pendingThreads = new ArrayList<>();
 242         for (int i = 0; i < getNumberOfThreads(); i++) {
 243             JavaThread thread = getJavaThreadAt(i);
 244             if (thread.isCompilerThread() || thread.isCodeCacheSweeperThread()) {
 245                 continue;
 246             }
 247             ObjectMonitor pending = thread.getCurrentPendingMonitor();
 248             if (monitor.equals(pending)) {
 249                 pendingThreads.add(thread);
 250             }
 251         }
 252         return pendingThreads;
 253     }
 254 
 255     // Get list of Java threads that have called Object.wait on the specified monitor.
 256     public List<JavaThread> getWaitingThreads(ObjectMonitor monitor) {
 257         List<JavaThread> pendingThreads = new ArrayList<>();
 258         for (int i = 0; i < getNumberOfThreads(); i++) {
 259             JavaThread thread = getJavaThreadAt(i);
 260             ObjectMonitor waiting = thread.getCurrentWaitingMonitor();
 261             if (monitor.equals(waiting)) {
 262                 pendingThreads.add(thread);
 263             }
 264         }
 265         return pendingThreads;
 266     }
 267 
 268     // FIXME: add other accessors
 269 }