1 /*
   2  * Copyright (c) 2000, 2016, 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 
  45 class ThreadsList extends VMObject {
  46 
  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 
  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").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     }
 164 
 165     public Threads() {
 166         _list = VMObjectFactory.newObject(ThreadsList.class, threadListField.getValue());
 167     }
 168 
 169     /** NOTE: this returns objects of type JavaThread, CompilerThread,
 170       JvmtiAgentThread, and ServiceThread.
 171       The latter four are 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 three 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     public interface JavaThreadsDo {
 192         void doJavaThread(JavaThread thread);
 193     }
 194 
 195     public void doJavaThreads(JavaThreadsDo jtDo) {
 196         for (int i = 0; i < _list.length(); i++) {
 197             jtDo.doJavaThread(getJavaThreadAt(i));
 198         }
 199     }
 200 
 201     /** Routine for instantiating appropriately-typed wrapper for a
 202       JavaThread. Currently needs to be public for OopUtilities to
 203       access it. */
 204     public JavaThread createJavaThreadWrapper(Address threadAddr) {
 205         try {
 206             JavaThread thread = (JavaThread)virtualConstructor.instantiateWrapperFor(threadAddr);
 207             thread.setThreadPDAccess(access);
 208             return thread;
 209         } catch (Exception e) {
 210             throw new RuntimeException("Unable to deduce type of thread from address " + threadAddr +
 211             " (expected type JavaThread, CompilerThread, ServiceThread, JvmtiAgentThread or CodeCacheSweeperThread)", e);
 212         }
 213     }
 214 
 215     /** Memory operations */
 216     public void oopsDo(AddressVisitor oopVisitor) {
 217         // FIXME: add more of VM functionality
 218         VM.getVM().getThreads().doJavaThreads((thread) -> thread.oopsDo(oopVisitor));
 219     }
 220 
 221     // refer to Threads::owning_thread_from_monitor_owner
 222     public JavaThread owningThreadFromMonitor(Address o) {
 223         if (o == null) return null;
 224         for (int i = 0; i < getNumberOfThreads(); i++) {
 225             JavaThread thread = getJavaThreadAt(i);
 226             if (o.equals(thread.threadObjectAddress())) {
 227                 return thread;
 228             }
 229         }
 230 
 231         for (int i = 0; i < getNumberOfThreads(); i++) {
 232             JavaThread thread = getJavaThreadAt(i);
 233             if (thread.isLockOwned(o))
 234                 return thread;
 235         }
 236         return null;
 237     }
 238 
 239     public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) {
 240         return owningThreadFromMonitor(monitor.owner());
 241     }
 242 
 243     // refer to Threads::get_pending_threads
 244     // Get list of Java threads that are waiting to enter the specified monitor.
 245     public List getPendingThreads(ObjectMonitor monitor) {
 246         List pendingThreads = new ArrayList();
 247         for (int i = 0; i < getNumberOfThreads(); i++) {
 248             JavaThread thread = getJavaThreadAt(i);
 249             if (thread.isCompilerThread() || thread.isCodeCacheSweeperThread()) {
 250                 continue;
 251             }
 252             ObjectMonitor pending = thread.getCurrentPendingMonitor();
 253             if (monitor.equals(pending)) {
 254                 pendingThreads.add(thread);
 255             }
 256         }
 257         return pendingThreads;
 258     }
 259 
 260     // Get list of Java threads that have called Object.wait on the specified monitor.
 261     public List getWaitingThreads(ObjectMonitor monitor) {
 262         List pendingThreads = new ArrayList();
 263         for (int i = 0; i < getNumberOfThreads(); i++) {
 264             JavaThread thread = getJavaThreadAt(i);
 265             ObjectMonitor waiting = thread.getCurrentWaitingMonitor();
 266             if (monitor.equals(waiting)) {
 267                 pendingThreads.add(thread);
 268             }
 269         }
 270         return pendingThreads;
 271     }
 272 
 273     // FIXME: add other accessors
 274 }