1 /*
   2  * Copyright (c) 2000, 2021, 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.io.*;
  28 import java.net.*;
  29 import java.util.*;
  30 import java.util.regex.*;
  31 import sun.jvm.hotspot.code.*;
  32 import sun.jvm.hotspot.c1.*;
  33 import sun.jvm.hotspot.code.*;
  34 import sun.jvm.hotspot.debugger.*;
  35 import sun.jvm.hotspot.interpreter.*;
  36 import sun.jvm.hotspot.memory.*;
  37 import sun.jvm.hotspot.oops.*;
  38 import sun.jvm.hotspot.types.*;
  39 import sun.jvm.hotspot.utilities.*;
  40 import sun.jvm.hotspot.runtime.*;
  41 import sun.jvm.hotspot.classfile.*;
  42 import sun.jvm.hotspot.utilities.Observable;
  43 import sun.jvm.hotspot.utilities.Observer;
  44 
  45 /** <P> This class encapsulates the global state of the VM; the
  46     universe, object heap, interpreter, etc. It is a Singleton and
  47     must be initialized with a call to initialize() before calling
  48     getVM(). </P>
  49 
  50     <P> Many auxiliary classes (i.e., most of the VMObjects) keep
  51     needed field offsets in the form of static Field objects. In a
  52     debugging system, the VM might be shutdown and re-initialized (on
  53     a differently-configured build, i.e., 32- vs. 64-bit), and all old
  54     cached state (including fields and field offsets) must be
  55     flushed. </P>
  56 
  57     <P> An Observer pattern is used to implement the initialization of
  58     such classes. Each such class, in its static initializer,
  59     registers an Observer with the VM class via
  60     VM.registerVMInitializedObserver(). This Observer is guaranteed to
  61     be notified whenever the VM is initialized (or re-initialized). To
  62     implement the first-time initialization, the observer is also
  63     notified when it registers itself with the VM. (For bootstrapping
  64     reasons, this implies that the constructor of VM can not
  65     instantiate any such objects, since VM.soleInstance will not have
  66     been set yet. This is a bootstrapping issue which may have to be
  67     revisited later.) </P>
  68 */
  69 
  70 public class VM {
  71   private static VM    soleInstance;
  72   private static List<Observer> vmInitializedObservers = new ArrayList<>();
  73   private List<Observer> vmResumedObservers   = new ArrayList<>();
  74   private List<Observer> vmSuspendedObservers = new ArrayList<>();
  75   private TypeDataBase db;
  76   private boolean      isBigEndian;
  77   /** This is only present if in a debugging system */
  78   private JVMDebugger  debugger;
  79   private long         logAddressSize;
  80   private Universe     universe;
  81   private ObjectHeap   heap;
  82   private SystemDictionary dict;
  83   private ClassLoaderDataGraph cldGraph;
  84   private Threads      threads;
  85   private ObjectSynchronizer synchronizer;
  86   private JNIHandles   handles;
  87   private Interpreter  interpreter;
  88   private StubRoutines stubRoutines;
  89   private FileMapInfo  fileMapInfo;
  90   private Bytes        bytes;
  91 
  92   /** Flag indicating if JVMTI support is included in the build */
  93   private boolean      isJvmtiSupported;
  94   /** Flags indicating whether we are attached to a core, C1, or C2 build */
  95   private boolean      usingClientCompiler;
  96   private boolean      usingServerCompiler;
  97   /** alignment constants */
  98   private boolean      isLP64;
  99   private int          bytesPerLong;
 100   private int          bytesPerWord;
 101   private int          objectAlignmentInBytes;
 102   private int          minObjAlignmentInBytes;
 103   private int          logMinObjAlignmentInBytes;
 104   private int          heapWordSize;
 105   private int          heapOopSize;
 106   private int          klassPtrSize;
 107   private int          oopSize;
 108   /** -XX flags (value origin) */
 109   public static int    Flags_DEFAULT;
 110   public static int    Flags_COMMAND_LINE;
 111   public static int    Flags_ENVIRON_VAR;
 112   public static int    Flags_CONFIG_FILE;
 113   public static int    Flags_MANAGEMENT;
 114   public static int    Flags_ERGONOMIC;
 115   public static int    Flags_ATTACH_ON_DEMAND;
 116   public static int    Flags_INTERNAL;
 117   public static int    Flags_JIMAGE_RESOURCE;
 118   private static int   Flags_VALUE_ORIGIN_MASK;
 119   private static int   Flags_WAS_SET_ON_COMMAND_LINE;
 120   /** This is only present in a non-core build */
 121   private CodeCache    codeCache;
 122   /** This is only present in a C1 build */
 123   private Runtime1     runtime1;
 124   /** These constants come from globalDefinitions.hpp */
 125   private int          invocationEntryBCI;
 126   private ReversePtrs  revPtrs;
 127   private VMRegImpl    vmregImpl;
 128   private int          reserveForAllocationPrefetch;
 129 
 130   // System.getProperties from debuggee VM
 131   private Properties   sysProps;
 132 
 133   // VM version strings come from Abstract_VM_Version class
 134   private String       vmRelease;
 135   private String       vmInternalInfo;
 136 
 137   private Flag[] commandLineFlags;
 138   private Map<String, Flag> flagsMap;
 139 
 140   private static Type intType;
 141   private static Type uintType;
 142   private static Type intxType;
 143   private static Type uintxType;
 144   private static Type sizetType;
 145   private static Type uint64tType;
 146   private static CIntegerType boolType;
 147   private Boolean sharingEnabled;
 148   private Boolean compressedOopsEnabled;
 149   private Boolean compressedKlassPointersEnabled;
 150   private Boolean compactObjectHeadersEnabled;
 151 
 152   // command line flags supplied to VM - see struct JVMFlag in jvmFlag.hpp
 153   public static final class Flag {
 154      private String type;
 155      private String name;
 156      private Address addr;
 157      private int flags;
 158 
 159      private Flag(String type, String name, Address addr, int flags) {
 160         this.type = type;
 161         this.name = name;
 162         this.addr = addr;
 163         this.flags = flags;
 164      }
 165 
 166      public String getType() {
 167         return type;
 168      }
 169 
 170      public String getName() {
 171         return name;
 172      }
 173 
 174      public Address getAddress() {
 175         return addr;
 176      }
 177 
 178      public int getOrigin() {
 179         return flags & Flags_VALUE_ORIGIN_MASK;
 180      }
 181 
 182      // See JVMFlag::print_origin() in HotSpot
 183      public String getOriginString() {
 184         var origin = flags & Flags_VALUE_ORIGIN_MASK;
 185         if (origin == Flags_DEFAULT) {
 186             return "default";
 187         } else if (origin == Flags_COMMAND_LINE) {
 188             return "command line";
 189         } else if (origin == Flags_ENVIRON_VAR) {
 190             return "environment";
 191         } else if (origin == Flags_CONFIG_FILE) {
 192             return "config file";
 193         } else if (origin == Flags_MANAGEMENT) {
 194             return "management";
 195         } else if (origin == Flags_ERGONOMIC) {
 196             String result = "";
 197             if ((flags & Flags_WAS_SET_ON_COMMAND_LINE) == Flags_WAS_SET_ON_COMMAND_LINE) {
 198                 result = "command line, ";
 199             }
 200             return result + "ergonomic";
 201         } else if (origin == Flags_ATTACH_ON_DEMAND) {
 202             return "attach";
 203         } else if (origin == Flags_INTERNAL) {
 204             return "internal";
 205         } else if (origin == Flags_JIMAGE_RESOURCE) {
 206             return "jimage";
 207         } else {
 208             throw new IllegalStateException(
 209                 "Unknown flag origin " + origin + " is detected in " + name);
 210         }
 211      }
 212 
 213      public boolean isBool() {
 214         return type.equals("bool");
 215      }
 216 
 217      public boolean getBool() {
 218         if (Assert.ASSERTS_ENABLED) {
 219            Assert.that(isBool(), "not a bool flag!");
 220         }
 221         return addr.getCIntegerAt(0, boolType.getSize(), boolType.isUnsigned()) != 0;
 222      }
 223 
 224      public boolean isInt() {
 225         return type.equals("int");
 226      }
 227 
 228      public long getInt() {
 229         if (Assert.ASSERTS_ENABLED) {
 230            Assert.that(isInt(), "not an int flag!");
 231         }
 232         return addr.getCIntegerAt(0, intType.getSize(), false);
 233      }
 234 
 235      public boolean isUInt() {
 236         return type.equals("uint");
 237      }
 238 
 239      public long getUInt() {
 240         if (Assert.ASSERTS_ENABLED) {
 241            Assert.that(isUInt(), "not a uint flag!");
 242         }
 243         return addr.getCIntegerAt(0, uintType.getSize(), false);
 244      }
 245 
 246      public boolean isIntx() {
 247         return type.equals("intx");
 248      }
 249 
 250      public long getIntx() {
 251         if (Assert.ASSERTS_ENABLED) {
 252            Assert.that(isIntx(), "not an intx flag!");
 253         }
 254         return addr.getCIntegerAt(0, intxType.getSize(), false);
 255      }
 256 
 257      public boolean isUIntx() {
 258         return type.equals("uintx");
 259      }
 260 
 261      public long getUIntx() {
 262         if (Assert.ASSERTS_ENABLED) {
 263            Assert.that(isUIntx(), "not a uintx flag!");
 264         }
 265         return addr.getCIntegerAt(0, uintxType.getSize(), true);
 266      }
 267 
 268      public boolean isSizet() {
 269         return type.equals("size_t");
 270      }
 271 
 272      public long getSizet() {
 273         if (Assert.ASSERTS_ENABLED) {
 274            Assert.that(isSizet(), "not a size_t flag!");
 275         }
 276         return addr.getCIntegerAt(0, sizetType.getSize(), true);
 277      }
 278 
 279      public boolean isCcstr() {
 280         return type.equals("ccstr");
 281      }
 282 
 283      public String getCcstr() {
 284         if (Assert.ASSERTS_ENABLED) {
 285            Assert.that(isCcstr(), "not a ccstr flag!");
 286         }
 287         return CStringUtilities.getString(addr.getAddressAt(0));
 288      }
 289 
 290      public boolean isCcstrlist() {
 291         return type.equals("ccstrlist");
 292      }
 293 
 294      public String getCcstrlist() {
 295         if (Assert.ASSERTS_ENABLED) {
 296            Assert.that(isCcstrlist(), "not a ccstrlist flag!");
 297         }
 298         return CStringUtilities.getString(addr.getAddressAt(0));
 299      }
 300 
 301      public boolean isDouble() {
 302         return type.equals("double");
 303      }
 304 
 305      public double getDouble() {
 306         if (Assert.ASSERTS_ENABLED) {
 307            Assert.that(isDouble(), "not a double flag!");
 308         }
 309         return addr.getJDoubleAt(0);
 310      }
 311 
 312      public boolean isUint64t() {
 313         return type.equals("uint64_t");
 314      }
 315 
 316      public long getUint64t() {
 317         if (Assert.ASSERTS_ENABLED) {
 318            Assert.that(isUint64t(), "not an uint64_t flag!");
 319         }
 320         return addr.getCIntegerAt(0, uint64tType.getSize(), true);
 321      }
 322 
 323      public String getValue() {
 324         if (isBool()) {
 325            return Boolean.toString(getBool());
 326         } else if (isInt()) {
 327            return Long.toString(getInt());
 328         } else if (isUInt()) {
 329            return Long.toString(getUInt());
 330         } else if (isIntx()) {
 331            return Long.toString(getIntx());
 332         } else if (isUIntx()) {
 333            return Long.toUnsignedString(getUIntx());
 334         } else if (isSizet()) {
 335            return Long.toUnsignedString(getSizet());
 336         } else if (isCcstr()) {
 337            var str = getCcstr();
 338            if (str != null) {
 339                str = "\"" + str + "\"";
 340            }
 341            return str;
 342         } else if (isCcstrlist()) {
 343            var str = getCcstrlist();
 344            if (str != null) {
 345                str = "\"" + str + "\"";
 346            }
 347            return str;
 348         } else if (isDouble()) {
 349            return Double.toString(getDouble());
 350         } else if (isUint64t()) {
 351            return Long.toUnsignedString(getUint64t());
 352         } else {
 353            throw new WrongTypeException("Unknown type: " + type + " (" + name + ")");
 354         }
 355      }
 356   };
 357 
 358   private static void checkVMVersion(String vmRelease) {
 359      if (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null) {
 360         // read sa build version.
 361         String versionProp = "sun.jvm.hotspot.runtime.VM.saBuildVersion";
 362         String saVersion = saProps.getProperty(versionProp);
 363         if (saVersion == null)
 364            throw new RuntimeException("Missing property " + versionProp);
 365 
 366         // Strip nonproduct VM version substring (note: saVersion doesn't have it).
 367         String vmVersion = vmRelease.replaceAll("(-fastdebug)|(-debug)|(-jvmg)|(-optimized)|(-profiled)","");
 368 
 369         if (saVersion.equals(vmVersion)) {
 370            // Exact match
 371            return;
 372         }
 373         if (saVersion.indexOf('-') == saVersion.lastIndexOf('-') &&
 374             vmVersion.indexOf('-') == vmVersion.lastIndexOf('-')) {
 375            // Throw exception if different release versions:
 376            // <major>.<minor>-b<n>
 377            throw new VMVersionMismatchException(saVersion, vmRelease);
 378         } else {
 379            // Otherwise print warning to allow mismatch not release versions
 380            // during development.
 381            System.err.println("WARNING: Hotspot VM version " + vmRelease +
 382                               " does not match with SA version " + saVersion +
 383                               "." + " You may see unexpected results. ");
 384         }
 385      } else {
 386         System.err.println("WARNING: You have disabled SA and VM version check. You may be "  +
 387                            "using incompatible version of SA and you may see unexpected " +
 388                            "results.");
 389      }
 390   }
 391 
 392   private static final boolean disableDerivedPointerTableCheck;
 393   private static final Properties saProps;
 394 
 395   static {
 396      saProps = new Properties();
 397      URL url = null;
 398      try {
 399        saProps.load(VM.class.getResourceAsStream("/sa.properties"));
 400      } catch (Exception e) {
 401        System.err.println("Unable to load properties  " +
 402                                   (url == null ? "null" : url.toString()) +
 403                                   ": " + e.getMessage());
 404      }
 405 
 406      disableDerivedPointerTableCheck = System.getProperty("sun.jvm.hotspot.runtime.VM.disableDerivedPointerTableCheck") != null;
 407   }
 408 
 409   private VM(TypeDataBase db, JVMDebugger debugger, boolean isBigEndian) {
 410     this.db          = db;
 411     this.debugger    = debugger;
 412     this.isBigEndian = isBigEndian;
 413 
 414     // Note that we don't construct universe, heap, threads,
 415     // interpreter, or stubRoutines here (any more).  The current
 416     // initialization mechanisms require that the VM be completely set
 417     // up (i.e., out of its constructor, with soleInstance assigned)
 418     // before their static initializers are run.
 419 
 420     if (db.getAddressSize() == 4) {
 421       logAddressSize = 2;
 422     } else if (db.getAddressSize() == 8) {
 423       logAddressSize = 3;
 424     } else {
 425       throw new RuntimeException("Address size " + db.getAddressSize() + " not yet supported");
 426     }
 427 
 428     // read VM version info
 429     try {
 430        Type vmVersion = db.lookupType("Abstract_VM_Version");
 431        Address releaseAddr = vmVersion.getAddressField("_s_vm_release").getValue();
 432        vmRelease = CStringUtilities.getString(releaseAddr);
 433        Address vmInternalInfoAddr = vmVersion.getAddressField("_s_internal_vm_info_string").getValue();
 434        vmInternalInfo = CStringUtilities.getString(vmInternalInfoAddr);
 435 
 436        Type threadLocalAllocBuffer = db.lookupType("ThreadLocalAllocBuffer");
 437        CIntegerType intType = (CIntegerType) db.lookupType("int");
 438        CIntegerField reserveForAllocationPrefetchField = threadLocalAllocBuffer.getCIntegerField("_reserve_for_allocation_prefetch");
 439        reserveForAllocationPrefetch = (int)reserveForAllocationPrefetchField.getCInteger(intType);
 440     } catch (Exception exp) {
 441        throw new RuntimeException("can't determine target's VM version : " + exp.getMessage());
 442     }
 443 
 444     checkVMVersion(vmRelease);
 445 
 446     invocationEntryBCI = db.lookupIntConstant("InvocationEntryBci").intValue();
 447 
 448     // We infer the presence of JVMTI from the presence of the InstanceKlass::_breakpoints field.
 449     {
 450       Type type = db.lookupType("InstanceKlass");
 451       if (type.getField("_breakpoints", false, false) == null) {
 452         isJvmtiSupported = false;
 453       } else {
 454         isJvmtiSupported = true;
 455       }
 456     }
 457 
 458     // We infer the presence of C1 or C2 from a couple of fields we
 459     // already have present in the type database
 460     {
 461       Type type = db.lookupType("Method");
 462       if (type.getField("_from_compiled_entry", false, false) == null) {
 463         // Neither C1 nor C2 is present
 464         usingClientCompiler = false;
 465         usingServerCompiler = false;
 466       } else {
 467         // Determine whether C2 is present
 468         if (db.lookupType("Matcher", false) != null) {
 469           usingServerCompiler = true;
 470         } else {
 471           usingClientCompiler = true;
 472         }
 473       }
 474     }
 475 
 476     if (debugger != null) {
 477       isLP64 = debugger.getMachineDescription().isLP64();
 478     }
 479     bytesPerLong = db.lookupIntConstant("BytesPerLong").intValue();
 480     bytesPerWord = db.lookupIntConstant("BytesPerWord").intValue();
 481     heapWordSize = db.lookupIntConstant("HeapWordSize").intValue();
 482     Flags_DEFAULT = db.lookupIntConstant("JVMFlagOrigin::DEFAULT").intValue();
 483     Flags_COMMAND_LINE = db.lookupIntConstant("JVMFlagOrigin::COMMAND_LINE").intValue();
 484     Flags_ENVIRON_VAR = db.lookupIntConstant("JVMFlagOrigin::ENVIRON_VAR").intValue();
 485     Flags_CONFIG_FILE = db.lookupIntConstant("JVMFlagOrigin::CONFIG_FILE").intValue();
 486     Flags_MANAGEMENT = db.lookupIntConstant("JVMFlagOrigin::MANAGEMENT").intValue();
 487     Flags_ERGONOMIC = db.lookupIntConstant("JVMFlagOrigin::ERGONOMIC").intValue();
 488     Flags_ATTACH_ON_DEMAND = db.lookupIntConstant("JVMFlagOrigin::ATTACH_ON_DEMAND").intValue();
 489     Flags_INTERNAL = db.lookupIntConstant("JVMFlagOrigin::INTERNAL").intValue();
 490     Flags_JIMAGE_RESOURCE = db.lookupIntConstant("JVMFlagOrigin::JIMAGE_RESOURCE").intValue();
 491     Flags_VALUE_ORIGIN_MASK = db.lookupIntConstant("JVMFlag::VALUE_ORIGIN_MASK").intValue();
 492     Flags_WAS_SET_ON_COMMAND_LINE = db.lookupIntConstant("JVMFlag::WAS_SET_ON_COMMAND_LINE").intValue();
 493     oopSize  = db.lookupIntConstant("oopSize").intValue();
 494 
 495     intType = db.lookupType("int");
 496     uintType = db.lookupType("uint");
 497     intxType = db.lookupType("intx");
 498     uintxType = db.lookupType("uintx");
 499     sizetType = db.lookupType("size_t");
 500     uint64tType = db.lookupType("uint64_t");
 501     boolType = (CIntegerType) db.lookupType("bool");
 502 
 503     minObjAlignmentInBytes = getObjectAlignmentInBytes();
 504     if ((minObjAlignmentInBytes & (minObjAlignmentInBytes - 1)) != 0) {
 505       throw new RuntimeException("Object alignment " + minObjAlignmentInBytes + " is not power of two");
 506     }
 507 
 508     logMinObjAlignmentInBytes = Integer.numberOfTrailingZeros(minObjAlignmentInBytes);
 509 
 510     if (isCompressedOopsEnabled()) {
 511       // Size info for oops within java objects is fixed
 512       heapOopSize = (int)getIntSize();
 513     } else {
 514       heapOopSize = (int)getOopSize();
 515     }
 516 
 517     if (isCompressedKlassPointersEnabled()) {
 518       klassPtrSize = (int)getIntSize();
 519     } else {
 520       klassPtrSize = (int)getOopSize(); // same as an oop
 521     }
 522   }
 523 
 524   /** This could be used by a reflective runtime system */
 525   public static void initialize(TypeDataBase db, boolean isBigEndian) {
 526     if (soleInstance != null) {
 527       throw new RuntimeException("Attempt to initialize VM twice");
 528     }
 529     soleInstance = new VM(db, null, isBigEndian);
 530     for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) {
 531       ((Observer) iter.next()).update(null, null);
 532     }
 533   }
 534 
 535   /** This is used by the debugging system */
 536   public static void initialize(TypeDataBase db, JVMDebugger debugger) {
 537     if (soleInstance != null) {
 538       // Using multiple SA Tool classes in the same process creates a call here.
 539       return;
 540     }
 541     soleInstance = new VM(db, debugger, debugger.getMachineDescription().isBigEndian());
 542 
 543     for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) {
 544       ((Observer) iter.next()).update(null, null);
 545     }
 546 
 547     debugger.putHeapConst(soleInstance.getHeapOopSize(), soleInstance.getKlassPtrSize(),
 548                           CompressedOops.getBase(), CompressedOops.getShift(),
 549                           CompressedKlassPointers.getBase(), CompressedKlassPointers.getShift());
 550   }
 551 
 552   /** This is used by the debugging system */
 553   public static void shutdown() {
 554     soleInstance = null;
 555   }
 556 
 557   /** This is used by both the debugger and any runtime system. It is
 558       the basic mechanism by which classes which mimic underlying VM
 559       functionality cause themselves to be initialized. The given
 560       observer will be notified (with arguments (null, null)) when the
 561       VM is re-initialized, as well as when it registers itself with
 562       the VM. */
 563   public static void registerVMInitializedObserver(Observer o) {
 564     vmInitializedObservers.add(o);
 565     o.update(null, null);
 566   }
 567 
 568   /** This is the primary accessor used by both the debugger and any
 569       potential runtime system */
 570   public static VM getVM() {
 571     if (soleInstance == null) {
 572       throw new RuntimeException("VM.initialize() was not yet called");
 573     }
 574     return soleInstance;
 575   }
 576 
 577   /** This is only used by the debugging system. The given observer
 578       will be notified if the underlying VM resumes execution. NOTE
 579       that the given observer is not triggered if the VM is currently
 580       running and therefore differs in behavior from {@link
 581       #registerVMInitializedObserver} (because of the possibility of
 582       race conditions if the observer is added while the VM is being
 583       suspended or resumed).  */
 584   public void registerVMResumedObserver(Observer o) {
 585     vmResumedObservers.add(o);
 586   }
 587 
 588   /** This is only used by the debugging system. The given observer
 589       will be notified if the underlying VM suspends execution. NOTE
 590       that the given observer is not triggered if the VM is currently
 591       suspended and therefore differs in behavior from {@link
 592       #registerVMInitializedObserver} (because of the possibility of
 593       race conditions if the observer is added while the VM is being
 594       suspended or resumed).  */
 595   public void registerVMSuspendedObserver(Observer o) {
 596     vmSuspendedObservers.add(o);
 597   }
 598 
 599   /** This is only used by the debugging system. Informs all
 600       registered resumption observers that the VM has been resumed.
 601       The application is responsible for actually having performed the
 602       resumption. No OopHandles must be used after this point, as they
 603       may move in the target address space due to garbage
 604       collection. */
 605   public void fireVMResumed() {
 606     for (Iterator iter = vmResumedObservers.iterator(); iter.hasNext(); ) {
 607       ((Observer) iter.next()).update(null, null);
 608     }
 609   }
 610 
 611   /** This is only used by the debugging system. Informs all
 612       registered suspension observers that the VM has been suspended.
 613       The application is responsible for actually having performed the
 614       suspension. Garbage collection must be forbidden at this point;
 615       for example, a JPDA-level suspension is not adequate since the
 616       VM thread may still be running. */
 617   public void fireVMSuspended() {
 618     for (Iterator iter = vmSuspendedObservers.iterator(); iter.hasNext(); ) {
 619       ((Observer) iter.next()).update(null, null);
 620     }
 621   }
 622 
 623   /** Returns the OS this VM is running on. Notice that by delegating
 624       to the debugger we can transparently support remote
 625       debugging. */
 626   public String getOS() {
 627     if (debugger != null) {
 628       return debugger.getOS();
 629     }
 630     return PlatformInfo.getOS();
 631   }
 632 
 633   /** Returns the CPU this VM is running on. Notice that by delegating
 634       to the debugger we can transparently support remote
 635       debugging. */
 636   public String getCPU() {
 637     if (debugger != null) {
 638       return debugger.getCPU();
 639     }
 640     return PlatformInfo.getCPU();
 641   }
 642 
 643   public Type lookupType(String cTypeName) {
 644     return db.lookupType(cTypeName);
 645   }
 646 
 647   public Integer lookupIntConstant(String name) {
 648     return db.lookupIntConstant(name);
 649   }
 650 
 651   // Convenience function for conversions
 652   static public long getAddressValue(Address addr) {
 653     return VM.getVM().getDebugger().getAddressValue(addr);
 654   }
 655 
 656   public long getAddressSize() {
 657     return db.getAddressSize();
 658   }
 659 
 660   public long getOopSize() {
 661     return oopSize;
 662   }
 663 
 664   public long getLogAddressSize() {
 665     return logAddressSize;
 666   }
 667 
 668   public long getIntSize() {
 669     return db.getJIntType().getSize();
 670   }
 671 
 672   /** Indicates whether the underlying machine supports the LP64 data
 673       model. This is needed for conditionalizing code in a few places */
 674   public boolean isLP64() {
 675     if (Assert.ASSERTS_ENABLED) {
 676       Assert.that(isDebugging(), "Debugging system only for now");
 677     }
 678     return isLP64;
 679   }
 680 
 681   /** Get bytes-per-long == long/double natural alignment. */
 682   public int getBytesPerLong() {
 683     return bytesPerLong;
 684   }
 685 
 686   public int getBytesPerWord() {
 687     return bytesPerWord;
 688   }
 689 
 690   /** Get minimum object alignment in bytes. */
 691   public int getMinObjAlignmentInBytes() {
 692     return minObjAlignmentInBytes;
 693   }
 694   public int getLogMinObjAlignmentInBytes() {
 695     return logMinObjAlignmentInBytes;
 696   }
 697 
 698   public int getHeapWordSize() {
 699     return heapWordSize;
 700   }
 701 
 702   public int getHeapOopSize() {
 703     return heapOopSize;
 704   }
 705 
 706   public int getKlassPtrSize() {
 707     return klassPtrSize;
 708   }
 709   /** Utility routine for getting data structure alignment correct */
 710   public long alignUp(long size, long alignment) {
 711     return (size + alignment - 1) & ~(alignment - 1);
 712   }
 713 
 714   /** Utility routine for getting data structure alignment correct */
 715   public long alignDown(long size, long alignment) {
 716     return size & ~(alignment - 1);
 717   }
 718 
 719   /** Utility routine for building an int from two "unsigned" 16-bit
 720       shorts */
 721   public int buildIntFromShorts(short low, short high) {
 722     return (((int) high) << 16) | (((int) low) & 0xFFFF);
 723   }
 724 
 725   /** Utility routine for building a long from two "unsigned" 32-bit
 726       ints in <b>platform-dependent</b> order */
 727   public long buildLongFromIntsPD(int oneHalf, int otherHalf) {
 728     if (isBigEndian) {
 729       return (((long) otherHalf) << 32) | (((long) oneHalf) & 0x00000000FFFFFFFFL);
 730     } else{
 731       return (((long) oneHalf) << 32) | (((long) otherHalf) & 0x00000000FFFFFFFFL);
 732     }
 733   }
 734 
 735   public TypeDataBase getTypeDataBase() {
 736     return db;
 737   }
 738 
 739   public Universe    getUniverse() {
 740     if (universe == null) {
 741       universe = new Universe();
 742     }
 743     return universe;
 744   }
 745 
 746   public ObjectHeap  getObjectHeap() {
 747     if (heap == null) {
 748       heap = new ObjectHeap(db);
 749     }
 750     return heap;
 751   }
 752 
 753   public SystemDictionary getSystemDictionary() {
 754     if (dict == null) {
 755       dict = new SystemDictionary();
 756     }
 757     return dict;
 758   }
 759 
 760   public ClassLoaderDataGraph getClassLoaderDataGraph() {
 761     if (cldGraph == null) {
 762       cldGraph = new ClassLoaderDataGraph();
 763     }
 764     return cldGraph;
 765   }
 766 
 767   public Threads     getThreads() {
 768     if (threads == null) {
 769       threads = new Threads();
 770     }
 771     return threads;
 772   }
 773 
 774   public ObjectSynchronizer getObjectSynchronizer() {
 775     if (synchronizer == null) {
 776       synchronizer = new ObjectSynchronizer();
 777     }
 778     return synchronizer;
 779   }
 780 
 781   public JNIHandles getJNIHandles() {
 782     if (handles == null) {
 783       handles = new JNIHandles();
 784     }
 785     return handles;
 786   }
 787 
 788   public Interpreter getInterpreter() {
 789     if (interpreter == null) {
 790       interpreter = new Interpreter();
 791     }
 792     return interpreter;
 793   }
 794 
 795   public StubRoutines getStubRoutines() {
 796     if (stubRoutines == null) {
 797       stubRoutines = new StubRoutines();
 798     }
 799     return stubRoutines;
 800   }
 801 
 802   public VMRegImpl getVMRegImplInfo() {
 803     if (vmregImpl == null) {
 804       vmregImpl = new VMRegImpl();
 805     }
 806     return vmregImpl;
 807   }
 808 
 809   public FileMapInfo getFileMapInfo() {
 810     if (!isSharingEnabled()) {
 811       return null;
 812     }
 813     if (fileMapInfo == null) {
 814       fileMapInfo = new FileMapInfo();
 815     }
 816     return fileMapInfo;
 817   }
 818 
 819   public Bytes getBytes() {
 820     if (bytes == null) {
 821       bytes = new Bytes(debugger.getMachineDescription());
 822     }
 823     return bytes;
 824   }
 825 
 826   /** Returns true if this is a isBigEndian, false otherwise */
 827   public boolean isBigEndian() {
 828     return isBigEndian;
 829   }
 830 
 831   /** Returns true if JVMTI is supported, false otherwise */
 832   public boolean isJvmtiSupported() {
 833     return isJvmtiSupported;
 834   }
 835 
 836   /** Returns true if this is a "core" build, false if either C1 or C2
 837       is present */
 838   public boolean isCore() {
 839     return (!(usingClientCompiler || usingServerCompiler));
 840   }
 841 
 842   /** Returns true if this is a C1 build, false otherwise */
 843   public boolean isClientCompiler() {
 844     return usingClientCompiler;
 845   }
 846 
 847   /** Returns true if this is a C2 build, false otherwise */
 848   public boolean isServerCompiler() {
 849     return usingServerCompiler;
 850   }
 851 
 852   /** Returns true if C2 derived pointer table should be used, false otherwise */
 853   public boolean useDerivedPointerTable() {
 854     return !disableDerivedPointerTableCheck;
 855   }
 856 
 857   /** Returns the code cache; should not be used if is core build */
 858   public CodeCache getCodeCache() {
 859     if (Assert.ASSERTS_ENABLED) {
 860       Assert.that(!isCore(), "noncore builds only");
 861     }
 862     if (codeCache == null) {
 863       codeCache = new CodeCache();
 864     }
 865     return codeCache;
 866   }
 867 
 868   /** Should only be called for C1 builds */
 869   public Runtime1 getRuntime1() {
 870     if (Assert.ASSERTS_ENABLED) {
 871       Assert.that(isClientCompiler(), "C1 builds only");
 872     }
 873     if (runtime1 == null) {
 874       runtime1 = new Runtime1();
 875     }
 876     return runtime1;
 877   }
 878 
 879   /** Test to see whether we're in debugging mode (NOTE: this really
 880       should not be tested by this code; currently only used in
 881       StackFrameStream) */
 882   public boolean isDebugging() {
 883     return (debugger != null);
 884   }
 885 
 886   /** This is only used by the debugging (i.e., non-runtime) system */
 887   public JVMDebugger getDebugger() {
 888     if (debugger == null) {
 889       throw new RuntimeException("Attempt to use debugger in runtime system");
 890     }
 891     return debugger;
 892   }
 893 
 894   /** Indicates whether a given program counter is in Java code. This
 895       includes but is not spanned by the interpreter and code cache.
 896       Only used in the debugging system, for implementing
 897       JavaThread.currentFrameGuess() on x86. */
 898   public boolean isJavaPCDbg(Address addr) {
 899     // FIXME: this is not a complete enough set: must include areas
 900     // like vtable stubs
 901     return (getInterpreter().contains(addr) ||
 902             getCodeCache().contains(addr));
 903   }
 904 
 905   /** FIXME: figure out where to stick this */
 906   public int getInvocationEntryBCI() {
 907     return invocationEntryBCI;
 908   }
 909 
 910   // FIXME: figure out where to stick this
 911   public boolean wizardMode() {
 912     return true;
 913   }
 914 
 915   public ReversePtrs getRevPtrs() {
 916     return revPtrs;
 917   }
 918 
 919   public void setRevPtrs(ReversePtrs rp) {
 920     revPtrs = rp;
 921   }
 922 
 923   // returns null, if not available.
 924   public String getVMRelease() {
 925     return vmRelease;
 926   }
 927 
 928   // returns null, if not available.
 929   public String getVMInternalInfo() {
 930     return vmInternalInfo;
 931   }
 932 
 933   public int getReserveForAllocationPrefetch() {
 934     return reserveForAllocationPrefetch;
 935   }
 936 
 937   public boolean isSharingEnabled() {
 938     if (sharingEnabled == null) {
 939       Flag flag = getCommandLineFlag("UseSharedSpaces");
 940       sharingEnabled = (flag == null)? Boolean.FALSE :
 941           (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 942     }
 943     return sharingEnabled.booleanValue();
 944   }
 945 
 946   public boolean isCompressedOopsEnabled() {
 947     if (compressedOopsEnabled == null) {
 948         Flag flag = getCommandLineFlag("UseCompressedOops");
 949         compressedOopsEnabled = (flag == null) ? Boolean.FALSE:
 950              (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 951     }
 952     return compressedOopsEnabled.booleanValue();
 953   }
 954 
 955   public boolean isCompressedKlassPointersEnabled() {
 956     if (compressedKlassPointersEnabled == null) {
 957         Flag flag = getCommandLineFlag("UseCompressedClassPointers");
 958         compressedKlassPointersEnabled = (flag == null) ? Boolean.FALSE:
 959              (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 960     }
 961     return compressedKlassPointersEnabled.booleanValue();
 962   }
 963 
 964   public boolean isCompactObjectHeadersEnabled() {
 965     if (compactObjectHeadersEnabled == null) {
 966       Flag flag = getCommandLineFlag("UseCompactObjectHeaders");
 967       compactObjectHeadersEnabled = (flag == null) ? Boolean.FALSE:
 968                      (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 969     }
 970     return compactObjectHeadersEnabled.booleanValue();
 971   }
 972 
 973   public int getObjectAlignmentInBytes() {
 974     if (objectAlignmentInBytes == 0) {
 975         Flag flag = getCommandLineFlag("ObjectAlignmentInBytes");
 976         objectAlignmentInBytes = (flag == null) ? 8 : (int)flag.getIntx();
 977     }
 978     return objectAlignmentInBytes;
 979   }
 980 
 981   /** Indicates whether Thread-Local Allocation Buffers are used */
 982   public boolean getUseTLAB() {
 983       Flag flag = getCommandLineFlag("UseTLAB");
 984       return (flag == null) ? false: flag.getBool();
 985   }
 986 
 987   public boolean getCommandLineBooleanFlag(String name) {
 988     Flag flag = getCommandLineFlag(name);
 989     return (flag == null) ? Boolean.FALSE:
 990       (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 991   }
 992 
 993   // returns null, if not available.
 994   public Flag[] getCommandLineFlags() {
 995     if (commandLineFlags == null) {
 996        readCommandLineFlags();
 997     }
 998 
 999     return commandLineFlags;
1000   }
1001 
1002   public Flag getCommandLineFlag(String name) {
1003     if (flagsMap == null) {
1004       flagsMap = new HashMap<>();
1005       Flag[] flags = getCommandLineFlags();
1006       for (int i = 0; i < flags.length; i++) {
1007         flagsMap.put(flags[i].getName(), flags[i]);
1008       }
1009     }
1010     return (Flag) flagsMap.get(name);
1011   }
1012 
1013   private static final String cmdFlagTypes[] = {
1014     "bool",
1015     "int",
1016     "uint",
1017     "intx",
1018     "uintx",
1019     "uint64_t",
1020     "size_t",
1021     "double",
1022     "ccstr",
1023     "ccstrlist"
1024   };
1025 
1026   private String getFlagTypeAsString(int typeIndex) {
1027     if (0 <= typeIndex && typeIndex < cmdFlagTypes.length) {
1028       return cmdFlagTypes[typeIndex];
1029     } else {
1030       return "unknown";
1031     }
1032   }
1033 
1034   private void readCommandLineFlags() {
1035     // get command line flags
1036     TypeDataBase db = getTypeDataBase();
1037     Type flagType = db.lookupType("JVMFlag");
1038     int numFlags = (int) flagType.getCIntegerField("numFlags").getValue();
1039     // NOTE: last flag contains null values.
1040     commandLineFlags = new Flag[numFlags - 1];
1041 
1042     Address flagAddr = flagType.getAddressField("flags").getValue();
1043     CIntField typeFld = new CIntField(flagType.getCIntegerField("_type"), 0);
1044     AddressField nameFld = flagType.getAddressField("_name");
1045     AddressField addrFld = flagType.getAddressField("_addr");
1046     CIntField flagsFld = new CIntField(flagType.getCIntegerField("_flags"), 0);
1047 
1048     long flagSize = flagType.getSize(); // sizeof(Flag)
1049 
1050     // NOTE: last flag contains null values.
1051     for (int f = 0; f < numFlags - 1; f++) {
1052       int typeIndex = (int)typeFld.getValue(flagAddr);
1053       String type = getFlagTypeAsString(typeIndex);
1054       String name = CStringUtilities.getString(nameFld.getValue(flagAddr));
1055       Address addr = addrFld.getValue(flagAddr);
1056       int flags = (int)flagsFld.getValue(flagAddr);
1057       commandLineFlags[f] = new Flag(type, name, addr, flags);
1058       flagAddr = flagAddr.addOffsetTo(flagSize);
1059     }
1060 
1061     // sort flags by name
1062     Arrays.sort(commandLineFlags, new Comparator<>() {
1063         public int compare(Flag f1, Flag f2) {
1064           return f1.getName().compareTo(f2.getName());
1065         }
1066       });
1067   }
1068 
1069   public String getSystemProperty(String key) {
1070     Properties props = getSystemProperties();
1071     return (props != null)? props.getProperty(key) : null;
1072   }
1073 
1074   public Properties getSystemProperties() {
1075     if (sysProps == null) {
1076        readSystemProperties();
1077     }
1078     return sysProps;
1079   }
1080 
1081   private void readSystemProperties() {
1082     final InstanceKlass systemKls = getSystemDictionary().getSystemKlass();
1083     systemKls.iterateStaticFields(new DefaultOopVisitor() {
1084         ObjectReader objReader = new ObjectReader();
1085         public void doOop(sun.jvm.hotspot.oops.OopField field, boolean isVMField) {
1086           if (field.getID().getName().equals("props")) {
1087             try {
1088               sysProps = (Properties) objReader.readObject(field.getValue(getObj()));
1089             } catch (Exception e) {
1090               e.printStackTrace();
1091             }
1092           }
1093         }
1094       });
1095   }
1096 }