1 /*
   2  * Copyright 2000-2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any 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.debugger.*;
  34 import sun.jvm.hotspot.interpreter.*;
  35 import sun.jvm.hotspot.memory.*;
  36 import sun.jvm.hotspot.oops.*;
  37 import sun.jvm.hotspot.types.*;
  38 import sun.jvm.hotspot.utilities.*;
  39 import sun.jvm.hotspot.runtime.*;
  40 
  41 /** <P> This class encapsulates the global state of the VM; the
  42     universe, object heap, interpreter, etc. It is a Singleton and
  43     must be initialized with a call to initialize() before calling
  44     getVM(). </P>
  45 
  46     <P> Many auxiliary classes (i.e., most of the VMObjects) keep
  47     needed field offsets in the form of static Field objects. In a
  48     debugging system, the VM might be shutdown and re-initialized (on
  49     a differently-configured build, i.e., 32- vs. 64-bit), and all old
  50     cached state (including fields and field offsets) must be
  51     flushed. </P>
  52 
  53     <P> An Observer pattern is used to implement the initialization of
  54     such classes. Each such class, in its static initializer,
  55     registers an Observer with the VM class via
  56     VM.registerVMInitializedObserver(). This Observer is guaranteed to
  57     be notified whenever the VM is initialized (or re-initialized). To
  58     implement the first-time initialization, the observer is also
  59     notified when it registers itself with the VM. (For bootstrapping
  60     reasons, this implies that the constructor of VM can not
  61     instantiate any such objects, since VM.soleInstance will not have
  62     been set yet. This is a bootstrapping issue which may have to be
  63     revisited later.) </P>
  64 */
  65 
  66 public class VM {
  67   private static VM    soleInstance;
  68   private static List  vmInitializedObservers = new ArrayList();
  69   private List         vmResumedObservers   = new ArrayList();
  70   private List         vmSuspendedObservers = new ArrayList();
  71   private TypeDataBase db;
  72   private boolean      isBigEndian;
  73   /** This is only present if in a debugging system */
  74   private JVMDebugger  debugger;
  75   private long         stackBias;
  76   private long         logAddressSize;
  77   private Universe     universe;
  78   private ObjectHeap   heap;
  79   private SymbolTable  symbols;
  80   private StringTable  strings;
  81   private SystemDictionary dict;
  82   private Threads      threads;
  83   private ObjectSynchronizer synchronizer;
  84   private JNIHandles   handles;
  85   private Interpreter  interpreter;
  86   private StubRoutines stubRoutines;
  87   private Bytes        bytes;
  88   /** Flags indicating whether we are attached to a core, C1, or C2 build */
  89   private boolean      usingClientCompiler;
  90   private boolean      usingServerCompiler;
  91   /** Flag indicating whether UseTLAB is turned on */
  92   private boolean      useTLAB;
  93   /** alignment constants */
  94   private boolean      isLP64;
  95   private int          bytesPerLong;
  96   private int          minObjAlignmentInBytes;
  97   private int          logMinObjAlignmentInBytes;
  98   private int          heapWordSize;
  99   private int          heapOopSize;
 100   private int          oopSize;
 101   /** This is only present in a non-core build */
 102   private CodeCache    codeCache;
 103   /** This is only present in a C1 build */
 104   private Runtime1     runtime1;
 105   /** These constants come from globalDefinitions.hpp */
 106   private int          invocationEntryBCI;
 107   private int          invalidOSREntryBCI;
 108   private ReversePtrs  revPtrs;
 109   private VMRegImpl    vmregImpl;
 110 
 111   // System.getProperties from debuggee VM
 112   private Properties   sysProps;
 113 
 114   // VM version strings come from Abstract_VM_Version class
 115   private String       vmRelease;
 116   private String       vmInternalInfo;
 117 
 118   private Flag[] commandLineFlags;
 119   private Map flagsMap;
 120 
 121   private static Type intxType;
 122   private static Type uintxType;
 123   private static CIntegerType boolType;
 124   private Boolean sharingEnabled;
 125   private Boolean compressedOopsEnabled;
 126 
 127   // command line flags supplied to VM - see struct Flag in globals.hpp
 128   public static final class Flag {
 129      private String type;
 130      private String name;
 131      private Address addr;
 132      private String kind;
 133 
 134      private Flag(String type, String name, Address addr, String kind) {
 135         this.type = type;
 136         this.name = name;
 137         this.addr = addr;
 138         this.kind = kind;
 139      }
 140 
 141      public String getType() {
 142         return type;
 143      }
 144 
 145      public String getName() {
 146         return name;
 147      }
 148 
 149      public Address getAddress() {
 150         return addr;
 151      }
 152 
 153      public String getKind() {
 154         return kind;
 155      }
 156 
 157      public boolean isBool() {
 158         return type.equals("bool");
 159      }
 160 
 161      public boolean getBool() {
 162         if (Assert.ASSERTS_ENABLED) {
 163            Assert.that(isBool(), "not a bool flag!");
 164         }
 165         return addr.getCIntegerAt(0, boolType.getSize(), boolType.isUnsigned())
 166                != 0;
 167      }
 168 
 169      public boolean isIntx() {
 170         return type.equals("intx");
 171      }
 172 
 173      public long getIntx() {
 174         if (Assert.ASSERTS_ENABLED) {
 175            Assert.that(isIntx(), "not a intx flag!");
 176         }
 177         return addr.getCIntegerAt(0, intxType.getSize(), false);
 178      }
 179 
 180      public boolean isUIntx() {
 181         return type.equals("uintx");
 182      }
 183 
 184      public long getUIntx() {
 185         if (Assert.ASSERTS_ENABLED) {
 186            Assert.that(isUIntx(), "not a uintx flag!");
 187         }
 188         return addr.getCIntegerAt(0, uintxType.getSize(), true);
 189      }
 190 
 191      public String getValue() {
 192         if (isBool()) {
 193            return new Boolean(getBool()).toString();
 194         } else if (isIntx()) {
 195            return new Long(getIntx()).toString();
 196         } else if (isUIntx()) {
 197            return new Long(getUIntx()).toString();
 198         } else {
 199            return null;
 200         }
 201      }
 202   };
 203 
 204   private static void checkVMVersion(String vmRelease) {
 205      if (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null) {
 206         // read sa build version.
 207         String versionProp = "sun.jvm.hotspot.runtime.VM.saBuildVersion";
 208         String saVersion = saProps.getProperty(versionProp);
 209         if (saVersion == null)
 210            throw new RuntimeException("Missing property " + versionProp);
 211 
 212         // Strip nonproduct VM version substring (note: saVersion doesn't have it).
 213         String vmVersion = vmRelease.replaceAll("(-fastdebug)|(-debug)|(-jvmg)|(-optimized)|(-profiled)","");
 214 
 215         if (saVersion.equals(vmVersion)) {
 216            // Exact match
 217            return;
 218         }
 219         if (saVersion.indexOf('-') == saVersion.lastIndexOf('-') &&
 220             vmVersion.indexOf('-') == vmVersion.lastIndexOf('-')) {
 221            // Throw exception if different release versions:
 222            // <major>.<minor>-b<n>
 223            throw new VMVersionMismatchException(saVersion, vmRelease);
 224         } else {
 225            // Otherwise print warning to allow mismatch not release versions
 226            // during development.
 227            System.err.println("WARNING: Hotspot VM version " + vmRelease +
 228                               " does not match with SA version " + saVersion +
 229                               "." + " You may see unexpected results. ");
 230         }
 231      } else {
 232         System.err.println("WARNING: You have disabled SA and VM version check. You may be "  +
 233                            "using incompatible version of SA and you may see unexpected " +
 234                            "results.");
 235      }
 236   }
 237 
 238   private static final boolean disableDerivedPrinterTableCheck;
 239   private static final Properties saProps;
 240 
 241   static {
 242      saProps = new Properties();
 243      URL url = null;
 244      try {
 245        url = VM.class.getClassLoader().getResource("sa.properties");
 246        saProps.load(new BufferedInputStream(url.openStream()));
 247      } catch (Exception e) {
 248        throw new RuntimeException("Unable to load properties  " +
 249                                   (url == null ? "null" : url.toString()) +
 250                                   ": " + e.getMessage());
 251      }
 252 
 253      disableDerivedPrinterTableCheck = System.getProperty("sun.jvm.hotspot.runtime.VM.disableDerivedPointerTableCheck") != null;
 254   }
 255 
 256   private VM(TypeDataBase db, JVMDebugger debugger, boolean isBigEndian) {
 257     this.db          = db;
 258     this.debugger    = debugger;
 259     this.isBigEndian = isBigEndian;
 260 
 261     // Note that we don't construct universe, heap, threads,
 262     // interpreter, or stubRoutines here (any more).  The current
 263     // initialization mechanisms require that the VM be completely set
 264     // up (i.e., out of its constructor, with soleInstance assigned)
 265     // before their static initializers are run.
 266 
 267     if (db.getAddressSize() == 4) {
 268       logAddressSize = 2;
 269     } else if (db.getAddressSize() == 8) {
 270       logAddressSize = 3;
 271     } else {
 272       throw new RuntimeException("Address size " + db.getAddressSize() + " not yet supported");
 273     }
 274 
 275     // read VM version info
 276     try {
 277        Type vmVersion = db.lookupType("Abstract_VM_Version");
 278        Address releaseAddr = vmVersion.getAddressField("_s_vm_release").getValue();
 279        vmRelease = CStringUtilities.getString(releaseAddr);
 280        Address vmInternalInfoAddr = vmVersion.getAddressField("_s_internal_vm_info_string").getValue();
 281        vmInternalInfo = CStringUtilities.getString(vmInternalInfoAddr);
 282     } catch (Exception exp) {
 283        throw new RuntimeException("can't determine target's VM version : " + exp.getMessage());
 284     }
 285 
 286     checkVMVersion(vmRelease);
 287 
 288     stackBias    = db.lookupIntConstant("STACK_BIAS").intValue();
 289     invocationEntryBCI = db.lookupIntConstant("InvocationEntryBci").intValue();
 290     invalidOSREntryBCI = db.lookupIntConstant("InvalidOSREntryBci").intValue();
 291 
 292     // We infer the presence of C1 or C2 from a couple of fields we
 293     // already have present in the type database
 294     {
 295       Type type = db.lookupType("methodOopDesc");
 296       if (type.getField("_from_compiled_entry", false, false) == null) {
 297         // Neither C1 nor C2 is present
 298         usingClientCompiler = false;
 299         usingServerCompiler = false;
 300       } else {
 301         // Determine whether C2 is present
 302         if (type.getField("_interpreter_invocation_count", false, false) != null) {
 303           usingServerCompiler = true;
 304         } else {
 305           usingClientCompiler = true;
 306         }
 307       }
 308     }
 309 
 310     useTLAB = (db.lookupIntConstant("UseTLAB").intValue() != 0);
 311 
 312     if (debugger != null) {
 313       isLP64 = debugger.getMachineDescription().isLP64();
 314     }
 315     bytesPerLong = db.lookupIntConstant("BytesPerLong").intValue();
 316     minObjAlignmentInBytes = db.lookupIntConstant("MinObjAlignmentInBytes").intValue();
 317     // minObjAlignment = db.lookupIntConstant("MinObjAlignment").intValue();
 318     logMinObjAlignmentInBytes = db.lookupIntConstant("LogMinObjAlignmentInBytes").intValue();
 319     heapWordSize = db.lookupIntConstant("HeapWordSize").intValue();
 320     oopSize  = db.lookupIntConstant("oopSize").intValue();
 321 
 322     intxType = db.lookupType("intx");
 323     uintxType = db.lookupType("uintx");
 324     boolType = (CIntegerType) db.lookupType("bool");
 325 
 326     if (isCompressedOopsEnabled()) {
 327       // Size info for oops within java objects is fixed
 328       heapOopSize = (int)getIntSize();
 329     } else {
 330       heapOopSize = (int)getOopSize();
 331     }
 332   }
 333 
 334   /** This could be used by a reflective runtime system */
 335   public static void initialize(TypeDataBase db, boolean isBigEndian) {
 336     if (soleInstance != null) {
 337       throw new RuntimeException("Attempt to initialize VM twice");
 338     }
 339     soleInstance = new VM(db, null, isBigEndian);
 340     for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) {
 341       ((Observer) iter.next()).update(null, null);
 342     }
 343   }
 344 
 345   /** This is used by the debugging system */
 346   public static void initialize(TypeDataBase db, JVMDebugger debugger) {
 347     if (soleInstance != null) {
 348       throw new RuntimeException("Attempt to initialize VM twice");
 349     }
 350     soleInstance = new VM(db, debugger, debugger.getMachineDescription().isBigEndian());
 351 
 352     for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) {
 353       ((Observer) iter.next()).update(null, null);
 354     }
 355 
 356     debugger.putHeapConst(soleInstance.getHeapOopSize(), Universe.getNarrowOopBase(),
 357                           Universe.getNarrowOopShift());
 358   }
 359 
 360   /** This is used by the debugging system */
 361   public static void shutdown() {
 362     soleInstance = null;
 363   }
 364 
 365   /** This is used by both the debugger and any runtime system. It is
 366       the basic mechanism by which classes which mimic underlying VM
 367       functionality cause themselves to be initialized. The given
 368       observer will be notified (with arguments (null, null)) when the
 369       VM is re-initialized, as well as when it registers itself with
 370       the VM. */
 371   public static void registerVMInitializedObserver(Observer o) {
 372     vmInitializedObservers.add(o);
 373     o.update(null, null);
 374   }
 375 
 376   /** This is the primary accessor used by both the debugger and any
 377       potential runtime system */
 378   public static VM getVM() {
 379     if (soleInstance == null) {
 380       throw new RuntimeException("VM.initialize() was not yet called");
 381     }
 382     return soleInstance;
 383   }
 384 
 385   /** This is only used by the debugging system. The given observer
 386       will be notified if the underlying VM resumes execution. NOTE
 387       that the given observer is not triggered if the VM is currently
 388       running and therefore differs in behavior from {@link
 389       #registerVMInitializedObserver} (because of the possibility of
 390       race conditions if the observer is added while the VM is being
 391       suspended or resumed).  */
 392   public void registerVMResumedObserver(Observer o) {
 393     vmResumedObservers.add(o);
 394   }
 395 
 396   /** This is only used by the debugging system. The given observer
 397       will be notified if the underlying VM suspends execution. NOTE
 398       that the given observer is not triggered if the VM is currently
 399       suspended and therefore differs in behavior from {@link
 400       #registerVMInitializedObserver} (because of the possibility of
 401       race conditions if the observer is added while the VM is being
 402       suspended or resumed).  */
 403   public void registerVMSuspendedObserver(Observer o) {
 404     vmSuspendedObservers.add(o);
 405   }
 406 
 407   /** This is only used by the debugging system. Informs all
 408       registered resumption observers that the VM has been resumed.
 409       The application is responsible for actually having performed the
 410       resumption. No OopHandles must be used after this point, as they
 411       may move in the target address space due to garbage
 412       collection. */
 413   public void fireVMResumed() {
 414     for (Iterator iter = vmResumedObservers.iterator(); iter.hasNext(); ) {
 415       ((Observer) iter.next()).update(null, null);
 416     }
 417   }
 418 
 419   /** This is only used by the debugging system. Informs all
 420       registered suspension observers that the VM has been suspended.
 421       The application is responsible for actually having performed the
 422       suspension. Garbage collection must be forbidden at this point;
 423       for example, a JPDA-level suspension is not adequate since the
 424       VM thread may still be running. */
 425   public void fireVMSuspended() {
 426     for (Iterator iter = vmSuspendedObservers.iterator(); iter.hasNext(); ) {
 427       ((Observer) iter.next()).update(null, null);
 428     }
 429   }
 430 
 431   /** Returns the OS this VM is running on. Notice that by delegating
 432       to the debugger we can transparently support remote
 433       debugging. */
 434   public String getOS() {
 435     if (debugger != null) {
 436       return debugger.getOS();
 437     }
 438     return PlatformInfo.getOS();
 439   }
 440 
 441   /** Returns the CPU this VM is running on. Notice that by delegating
 442       to the debugger we can transparently support remote
 443       debugging. */
 444   public String getCPU() {
 445     if (debugger != null) {
 446       return debugger.getCPU();
 447     }
 448     return PlatformInfo.getCPU();
 449   }
 450 
 451   public Type lookupType(String cTypeName) {
 452     return db.lookupType(cTypeName);
 453   }
 454 
 455   public Integer lookupIntConstant(String name) {
 456     return db.lookupIntConstant(name);
 457   }
 458 
 459   // Convenience function for conversions
 460   static public long getAddressValue(Address addr) {
 461     return VM.getVM().getDebugger().getAddressValue(addr);
 462   }
 463 
 464   public long getAddressSize() {
 465     return db.getAddressSize();
 466   }
 467 
 468   public long getOopSize() {
 469     return oopSize;
 470   }
 471 
 472   public long getLogAddressSize() {
 473     return logAddressSize;
 474   }
 475 
 476   public long getIntSize() {
 477     return db.getJIntType().getSize();
 478   }
 479 
 480   /** NOTE: this offset is in BYTES in this system! */
 481   public long getStackBias() {
 482     return stackBias;
 483   }
 484 
 485   /** Indicates whether the underlying machine supports the LP64 data
 486       model. This is needed for conditionalizing code in a few places */
 487   public boolean isLP64() {
 488     if (Assert.ASSERTS_ENABLED) {
 489       Assert.that(isDebugging(), "Debugging system only for now");
 490     }
 491     return isLP64;
 492   }
 493 
 494   /** Get bytes-per-long == long/double natural alignment. */
 495   public int getBytesPerLong() {
 496     return bytesPerLong;
 497   }
 498 
 499   /** Get minimum object alignment in bytes. */
 500   public int getMinObjAlignment() {
 501     return minObjAlignmentInBytes;
 502   }
 503 
 504   public int getMinObjAlignmentInBytes() {
 505     return minObjAlignmentInBytes;
 506   }
 507   public int getLogMinObjAlignmentInBytes() {
 508     return logMinObjAlignmentInBytes;
 509   }
 510 
 511   public int getHeapWordSize() {
 512     return heapWordSize;
 513   }
 514 
 515   public int getHeapOopSize() {
 516     return heapOopSize;
 517   }
 518   /** Utility routine for getting data structure alignment correct */
 519   public long alignUp(long size, long alignment) {
 520     return (size + alignment - 1) & ~(alignment - 1);
 521   }
 522 
 523   /** Utility routine for getting data structure alignment correct */
 524   public long alignDown(long size, long alignment) {
 525     return size & ~(alignment - 1);
 526   }
 527 
 528   /** Utility routine for building an int from two "unsigned" 16-bit
 529       shorts */
 530   public int buildIntFromShorts(short low, short high) {
 531     return (((int) high) << 16) | (((int) low) & 0xFFFF);
 532   }
 533 
 534   /** Utility routine for building a long from two "unsigned" 32-bit
 535       ints in <b>platform-dependent</b> order */
 536   public long buildLongFromIntsPD(int oneHalf, int otherHalf) {
 537     if (isBigEndian) {
 538       return (((long) otherHalf) << 32) | (((long) oneHalf) & 0x00000000FFFFFFFFL);
 539     } else{
 540       return (((long) oneHalf) << 32) | (((long) otherHalf) & 0x00000000FFFFFFFFL);
 541     }
 542   }
 543 
 544   /** Indicates whether Thread-Local Allocation Buffers are used */
 545   public boolean getUseTLAB() {
 546     return useTLAB;
 547   }
 548 
 549   public TypeDataBase getTypeDataBase() {
 550     return db;
 551   }
 552 
 553   public Universe    getUniverse() {
 554     if (universe == null) {
 555       universe = new Universe();
 556     }
 557     return universe;
 558   }
 559 
 560   public ObjectHeap  getObjectHeap() {
 561     if (heap == null) {
 562       heap = new ObjectHeap(db);
 563     }
 564     return heap;
 565   }
 566 
 567   public SymbolTable getSymbolTable() {
 568     if (symbols == null) {
 569       symbols = SymbolTable.getTheTable();
 570     }
 571     return symbols;
 572   }
 573 
 574   public StringTable getStringTable() {
 575     if (strings == null) {
 576       strings = StringTable.getTheTable();
 577     }
 578     return strings;
 579   }
 580 
 581   public SystemDictionary getSystemDictionary() {
 582     if (dict == null) {
 583       dict = new SystemDictionary();
 584     }
 585     return dict;
 586   }
 587 
 588   public Threads     getThreads() {
 589     if (threads == null) {
 590       threads = new Threads();
 591     }
 592     return threads;
 593   }
 594 
 595   public ObjectSynchronizer getObjectSynchronizer() {
 596     if (synchronizer == null) {
 597       synchronizer = new ObjectSynchronizer();
 598     }
 599     return synchronizer;
 600   }
 601 
 602   public JNIHandles getJNIHandles() {
 603     if (handles == null) {
 604       handles = new JNIHandles();
 605     }
 606     return handles;
 607   }
 608 
 609   public Interpreter getInterpreter() {
 610     if (interpreter == null) {
 611       interpreter = new Interpreter();
 612     }
 613     return interpreter;
 614   }
 615 
 616   public StubRoutines getStubRoutines() {
 617     if (stubRoutines == null) {
 618       stubRoutines = new StubRoutines();
 619     }
 620     return stubRoutines;
 621   }
 622 
 623   public VMRegImpl getVMRegImplInfo() {
 624     if (vmregImpl == null) {
 625       vmregImpl = new VMRegImpl();
 626     }
 627     return vmregImpl;
 628   }
 629 
 630   public Bytes getBytes() {
 631     if (bytes == null) {
 632       bytes = new Bytes(debugger.getMachineDescription());
 633     }
 634     return bytes;
 635   }
 636 
 637   /** Returns true if this is a isBigEndian, false otherwise */
 638   public boolean isBigEndian() {
 639     return isBigEndian;
 640   }
 641 
 642   /** Returns true if this is a "core" build, false if either C1 or C2
 643       is present */
 644   public boolean isCore() {
 645     return (!(usingClientCompiler || usingServerCompiler));
 646   }
 647 
 648   /** Returns true if this is a C1 build, false otherwise */
 649   public boolean isClientCompiler() {
 650     return usingClientCompiler;
 651   }
 652 
 653   /** Returns true if this is a C2 build, false otherwise */
 654   public boolean isServerCompiler() {
 655     return usingServerCompiler;
 656   }
 657 
 658   /** Returns true if C2 derived pointer table should be used, false otherwise */
 659   public boolean useDerivedPointerTable() {
 660     return !disableDerivedPrinterTableCheck;
 661   }
 662 
 663   /** Returns the code cache; should not be used if is core build */
 664   public CodeCache getCodeCache() {
 665     if (Assert.ASSERTS_ENABLED) {
 666       Assert.that(!isCore(), "noncore builds only");
 667     }
 668     if (codeCache == null) {
 669       codeCache = new CodeCache();
 670     }
 671     return codeCache;
 672   }
 673 
 674   /** Should only be called for C1 builds */
 675   public Runtime1 getRuntime1() {
 676     if (Assert.ASSERTS_ENABLED) {
 677       Assert.that(isClientCompiler(), "C1 builds only");
 678     }
 679     if (runtime1 == null) {
 680       runtime1 = new Runtime1();
 681     }
 682     return runtime1;
 683   }
 684 
 685   /** Test to see whether we're in debugging mode (NOTE: this really
 686       should not be tested by this code; currently only used in
 687       StackFrameStream) */
 688   public boolean isDebugging() {
 689     return (debugger != null);
 690   }
 691 
 692   /** This is only used by the debugging (i.e., non-runtime) system */
 693   public JVMDebugger getDebugger() {
 694     if (debugger == null) {
 695       throw new RuntimeException("Attempt to use debugger in runtime system");
 696     }
 697     return debugger;
 698   }
 699 
 700   /** Indicates whether a given program counter is in Java code. This
 701       includes but is not spanned by the interpreter and code cache.
 702       Only used in the debugging system, for implementing
 703       JavaThread.currentFrameGuess() on x86. */
 704   public boolean isJavaPCDbg(Address addr) {
 705     // FIXME: this is not a complete enough set: must include areas
 706     // like vtable stubs
 707     return (getInterpreter().contains(addr) ||
 708             getCodeCache().contains(addr));
 709   }
 710 
 711   /** FIXME: figure out where to stick this */
 712   public int getInvocationEntryBCI() {
 713     return invocationEntryBCI;
 714   }
 715 
 716   /** FIXME: figure out where to stick this */
 717   public int getInvalidOSREntryBCI() {
 718     return invalidOSREntryBCI;
 719   }
 720 
 721   // FIXME: figure out where to stick this
 722   public boolean wizardMode() {
 723     return true;
 724   }
 725 
 726   public ReversePtrs getRevPtrs() {
 727     return revPtrs;
 728   }
 729 
 730   public void setRevPtrs(ReversePtrs rp) {
 731     revPtrs = rp;
 732   }
 733 
 734   // returns null, if not available.
 735   public String getVMRelease() {
 736     return vmRelease;
 737   }
 738 
 739   // returns null, if not available.
 740   public String getVMInternalInfo() {
 741     return vmInternalInfo;
 742   }
 743 
 744   public boolean isSharingEnabled() {
 745     if (sharingEnabled == null) {
 746       Flag flag = getCommandLineFlag("UseSharedSpaces");
 747       sharingEnabled = (flag == null)? Boolean.FALSE :
 748           (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 749     }
 750     return sharingEnabled.booleanValue();
 751   }
 752 
 753   public boolean isCompressedOopsEnabled() {
 754     if (compressedOopsEnabled == null) {
 755         Flag flag = getCommandLineFlag("UseCompressedOops");
 756         compressedOopsEnabled = (flag == null) ? Boolean.FALSE:
 757              (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 758     }
 759     return compressedOopsEnabled.booleanValue();
 760   }
 761 
 762   // returns null, if not available.
 763   public Flag[] getCommandLineFlags() {
 764     if (commandLineFlags == null) {
 765        readCommandLineFlags();
 766     }
 767 
 768     return commandLineFlags;
 769   }
 770 
 771   public Flag getCommandLineFlag(String name) {
 772     if (flagsMap == null) {
 773       flagsMap = new HashMap();
 774       Flag[] flags = getCommandLineFlags();
 775       for (int i = 0; i < flags.length; i++) {
 776         flagsMap.put(flags[i].getName(), flags[i]);
 777       }
 778     }
 779     return (Flag) flagsMap.get(name);
 780   }
 781 
 782   private void readCommandLineFlags() {
 783     // get command line flags
 784     TypeDataBase db = getTypeDataBase();
 785     try {
 786        Type flagType = db.lookupType("Flag");
 787        int numFlags = (int) flagType.getCIntegerField("numFlags").getValue();
 788        // NOTE: last flag contains null values.
 789        commandLineFlags = new Flag[numFlags - 1];
 790 
 791        Address flagAddr = flagType.getAddressField("flags").getValue();
 792 
 793        AddressField typeFld = flagType.getAddressField("type");
 794        AddressField nameFld = flagType.getAddressField("name");
 795        AddressField addrFld = flagType.getAddressField("addr");
 796        AddressField kindFld = flagType.getAddressField("kind");
 797 
 798        long flagSize = flagType.getSize(); // sizeof(Flag)
 799 
 800        // NOTE: last flag contains null values.
 801        for (int f = 0; f < numFlags - 1; f++) {
 802           String type = CStringUtilities.getString(typeFld.getValue(flagAddr));
 803           String name = CStringUtilities.getString(nameFld.getValue(flagAddr));
 804           Address addr = addrFld.getValue(flagAddr);
 805           String kind = CStringUtilities.getString(kindFld.getValue(flagAddr));
 806           commandLineFlags[f] = new Flag(type, name, addr, kind);
 807           flagAddr = flagAddr.addOffsetTo(flagSize);
 808        }
 809 
 810        // sort flags by name
 811        Arrays.sort(commandLineFlags, new Comparator() {
 812                                         public int compare(Object o1, Object o2) {
 813                                            Flag f1 = (Flag) o1;
 814                                            Flag f2 = (Flag) o2;
 815                                            return f1.getName().compareTo(f2.getName());
 816                                         }
 817                                      });
 818     } catch (Exception exp) {
 819        // ignore. may be older version. command line flags not available.
 820     }
 821   }
 822 
 823   public String getSystemProperty(String key) {
 824     Properties props = getSystemProperties();
 825     return (props != null)? props.getProperty(key) : null;
 826   }
 827 
 828   public Properties getSystemProperties() {
 829     if (sysProps == null) {
 830        readSystemProperties();
 831     }
 832     return sysProps;
 833   }
 834 
 835   private void readSystemProperties() {
 836      InstanceKlass systemKls = getSystemDictionary().getSystemKlass();
 837      systemKls.iterate(new DefaultOopVisitor() {
 838                                ObjectReader objReader = new ObjectReader();
 839                                public void doOop(sun.jvm.hotspot.oops.OopField field, boolean isVMField) {
 840                                   if (field.getID().getName().equals("props")) {
 841                                      try {
 842                                         sysProps = (Properties) objReader.readObject(field.getValue(getObj()));
 843                                      } catch (Exception e) {
 844                                         if (Assert.ASSERTS_ENABLED) {
 845                                            e.printStackTrace();
 846                                         }
 847                                      }
 848                                   }
 849                                }
 850                         }, false);
 851   }
 852 }