1 /*
   2  * Copyright 2000-2008 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     heapOopSize  = db.lookupIntConstant("heapOopSize").intValue();
 322 
 323     intxType = db.lookupType("intx");
 324     uintxType = db.lookupType("uintx");
 325     boolType = (CIntegerType) db.lookupType("bool");
 326   }
 327 
 328   /** This could be used by a reflective runtime system */
 329   public static void initialize(TypeDataBase db, boolean isBigEndian) {
 330     if (soleInstance != null) {
 331       throw new RuntimeException("Attempt to initialize VM twice");
 332     }
 333     soleInstance = new VM(db, null, isBigEndian);
 334     for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) {
 335       ((Observer) iter.next()).update(null, null);
 336     }
 337   }
 338 
 339   /** This is used by the debugging system */
 340   public static void initialize(TypeDataBase db, JVMDebugger debugger) {
 341     if (soleInstance != null) {
 342       throw new RuntimeException("Attempt to initialize VM twice");
 343     }
 344     soleInstance = new VM(db, debugger, debugger.getMachineDescription().isBigEndian());
 345     debugger.putHeapConst(Universe.getHeapBase(), soleInstance.getHeapOopSize(),
 346                           soleInstance.logMinObjAlignmentInBytes);
 347     for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) {
 348       ((Observer) iter.next()).update(null, null);
 349     }
 350   }
 351 
 352   /** This is used by the debugging system */
 353   public static void shutdown() {
 354     soleInstance = null;
 355   }
 356 
 357   /** This is used by both the debugger and any runtime system. It is
 358       the basic mechanism by which classes which mimic underlying VM
 359       functionality cause themselves to be initialized. The given
 360       observer will be notified (with arguments (null, null)) when the
 361       VM is re-initialized, as well as when it registers itself with
 362       the VM. */
 363   public static void registerVMInitializedObserver(Observer o) {
 364     vmInitializedObservers.add(o);
 365     o.update(null, null);
 366   }
 367 
 368   /** This is the primary accessor used by both the debugger and any
 369       potential runtime system */
 370   public static VM getVM() {
 371     if (soleInstance == null) {
 372       throw new RuntimeException("VM.initialize() was not yet called");
 373     }
 374     return soleInstance;
 375   }
 376 
 377   /** This is only used by the debugging system. The given observer
 378       will be notified if the underlying VM resumes execution. NOTE
 379       that the given observer is not triggered if the VM is currently
 380       running and therefore differs in behavior from {@link
 381       #registerVMInitializedObserver} (because of the possibility of
 382       race conditions if the observer is added while the VM is being
 383       suspended or resumed).  */
 384   public void registerVMResumedObserver(Observer o) {
 385     vmResumedObservers.add(o);
 386   }
 387 
 388   /** This is only used by the debugging system. The given observer
 389       will be notified if the underlying VM suspends execution. NOTE
 390       that the given observer is not triggered if the VM is currently
 391       suspended and therefore differs in behavior from {@link
 392       #registerVMInitializedObserver} (because of the possibility of
 393       race conditions if the observer is added while the VM is being
 394       suspended or resumed).  */
 395   public void registerVMSuspendedObserver(Observer o) {
 396     vmSuspendedObservers.add(o);
 397   }
 398 
 399   /** This is only used by the debugging system. Informs all
 400       registered resumption observers that the VM has been resumed.
 401       The application is responsible for actually having performed the
 402       resumption. No OopHandles must be used after this point, as they
 403       may move in the target address space due to garbage
 404       collection. */
 405   public void fireVMResumed() {
 406     for (Iterator iter = vmResumedObservers.iterator(); iter.hasNext(); ) {
 407       ((Observer) iter.next()).update(null, null);
 408     }
 409   }
 410 
 411   /** This is only used by the debugging system. Informs all
 412       registered suspension observers that the VM has been suspended.
 413       The application is responsible for actually having performed the
 414       suspension. Garbage collection must be forbidden at this point;
 415       for example, a JPDA-level suspension is not adequate since the
 416       VM thread may still be running. */
 417   public void fireVMSuspended() {
 418     for (Iterator iter = vmSuspendedObservers.iterator(); iter.hasNext(); ) {
 419       ((Observer) iter.next()).update(null, null);
 420     }
 421   }
 422 
 423   /** Returns the OS this VM is running on. Notice that by delegating
 424       to the debugger we can transparently support remote
 425       debugging. */
 426   public String getOS() {
 427     if (debugger != null) {
 428       return debugger.getOS();
 429     }
 430     return PlatformInfo.getOS();
 431   }
 432 
 433   /** Returns the CPU this VM is running on. Notice that by delegating
 434       to the debugger we can transparently support remote
 435       debugging. */
 436   public String getCPU() {
 437     if (debugger != null) {
 438       return debugger.getCPU();
 439     }
 440     return PlatformInfo.getCPU();
 441   }
 442 
 443   public Type lookupType(String cTypeName) {
 444     return db.lookupType(cTypeName);
 445   }
 446 
 447   public Integer lookupIntConstant(String name) {
 448     return db.lookupIntConstant(name);
 449   }
 450 
 451   public long getAddressSize() {
 452     return db.getAddressSize();
 453   }
 454 
 455   public long getOopSize() {
 456     return oopSize;
 457   }
 458 
 459   public long getLogAddressSize() {
 460     return logAddressSize;
 461   }
 462 
 463   public long getIntSize() {
 464     return db.getJIntType().getSize();
 465   }
 466 
 467   /** NOTE: this offset is in BYTES in this system! */
 468   public long getStackBias() {
 469     return stackBias;
 470   }
 471 
 472   /** Indicates whether the underlying machine supports the LP64 data
 473       model. This is needed for conditionalizing code in a few places */
 474   public boolean isLP64() {
 475     if (Assert.ASSERTS_ENABLED) {
 476       Assert.that(isDebugging(), "Debugging system only for now");
 477     }
 478     return isLP64;
 479   }
 480 
 481   /** Get bytes-per-long == long/double natural alignment. */
 482   public int getBytesPerLong() {
 483     return bytesPerLong;
 484   }
 485 
 486   /** Get minimum object alignment in bytes. */
 487   public int getMinObjAlignment() {
 488     return minObjAlignmentInBytes;
 489   }
 490 
 491   public int getMinObjAlignmentInBytes() {
 492     return minObjAlignmentInBytes;
 493   }
 494   public int getLogMinObjAlignmentInBytes() {
 495     return logMinObjAlignmentInBytes;
 496   }
 497 
 498   public int getHeapWordSize() {
 499     return heapWordSize;
 500   }
 501 
 502   public int getHeapOopSize() {
 503     return heapOopSize;
 504   }
 505   /** Utility routine for getting data structure alignment correct */
 506   public long alignUp(long size, long alignment) {
 507     return (size + alignment - 1) & ~(alignment - 1);
 508   }
 509 
 510   /** Utility routine for getting data structure alignment correct */
 511   public long alignDown(long size, long alignment) {
 512     return size & ~(alignment - 1);
 513   }
 514 
 515   /** Utility routine for building an int from two "unsigned" 16-bit
 516       shorts */
 517   public int buildIntFromShorts(short low, short high) {
 518     return (((int) high) << 16) | (((int) low) & 0xFFFF);
 519   }
 520 
 521   /** Utility routine for building a long from two "unsigned" 32-bit
 522       ints in <b>platform-dependent</b> order */
 523   public long buildLongFromIntsPD(int oneHalf, int otherHalf) {
 524     if (isBigEndian) {
 525       return (((long) otherHalf) << 32) | (((long) oneHalf) & 0x00000000FFFFFFFFL);
 526     } else{
 527       return (((long) oneHalf) << 32) | (((long) otherHalf) & 0x00000000FFFFFFFFL);
 528     }
 529   }
 530 
 531   /** Indicates whether Thread-Local Allocation Buffers are used */
 532   public boolean getUseTLAB() {
 533     return useTLAB;
 534   }
 535 
 536   public TypeDataBase getTypeDataBase() {
 537     return db;
 538   }
 539 
 540   public Universe    getUniverse() {
 541     if (universe == null) {
 542       universe = new Universe();
 543     }
 544     return universe;
 545   }
 546 
 547   public ObjectHeap  getObjectHeap() {
 548     if (heap == null) {
 549       heap = new ObjectHeap(db);
 550     }
 551     return heap;
 552   }
 553 
 554   public SymbolTable getSymbolTable() {
 555     if (symbols == null) {
 556       symbols = SymbolTable.getTheTable();
 557     }
 558     return symbols;
 559   }
 560 
 561   public StringTable getStringTable() {
 562     if (strings == null) {
 563       strings = StringTable.getTheTable();
 564     }
 565     return strings;
 566   }
 567 
 568   public SystemDictionary getSystemDictionary() {
 569     if (dict == null) {
 570       dict = new SystemDictionary();
 571     }
 572     return dict;
 573   }
 574 
 575   public Threads     getThreads() {
 576     if (threads == null) {
 577       threads = new Threads();
 578     }
 579     return threads;
 580   }
 581 
 582   public ObjectSynchronizer getObjectSynchronizer() {
 583     if (synchronizer == null) {
 584       synchronizer = new ObjectSynchronizer();
 585     }
 586     return synchronizer;
 587   }
 588 
 589   public JNIHandles getJNIHandles() {
 590     if (handles == null) {
 591       handles = new JNIHandles();
 592     }
 593     return handles;
 594   }
 595 
 596   public Interpreter getInterpreter() {
 597     if (interpreter == null) {
 598       interpreter = new Interpreter();
 599     }
 600     return interpreter;
 601   }
 602 
 603   public StubRoutines getStubRoutines() {
 604     if (stubRoutines == null) {
 605       stubRoutines = new StubRoutines();
 606     }
 607     return stubRoutines;
 608   }
 609 
 610   public VMRegImpl getVMRegImplInfo() {
 611     if (vmregImpl == null) {
 612       vmregImpl = new VMRegImpl();
 613     }
 614     return vmregImpl;
 615   }
 616 
 617   public Bytes getBytes() {
 618     if (bytes == null) {
 619       bytes = new Bytes(debugger.getMachineDescription());
 620     }
 621     return bytes;
 622   }
 623 
 624   /** Returns true if this is a isBigEndian, false otherwise */
 625   public boolean isBigEndian() {
 626     return isBigEndian;
 627   }
 628 
 629   /** Returns true if this is a "core" build, false if either C1 or C2
 630       is present */
 631   public boolean isCore() {
 632     return (!(usingClientCompiler || usingServerCompiler));
 633   }
 634 
 635   /** Returns true if this is a C1 build, false otherwise */
 636   public boolean isClientCompiler() {
 637     return usingClientCompiler;
 638   }
 639 
 640   /** Returns true if this is a C2 build, false otherwise */
 641   public boolean isServerCompiler() {
 642     return usingServerCompiler;
 643   }
 644 
 645   /** Returns true if C2 derived pointer table should be used, false otherwise */
 646   public boolean useDerivedPointerTable() {
 647     return !disableDerivedPrinterTableCheck;
 648   }
 649 
 650   /** Returns the code cache; should not be used if is core build */
 651   public CodeCache getCodeCache() {
 652     if (Assert.ASSERTS_ENABLED) {
 653       Assert.that(!isCore(), "noncore builds only");
 654     }
 655     if (codeCache == null) {
 656       codeCache = new CodeCache();
 657     }
 658     return codeCache;
 659   }
 660 
 661   /** Should only be called for C1 builds */
 662   public Runtime1 getRuntime1() {
 663     if (Assert.ASSERTS_ENABLED) {
 664       Assert.that(isClientCompiler(), "C1 builds only");
 665     }
 666     if (runtime1 == null) {
 667       runtime1 = new Runtime1();
 668     }
 669     return runtime1;
 670   }
 671 
 672   /** Test to see whether we're in debugging mode (NOTE: this really
 673       should not be tested by this code; currently only used in
 674       StackFrameStream) */
 675   public boolean isDebugging() {
 676     return (debugger != null);
 677   }
 678 
 679   /** This is only used by the debugging (i.e., non-runtime) system */
 680   public JVMDebugger getDebugger() {
 681     if (debugger == null) {
 682       throw new RuntimeException("Attempt to use debugger in runtime system");
 683     }
 684     return debugger;
 685   }
 686 
 687   /** Indicates whether a given program counter is in Java code. This
 688       includes but is not spanned by the interpreter and code cache.
 689       Only used in the debugging system, for implementing
 690       JavaThread.currentFrameGuess() on x86. */
 691   public boolean isJavaPCDbg(Address addr) {
 692     // FIXME: this is not a complete enough set: must include areas
 693     // like vtable stubs
 694     return (getInterpreter().contains(addr) ||
 695             getCodeCache().contains(addr));
 696   }
 697 
 698   /** FIXME: figure out where to stick this */
 699   public int getInvocationEntryBCI() {
 700     return invocationEntryBCI;
 701   }
 702 
 703   /** FIXME: figure out where to stick this */
 704   public int getInvalidOSREntryBCI() {
 705     return invalidOSREntryBCI;
 706   }
 707 
 708   // FIXME: figure out where to stick this
 709   public boolean wizardMode() {
 710     return true;
 711   }
 712 
 713   public ReversePtrs getRevPtrs() {
 714     return revPtrs;
 715   }
 716 
 717   public void setRevPtrs(ReversePtrs rp) {
 718     revPtrs = rp;
 719   }
 720 
 721   // returns null, if not available.
 722   public String getVMRelease() {
 723     return vmRelease;
 724   }
 725 
 726   // returns null, if not available.
 727   public String getVMInternalInfo() {
 728     return vmInternalInfo;
 729   }
 730 
 731   public boolean isSharingEnabled() {
 732     if (sharingEnabled == null) {
 733       Flag flag = getCommandLineFlag("UseSharedSpaces");
 734       sharingEnabled = (flag == null)? Boolean.FALSE :
 735           (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 736     }
 737     return sharingEnabled.booleanValue();
 738   }
 739 
 740   public boolean isCompressedOopsEnabled() {
 741     if (compressedOopsEnabled == null) {
 742         Flag flag = getCommandLineFlag("UseCompressedOops");
 743         compressedOopsEnabled = (flag == null) ? Boolean.FALSE:
 744              (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 745     }
 746     return compressedOopsEnabled.booleanValue();
 747   }
 748 
 749   // returns null, if not available.
 750   public Flag[] getCommandLineFlags() {
 751     if (commandLineFlags == null) {
 752        readCommandLineFlags();
 753     }
 754 
 755     return commandLineFlags;
 756   }
 757 
 758   public Flag getCommandLineFlag(String name) {
 759     if (flagsMap == null) {
 760       flagsMap = new HashMap();
 761       Flag[] flags = getCommandLineFlags();
 762       for (int i = 0; i < flags.length; i++) {
 763         flagsMap.put(flags[i].getName(), flags[i]);
 764       }
 765     }
 766     return (Flag) flagsMap.get(name);
 767   }
 768 
 769   private void readCommandLineFlags() {
 770     // get command line flags
 771     TypeDataBase db = getTypeDataBase();
 772     try {
 773        Type flagType = db.lookupType("Flag");
 774        int numFlags = (int) flagType.getCIntegerField("numFlags").getValue();
 775        // NOTE: last flag contains null values.
 776        commandLineFlags = new Flag[numFlags - 1];
 777 
 778        Address flagAddr = flagType.getAddressField("flags").getValue();
 779 
 780        AddressField typeFld = flagType.getAddressField("type");
 781        AddressField nameFld = flagType.getAddressField("name");
 782        AddressField addrFld = flagType.getAddressField("addr");
 783        AddressField kindFld = flagType.getAddressField("kind");
 784 
 785        long flagSize = flagType.getSize(); // sizeof(Flag)
 786 
 787        // NOTE: last flag contains null values.
 788        for (int f = 0; f < numFlags - 1; f++) {
 789           String type = CStringUtilities.getString(typeFld.getValue(flagAddr));
 790           String name = CStringUtilities.getString(nameFld.getValue(flagAddr));
 791           Address addr = addrFld.getValue(flagAddr);
 792           String kind = CStringUtilities.getString(kindFld.getValue(flagAddr));
 793           commandLineFlags[f] = new Flag(type, name, addr, kind);
 794           flagAddr = flagAddr.addOffsetTo(flagSize);
 795        }
 796 
 797        // sort flags by name
 798        Arrays.sort(commandLineFlags, new Comparator() {
 799                                         public int compare(Object o1, Object o2) {
 800                                            Flag f1 = (Flag) o1;
 801                                            Flag f2 = (Flag) o2;
 802                                            return f1.getName().compareTo(f2.getName());
 803                                         }
 804                                      });
 805     } catch (Exception exp) {
 806        // ignore. may be older version. command line flags not available.
 807     }
 808   }
 809 
 810   public String getSystemProperty(String key) {
 811     Properties props = getSystemProperties();
 812     return (props != null)? props.getProperty(key) : null;
 813   }
 814 
 815   public Properties getSystemProperties() {
 816     if (sysProps == null) {
 817        readSystemProperties();
 818     }
 819     return sysProps;
 820   }
 821 
 822   private void readSystemProperties() {
 823      InstanceKlass systemKls = getSystemDictionary().getSystemKlass();
 824      systemKls.iterate(new DefaultOopVisitor() {
 825                                ObjectReader objReader = new ObjectReader();
 826                                public void doOop(sun.jvm.hotspot.oops.OopField field, boolean isVMField) {
 827                                   if (field.getID().getName().equals("props")) {
 828                                      try {
 829                                         sysProps = (Properties) objReader.readObject(field.getValue(getObj()));
 830                                      } catch (Exception e) {
 831                                         if (Assert.ASSERTS_ENABLED) {
 832                                            e.printStackTrace();
 833                                         }
 834                                      }
 835                                   }
 836                                }
 837                         }, false);
 838   }
 839 }