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 == 8) {
 505       logMinObjAlignmentInBytes = 3;
 506     } else if (minObjAlignmentInBytes == 16) {
 507       logMinObjAlignmentInBytes = 4;
 508     } else {
 509       throw new RuntimeException("Object alignment " + minObjAlignmentInBytes + " not yet supported");
 510     }
 511 
 512     if (isCompressedOopsEnabled()) {
 513       // Size info for oops within java objects is fixed
 514       heapOopSize = (int)getIntSize();
 515     } else {
 516       heapOopSize = (int)getOopSize();
 517     }
 518 
 519     if (isCompressedKlassPointersEnabled()) {
 520       klassPtrSize = (int)getIntSize();
 521     } else {
 522       klassPtrSize = (int)getOopSize(); // same as an oop
 523     }
 524   }
 525 
 526   /** This could be used by a reflective runtime system */
 527   public static void initialize(TypeDataBase db, boolean isBigEndian) {
 528     if (soleInstance != null) {
 529       throw new RuntimeException("Attempt to initialize VM twice");
 530     }
 531     soleInstance = new VM(db, null, isBigEndian);
 532     for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) {
 533       ((Observer) iter.next()).update(null, null);
 534     }
 535   }
 536 
 537   /** This is used by the debugging system */
 538   public static void initialize(TypeDataBase db, JVMDebugger debugger) {
 539     if (soleInstance != null) {
 540       // Using multiple SA Tool classes in the same process creates a call here.
 541       return;
 542     }
 543     soleInstance = new VM(db, debugger, debugger.getMachineDescription().isBigEndian());
 544 
 545     for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) {
 546       ((Observer) iter.next()).update(null, null);
 547     }
 548 
 549     debugger.putHeapConst(soleInstance.getHeapOopSize(), soleInstance.getKlassPtrSize(),
 550                           CompressedOops.getBase(), CompressedOops.getShift(),
 551                           CompressedKlassPointers.getBase(), CompressedKlassPointers.getShift());
 552   }
 553 
 554   /** This is used by the debugging system */
 555   public static void shutdown() {
 556     soleInstance = null;
 557   }
 558 
 559   /** This is used by both the debugger and any runtime system. It is
 560       the basic mechanism by which classes which mimic underlying VM
 561       functionality cause themselves to be initialized. The given
 562       observer will be notified (with arguments (null, null)) when the
 563       VM is re-initialized, as well as when it registers itself with
 564       the VM. */
 565   public static void registerVMInitializedObserver(Observer o) {
 566     vmInitializedObservers.add(o);
 567     o.update(null, null);
 568   }
 569 
 570   /** This is the primary accessor used by both the debugger and any
 571       potential runtime system */
 572   public static VM getVM() {
 573     if (soleInstance == null) {
 574       throw new RuntimeException("VM.initialize() was not yet called");
 575     }
 576     return soleInstance;
 577   }
 578 
 579   /** This is only used by the debugging system. The given observer
 580       will be notified if the underlying VM resumes execution. NOTE
 581       that the given observer is not triggered if the VM is currently
 582       running and therefore differs in behavior from {@link
 583       #registerVMInitializedObserver} (because of the possibility of
 584       race conditions if the observer is added while the VM is being
 585       suspended or resumed).  */
 586   public void registerVMResumedObserver(Observer o) {
 587     vmResumedObservers.add(o);
 588   }
 589 
 590   /** This is only used by the debugging system. The given observer
 591       will be notified if the underlying VM suspends execution. NOTE
 592       that the given observer is not triggered if the VM is currently
 593       suspended and therefore differs in behavior from {@link
 594       #registerVMInitializedObserver} (because of the possibility of
 595       race conditions if the observer is added while the VM is being
 596       suspended or resumed).  */
 597   public void registerVMSuspendedObserver(Observer o) {
 598     vmSuspendedObservers.add(o);
 599   }
 600 
 601   /** This is only used by the debugging system. Informs all
 602       registered resumption observers that the VM has been resumed.
 603       The application is responsible for actually having performed the
 604       resumption. No OopHandles must be used after this point, as they
 605       may move in the target address space due to garbage
 606       collection. */
 607   public void fireVMResumed() {
 608     for (Iterator iter = vmResumedObservers.iterator(); iter.hasNext(); ) {
 609       ((Observer) iter.next()).update(null, null);
 610     }
 611   }
 612 
 613   /** This is only used by the debugging system. Informs all
 614       registered suspension observers that the VM has been suspended.
 615       The application is responsible for actually having performed the
 616       suspension. Garbage collection must be forbidden at this point;
 617       for example, a JPDA-level suspension is not adequate since the
 618       VM thread may still be running. */
 619   public void fireVMSuspended() {
 620     for (Iterator iter = vmSuspendedObservers.iterator(); iter.hasNext(); ) {
 621       ((Observer) iter.next()).update(null, null);
 622     }
 623   }
 624 
 625   /** Returns the OS this VM is running on. Notice that by delegating
 626       to the debugger we can transparently support remote
 627       debugging. */
 628   public String getOS() {
 629     if (debugger != null) {
 630       return debugger.getOS();
 631     }
 632     return PlatformInfo.getOS();
 633   }
 634 
 635   /** Returns the CPU this VM is running on. Notice that by delegating
 636       to the debugger we can transparently support remote
 637       debugging. */
 638   public String getCPU() {
 639     if (debugger != null) {
 640       return debugger.getCPU();
 641     }
 642     return PlatformInfo.getCPU();
 643   }
 644 
 645   public Type lookupType(String cTypeName) {
 646     return db.lookupType(cTypeName);
 647   }
 648 
 649   public Integer lookupIntConstant(String name) {
 650     return db.lookupIntConstant(name);
 651   }
 652 
 653   // Convenience function for conversions
 654   static public long getAddressValue(Address addr) {
 655     return VM.getVM().getDebugger().getAddressValue(addr);
 656   }
 657 
 658   public long getAddressSize() {
 659     return db.getAddressSize();
 660   }
 661 
 662   public long getOopSize() {
 663     return oopSize;
 664   }
 665 
 666   public long getLogAddressSize() {
 667     return logAddressSize;
 668   }
 669 
 670   public long getIntSize() {
 671     return db.getJIntType().getSize();
 672   }
 673 
 674   /** Indicates whether the underlying machine supports the LP64 data
 675       model. This is needed for conditionalizing code in a few places */
 676   public boolean isLP64() {
 677     if (Assert.ASSERTS_ENABLED) {
 678       Assert.that(isDebugging(), "Debugging system only for now");
 679     }
 680     return isLP64;
 681   }
 682 
 683   /** Get bytes-per-long == long/double natural alignment. */
 684   public int getBytesPerLong() {
 685     return bytesPerLong;
 686   }
 687 
 688   public int getBytesPerWord() {
 689     return bytesPerWord;
 690   }
 691 
 692   /** Get minimum object alignment in bytes. */
 693   public int getMinObjAlignmentInBytes() {
 694     return minObjAlignmentInBytes;
 695   }
 696   public int getLogMinObjAlignmentInBytes() {
 697     return logMinObjAlignmentInBytes;
 698   }
 699 
 700   public int getHeapWordSize() {
 701     return heapWordSize;
 702   }
 703 
 704   public int getHeapOopSize() {
 705     return heapOopSize;
 706   }
 707 
 708   public int getKlassPtrSize() {
 709     return klassPtrSize;
 710   }
 711   /** Utility routine for getting data structure alignment correct */
 712   public long alignUp(long size, long alignment) {
 713     return (size + alignment - 1) & ~(alignment - 1);
 714   }
 715 
 716   /** Utility routine for getting data structure alignment correct */
 717   public long alignDown(long size, long alignment) {
 718     return size & ~(alignment - 1);
 719   }
 720 
 721   /** Utility routine for building an int from two "unsigned" 16-bit
 722       shorts */
 723   public int buildIntFromShorts(short low, short high) {
 724     return (((int) high) << 16) | (((int) low) & 0xFFFF);
 725   }
 726 
 727   /** Utility routine for building a long from two "unsigned" 32-bit
 728       ints in <b>platform-dependent</b> order */
 729   public long buildLongFromIntsPD(int oneHalf, int otherHalf) {
 730     if (isBigEndian) {
 731       return (((long) otherHalf) << 32) | (((long) oneHalf) & 0x00000000FFFFFFFFL);
 732     } else{
 733       return (((long) oneHalf) << 32) | (((long) otherHalf) & 0x00000000FFFFFFFFL);
 734     }
 735   }
 736 
 737   public TypeDataBase getTypeDataBase() {
 738     return db;
 739   }
 740 
 741   public Universe    getUniverse() {
 742     if (universe == null) {
 743       universe = new Universe();
 744     }
 745     return universe;
 746   }
 747 
 748   public ObjectHeap  getObjectHeap() {
 749     if (heap == null) {
 750       heap = new ObjectHeap(db);
 751     }
 752     return heap;
 753   }
 754 
 755   public SystemDictionary getSystemDictionary() {
 756     if (dict == null) {
 757       dict = new SystemDictionary();
 758     }
 759     return dict;
 760   }
 761 
 762   public ClassLoaderDataGraph getClassLoaderDataGraph() {
 763     if (cldGraph == null) {
 764       cldGraph = new ClassLoaderDataGraph();
 765     }
 766     return cldGraph;
 767   }
 768 
 769   public Threads     getThreads() {
 770     if (threads == null) {
 771       threads = new Threads();
 772     }
 773     return threads;
 774   }
 775 
 776   public ObjectSynchronizer getObjectSynchronizer() {
 777     if (synchronizer == null) {
 778       synchronizer = new ObjectSynchronizer();
 779     }
 780     return synchronizer;
 781   }
 782 
 783   public JNIHandles getJNIHandles() {
 784     if (handles == null) {
 785       handles = new JNIHandles();
 786     }
 787     return handles;
 788   }
 789 
 790   public Interpreter getInterpreter() {
 791     if (interpreter == null) {
 792       interpreter = new Interpreter();
 793     }
 794     return interpreter;
 795   }
 796 
 797   public StubRoutines getStubRoutines() {
 798     if (stubRoutines == null) {
 799       stubRoutines = new StubRoutines();
 800     }
 801     return stubRoutines;
 802   }
 803 
 804   public VMRegImpl getVMRegImplInfo() {
 805     if (vmregImpl == null) {
 806       vmregImpl = new VMRegImpl();
 807     }
 808     return vmregImpl;
 809   }
 810 
 811   public FileMapInfo getFileMapInfo() {
 812     if (!isSharingEnabled()) {
 813       return null;
 814     }
 815     if (fileMapInfo == null) {
 816       fileMapInfo = new FileMapInfo();
 817     }
 818     return fileMapInfo;
 819   }
 820 
 821   public Bytes getBytes() {
 822     if (bytes == null) {
 823       bytes = new Bytes(debugger.getMachineDescription());
 824     }
 825     return bytes;
 826   }
 827 
 828   /** Returns true if this is a isBigEndian, false otherwise */
 829   public boolean isBigEndian() {
 830     return isBigEndian;
 831   }
 832 
 833   /** Returns true if JVMTI is supported, false otherwise */
 834   public boolean isJvmtiSupported() {
 835     return isJvmtiSupported;
 836   }
 837 
 838   /** Returns true if this is a "core" build, false if either C1 or C2
 839       is present */
 840   public boolean isCore() {
 841     return (!(usingClientCompiler || usingServerCompiler));
 842   }
 843 
 844   /** Returns true if this is a C1 build, false otherwise */
 845   public boolean isClientCompiler() {
 846     return usingClientCompiler;
 847   }
 848 
 849   /** Returns true if this is a C2 build, false otherwise */
 850   public boolean isServerCompiler() {
 851     return usingServerCompiler;
 852   }
 853 
 854   /** Returns true if C2 derived pointer table should be used, false otherwise */
 855   public boolean useDerivedPointerTable() {
 856     return !disableDerivedPointerTableCheck;
 857   }
 858 
 859   /** Returns the code cache; should not be used if is core build */
 860   public CodeCache getCodeCache() {
 861     if (Assert.ASSERTS_ENABLED) {
 862       Assert.that(!isCore(), "noncore builds only");
 863     }
 864     if (codeCache == null) {
 865       codeCache = new CodeCache();
 866     }
 867     return codeCache;
 868   }
 869 
 870   /** Should only be called for C1 builds */
 871   public Runtime1 getRuntime1() {
 872     if (Assert.ASSERTS_ENABLED) {
 873       Assert.that(isClientCompiler(), "C1 builds only");
 874     }
 875     if (runtime1 == null) {
 876       runtime1 = new Runtime1();
 877     }
 878     return runtime1;
 879   }
 880 
 881   /** Test to see whether we're in debugging mode (NOTE: this really
 882       should not be tested by this code; currently only used in
 883       StackFrameStream) */
 884   public boolean isDebugging() {
 885     return (debugger != null);
 886   }
 887 
 888   /** This is only used by the debugging (i.e., non-runtime) system */
 889   public JVMDebugger getDebugger() {
 890     if (debugger == null) {
 891       throw new RuntimeException("Attempt to use debugger in runtime system");
 892     }
 893     return debugger;
 894   }
 895 
 896   /** Indicates whether a given program counter is in Java code. This
 897       includes but is not spanned by the interpreter and code cache.
 898       Only used in the debugging system, for implementing
 899       JavaThread.currentFrameGuess() on x86. */
 900   public boolean isJavaPCDbg(Address addr) {
 901     // FIXME: this is not a complete enough set: must include areas
 902     // like vtable stubs
 903     return (getInterpreter().contains(addr) ||
 904             getCodeCache().contains(addr));
 905   }
 906 
 907   /** FIXME: figure out where to stick this */
 908   public int getInvocationEntryBCI() {
 909     return invocationEntryBCI;
 910   }
 911 
 912   // FIXME: figure out where to stick this
 913   public boolean wizardMode() {
 914     return true;
 915   }
 916 
 917   public ReversePtrs getRevPtrs() {
 918     return revPtrs;
 919   }
 920 
 921   public void setRevPtrs(ReversePtrs rp) {
 922     revPtrs = rp;
 923   }
 924 
 925   // returns null, if not available.
 926   public String getVMRelease() {
 927     return vmRelease;
 928   }
 929 
 930   // returns null, if not available.
 931   public String getVMInternalInfo() {
 932     return vmInternalInfo;
 933   }
 934 
 935   public int getReserveForAllocationPrefetch() {
 936     return reserveForAllocationPrefetch;
 937   }
 938 
 939   public boolean isSharingEnabled() {
 940     if (sharingEnabled == null) {
 941       Flag flag = getCommandLineFlag("UseSharedSpaces");
 942       sharingEnabled = (flag == null)? Boolean.FALSE :
 943           (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 944     }
 945     return sharingEnabled.booleanValue();
 946   }
 947 
 948   public boolean isCompressedOopsEnabled() {
 949     if (compressedOopsEnabled == null) {
 950         Flag flag = getCommandLineFlag("UseCompressedOops");
 951         compressedOopsEnabled = (flag == null) ? Boolean.FALSE:
 952              (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 953     }
 954     return compressedOopsEnabled.booleanValue();
 955   }
 956 
 957   public boolean isCompressedKlassPointersEnabled() {
 958     if (compressedKlassPointersEnabled == null) {
 959         Flag flag = getCommandLineFlag("UseCompressedClassPointers");
 960         compressedKlassPointersEnabled = (flag == null) ? Boolean.FALSE:
 961              (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 962     }
 963     return compressedKlassPointersEnabled.booleanValue();
 964   }
 965 
 966   public boolean isCompactObjectHeadersEnabled() {
 967     if (compactObjectHeadersEnabled == null) {
 968       Flag flag = getCommandLineFlag("UseCompactObjectHeaders");
 969       compactObjectHeadersEnabled = (flag == null) ? Boolean.FALSE:
 970                      (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 971     }
 972     return compactObjectHeadersEnabled.booleanValue();
 973   }
 974 
 975   public int getObjectAlignmentInBytes() {
 976     if (objectAlignmentInBytes == 0) {
 977         Flag flag = getCommandLineFlag("ObjectAlignmentInBytes");
 978         objectAlignmentInBytes = (flag == null) ? 8 : (int)flag.getIntx();
 979     }
 980     return objectAlignmentInBytes;
 981   }
 982 
 983   /** Indicates whether Thread-Local Allocation Buffers are used */
 984   public boolean getUseTLAB() {
 985       Flag flag = getCommandLineFlag("UseTLAB");
 986       return (flag == null) ? false: flag.getBool();
 987   }
 988 
 989   public boolean getCommandLineBooleanFlag(String name) {
 990     Flag flag = getCommandLineFlag(name);
 991     return (flag == null) ? Boolean.FALSE:
 992       (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 993   }
 994 
 995   // returns null, if not available.
 996   public Flag[] getCommandLineFlags() {
 997     if (commandLineFlags == null) {
 998        readCommandLineFlags();
 999     }
1000 
1001     return commandLineFlags;
1002   }
1003 
1004   public Flag getCommandLineFlag(String name) {
1005     if (flagsMap == null) {
1006       flagsMap = new HashMap<>();
1007       Flag[] flags = getCommandLineFlags();
1008       for (int i = 0; i < flags.length; i++) {
1009         flagsMap.put(flags[i].getName(), flags[i]);
1010       }
1011     }
1012     return (Flag) flagsMap.get(name);
1013   }
1014 
1015   private static final String cmdFlagTypes[] = {
1016     "bool",
1017     "int",
1018     "uint",
1019     "intx",
1020     "uintx",
1021     "uint64_t",
1022     "size_t",
1023     "double",
1024     "ccstr",
1025     "ccstrlist"
1026   };
1027 
1028   private String getFlagTypeAsString(int typeIndex) {
1029     if (0 <= typeIndex && typeIndex < cmdFlagTypes.length) {
1030       return cmdFlagTypes[typeIndex];
1031     } else {
1032       return "unknown";
1033     }
1034   }
1035 
1036   private void readCommandLineFlags() {
1037     // get command line flags
1038     TypeDataBase db = getTypeDataBase();
1039     Type flagType = db.lookupType("JVMFlag");
1040     int numFlags = (int) flagType.getCIntegerField("numFlags").getValue();
1041     // NOTE: last flag contains null values.
1042     commandLineFlags = new Flag[numFlags - 1];
1043 
1044     Address flagAddr = flagType.getAddressField("flags").getValue();
1045     CIntField typeFld = new CIntField(flagType.getCIntegerField("_type"), 0);
1046     AddressField nameFld = flagType.getAddressField("_name");
1047     AddressField addrFld = flagType.getAddressField("_addr");
1048     CIntField flagsFld = new CIntField(flagType.getCIntegerField("_flags"), 0);
1049 
1050     long flagSize = flagType.getSize(); // sizeof(Flag)
1051 
1052     // NOTE: last flag contains null values.
1053     for (int f = 0; f < numFlags - 1; f++) {
1054       int typeIndex = (int)typeFld.getValue(flagAddr);
1055       String type = getFlagTypeAsString(typeIndex);
1056       String name = CStringUtilities.getString(nameFld.getValue(flagAddr));
1057       Address addr = addrFld.getValue(flagAddr);
1058       int flags = (int)flagsFld.getValue(flagAddr);
1059       commandLineFlags[f] = new Flag(type, name, addr, flags);
1060       flagAddr = flagAddr.addOffsetTo(flagSize);
1061     }
1062 
1063     // sort flags by name
1064     Arrays.sort(commandLineFlags, new Comparator<>() {
1065         public int compare(Flag f1, Flag f2) {
1066           return f1.getName().compareTo(f2.getName());
1067         }
1068       });
1069   }
1070 
1071   public String getSystemProperty(String key) {
1072     Properties props = getSystemProperties();
1073     return (props != null)? props.getProperty(key) : null;
1074   }
1075 
1076   public Properties getSystemProperties() {
1077     if (sysProps == null) {
1078        readSystemProperties();
1079     }
1080     return sysProps;
1081   }
1082 
1083   private void readSystemProperties() {
1084     final InstanceKlass systemKls = getSystemDictionary().getSystemKlass();
1085     systemKls.iterateStaticFields(new DefaultOopVisitor() {
1086         ObjectReader objReader = new ObjectReader();
1087         public void doOop(sun.jvm.hotspot.oops.OopField field, boolean isVMField) {
1088           if (field.getID().getName().equals("props")) {
1089             try {
1090               sysProps = (Properties) objReader.readObject(field.getValue(getObj()));
1091             } catch (Exception e) {
1092               e.printStackTrace();
1093             }
1094           }
1095         }
1096       });
1097   }
1098 }