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   public long getAddressSize() {
 460     return db.getAddressSize();
 461   }
 462 
 463   public long getOopSize() {
 464     return oopSize;
 465   }
 466 
 467   public long getLogAddressSize() {
 468     return logAddressSize;
 469   }
 470 
 471   public long getIntSize() {
 472     return db.getJIntType().getSize();
 473   }
 474 
 475   /** NOTE: this offset is in BYTES in this system! */
 476   public long getStackBias() {
 477     return stackBias;
 478   }
 479 
 480   /** Indicates whether the underlying machine supports the LP64 data
 481       model. This is needed for conditionalizing code in a few places */
 482   public boolean isLP64() {
 483     if (Assert.ASSERTS_ENABLED) {
 484       Assert.that(isDebugging(), "Debugging system only for now");
 485     }
 486     return isLP64;
 487   }
 488 
 489   /** Get bytes-per-long == long/double natural alignment. */
 490   public int getBytesPerLong() {
 491     return bytesPerLong;
 492   }
 493 
 494   /** Get minimum object alignment in bytes. */
 495   public int getMinObjAlignment() {
 496     return minObjAlignmentInBytes;
 497   }
 498 
 499   public int getMinObjAlignmentInBytes() {
 500     return minObjAlignmentInBytes;
 501   }
 502   public int getLogMinObjAlignmentInBytes() {
 503     return logMinObjAlignmentInBytes;
 504   }
 505 
 506   public int getHeapWordSize() {
 507     return heapWordSize;
 508   }
 509 
 510   public int getHeapOopSize() {
 511     return heapOopSize;
 512   }
 513   /** Utility routine for getting data structure alignment correct */
 514   public long alignUp(long size, long alignment) {
 515     return (size + alignment - 1) & ~(alignment - 1);
 516   }
 517 
 518   /** Utility routine for getting data structure alignment correct */
 519   public long alignDown(long size, long alignment) {
 520     return size & ~(alignment - 1);
 521   }
 522 
 523   /** Utility routine for building an int from two "unsigned" 16-bit
 524       shorts */
 525   public int buildIntFromShorts(short low, short high) {
 526     return (((int) high) << 16) | (((int) low) & 0xFFFF);
 527   }
 528 
 529   /** Utility routine for building a long from two "unsigned" 32-bit
 530       ints in <b>platform-dependent</b> order */
 531   public long buildLongFromIntsPD(int oneHalf, int otherHalf) {
 532     if (isBigEndian) {
 533       return (((long) otherHalf) << 32) | (((long) oneHalf) & 0x00000000FFFFFFFFL);
 534     } else{
 535       return (((long) oneHalf) << 32) | (((long) otherHalf) & 0x00000000FFFFFFFFL);
 536     }
 537   }
 538 
 539   /** Indicates whether Thread-Local Allocation Buffers are used */
 540   public boolean getUseTLAB() {
 541     return useTLAB;
 542   }
 543 
 544   public TypeDataBase getTypeDataBase() {
 545     return db;
 546   }
 547 
 548   public Universe    getUniverse() {
 549     if (universe == null) {
 550       universe = new Universe();
 551     }
 552     return universe;
 553   }
 554 
 555   public ObjectHeap  getObjectHeap() {
 556     if (heap == null) {
 557       heap = new ObjectHeap(db);
 558     }
 559     return heap;
 560   }
 561 
 562   public SymbolTable getSymbolTable() {
 563     if (symbols == null) {
 564       symbols = SymbolTable.getTheTable();
 565     }
 566     return symbols;
 567   }
 568 
 569   public StringTable getStringTable() {
 570     if (strings == null) {
 571       strings = StringTable.getTheTable();
 572     }
 573     return strings;
 574   }
 575 
 576   public SystemDictionary getSystemDictionary() {
 577     if (dict == null) {
 578       dict = new SystemDictionary();
 579     }
 580     return dict;
 581   }
 582 
 583   public Threads     getThreads() {
 584     if (threads == null) {
 585       threads = new Threads();
 586     }
 587     return threads;
 588   }
 589 
 590   public ObjectSynchronizer getObjectSynchronizer() {
 591     if (synchronizer == null) {
 592       synchronizer = new ObjectSynchronizer();
 593     }
 594     return synchronizer;
 595   }
 596 
 597   public JNIHandles getJNIHandles() {
 598     if (handles == null) {
 599       handles = new JNIHandles();
 600     }
 601     return handles;
 602   }
 603 
 604   public Interpreter getInterpreter() {
 605     if (interpreter == null) {
 606       interpreter = new Interpreter();
 607     }
 608     return interpreter;
 609   }
 610 
 611   public StubRoutines getStubRoutines() {
 612     if (stubRoutines == null) {
 613       stubRoutines = new StubRoutines();
 614     }
 615     return stubRoutines;
 616   }
 617 
 618   public VMRegImpl getVMRegImplInfo() {
 619     if (vmregImpl == null) {
 620       vmregImpl = new VMRegImpl();
 621     }
 622     return vmregImpl;
 623   }
 624 
 625   public Bytes getBytes() {
 626     if (bytes == null) {
 627       bytes = new Bytes(debugger.getMachineDescription());
 628     }
 629     return bytes;
 630   }
 631 
 632   /** Returns true if this is a isBigEndian, false otherwise */
 633   public boolean isBigEndian() {
 634     return isBigEndian;
 635   }
 636 
 637   /** Returns true if this is a "core" build, false if either C1 or C2
 638       is present */
 639   public boolean isCore() {
 640     return (!(usingClientCompiler || usingServerCompiler));
 641   }
 642 
 643   /** Returns true if this is a C1 build, false otherwise */
 644   public boolean isClientCompiler() {
 645     return usingClientCompiler;
 646   }
 647 
 648   /** Returns true if this is a C2 build, false otherwise */
 649   public boolean isServerCompiler() {
 650     return usingServerCompiler;
 651   }
 652 
 653   /** Returns true if C2 derived pointer table should be used, false otherwise */
 654   public boolean useDerivedPointerTable() {
 655     return !disableDerivedPrinterTableCheck;
 656   }
 657 
 658   /** Returns the code cache; should not be used if is core build */
 659   public CodeCache getCodeCache() {
 660     if (Assert.ASSERTS_ENABLED) {
 661       Assert.that(!isCore(), "noncore builds only");
 662     }
 663     if (codeCache == null) {
 664       codeCache = new CodeCache();
 665     }
 666     return codeCache;
 667   }
 668 
 669   /** Should only be called for C1 builds */
 670   public Runtime1 getRuntime1() {
 671     if (Assert.ASSERTS_ENABLED) {
 672       Assert.that(isClientCompiler(), "C1 builds only");
 673     }
 674     if (runtime1 == null) {
 675       runtime1 = new Runtime1();
 676     }
 677     return runtime1;
 678   }
 679 
 680   /** Test to see whether we're in debugging mode (NOTE: this really
 681       should not be tested by this code; currently only used in
 682       StackFrameStream) */
 683   public boolean isDebugging() {
 684     return (debugger != null);
 685   }
 686 
 687   /** This is only used by the debugging (i.e., non-runtime) system */
 688   public JVMDebugger getDebugger() {
 689     if (debugger == null) {
 690       throw new RuntimeException("Attempt to use debugger in runtime system");
 691     }
 692     return debugger;
 693   }
 694 
 695   /** Indicates whether a given program counter is in Java code. This
 696       includes but is not spanned by the interpreter and code cache.
 697       Only used in the debugging system, for implementing
 698       JavaThread.currentFrameGuess() on x86. */
 699   public boolean isJavaPCDbg(Address addr) {
 700     // FIXME: this is not a complete enough set: must include areas
 701     // like vtable stubs
 702     return (getInterpreter().contains(addr) ||
 703             getCodeCache().contains(addr));
 704   }
 705 
 706   /** FIXME: figure out where to stick this */
 707   public int getInvocationEntryBCI() {
 708     return invocationEntryBCI;
 709   }
 710 
 711   /** FIXME: figure out where to stick this */
 712   public int getInvalidOSREntryBCI() {
 713     return invalidOSREntryBCI;
 714   }
 715 
 716   // FIXME: figure out where to stick this
 717   public boolean wizardMode() {
 718     return true;
 719   }
 720 
 721   public ReversePtrs getRevPtrs() {
 722     return revPtrs;
 723   }
 724 
 725   public void setRevPtrs(ReversePtrs rp) {
 726     revPtrs = rp;
 727   }
 728 
 729   // returns null, if not available.
 730   public String getVMRelease() {
 731     return vmRelease;
 732   }
 733 
 734   // returns null, if not available.
 735   public String getVMInternalInfo() {
 736     return vmInternalInfo;
 737   }
 738 
 739   public boolean isSharingEnabled() {
 740     if (sharingEnabled == null) {
 741       Flag flag = getCommandLineFlag("UseSharedSpaces");
 742       sharingEnabled = (flag == null)? Boolean.FALSE :
 743           (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 744     }
 745     return sharingEnabled.booleanValue();
 746   }
 747 
 748   public boolean isCompressedOopsEnabled() {
 749     if (compressedOopsEnabled == null) {
 750         Flag flag = getCommandLineFlag("UseCompressedOops");
 751         compressedOopsEnabled = (flag == null) ? Boolean.FALSE:
 752              (flag.getBool()? Boolean.TRUE: Boolean.FALSE);
 753     }
 754     return compressedOopsEnabled.booleanValue();
 755   }
 756 
 757   // returns null, if not available.
 758   public Flag[] getCommandLineFlags() {
 759     if (commandLineFlags == null) {
 760        readCommandLineFlags();
 761     }
 762 
 763     return commandLineFlags;
 764   }
 765 
 766   public Flag getCommandLineFlag(String name) {
 767     if (flagsMap == null) {
 768       flagsMap = new HashMap();
 769       Flag[] flags = getCommandLineFlags();
 770       for (int i = 0; i < flags.length; i++) {
 771         flagsMap.put(flags[i].getName(), flags[i]);
 772       }
 773     }
 774     return (Flag) flagsMap.get(name);
 775   }
 776 
 777   private void readCommandLineFlags() {
 778     // get command line flags
 779     TypeDataBase db = getTypeDataBase();
 780     try {
 781        Type flagType = db.lookupType("Flag");
 782        int numFlags = (int) flagType.getCIntegerField("numFlags").getValue();
 783        // NOTE: last flag contains null values.
 784        commandLineFlags = new Flag[numFlags - 1];
 785 
 786        Address flagAddr = flagType.getAddressField("flags").getValue();
 787 
 788        AddressField typeFld = flagType.getAddressField("type");
 789        AddressField nameFld = flagType.getAddressField("name");
 790        AddressField addrFld = flagType.getAddressField("addr");
 791        AddressField kindFld = flagType.getAddressField("kind");
 792 
 793        long flagSize = flagType.getSize(); // sizeof(Flag)
 794 
 795        // NOTE: last flag contains null values.
 796        for (int f = 0; f < numFlags - 1; f++) {
 797           String type = CStringUtilities.getString(typeFld.getValue(flagAddr));
 798           String name = CStringUtilities.getString(nameFld.getValue(flagAddr));
 799           Address addr = addrFld.getValue(flagAddr);
 800           String kind = CStringUtilities.getString(kindFld.getValue(flagAddr));
 801           commandLineFlags[f] = new Flag(type, name, addr, kind);
 802           flagAddr = flagAddr.addOffsetTo(flagSize);
 803        }
 804 
 805        // sort flags by name
 806        Arrays.sort(commandLineFlags, new Comparator() {
 807                                         public int compare(Object o1, Object o2) {
 808                                            Flag f1 = (Flag) o1;
 809                                            Flag f2 = (Flag) o2;
 810                                            return f1.getName().compareTo(f2.getName());
 811                                         }
 812                                      });
 813     } catch (Exception exp) {
 814        // ignore. may be older version. command line flags not available.
 815     }
 816   }
 817 
 818   public String getSystemProperty(String key) {
 819     Properties props = getSystemProperties();
 820     return (props != null)? props.getProperty(key) : null;
 821   }
 822 
 823   public Properties getSystemProperties() {
 824     if (sysProps == null) {
 825        readSystemProperties();
 826     }
 827     return sysProps;
 828   }
 829 
 830   private void readSystemProperties() {
 831      InstanceKlass systemKls = getSystemDictionary().getSystemKlass();
 832      systemKls.iterate(new DefaultOopVisitor() {
 833                                ObjectReader objReader = new ObjectReader();
 834                                public void doOop(sun.jvm.hotspot.oops.OopField field, boolean isVMField) {
 835                                   if (field.getID().getName().equals("props")) {
 836                                      try {
 837                                         sysProps = (Properties) objReader.readObject(field.getValue(getObj()));
 838                                      } catch (Exception e) {
 839                                         if (Assert.ASSERTS_ENABLED) {
 840                                            e.printStackTrace();
 841                                         }
 842                                      }
 843                                   }
 844                                }
 845                         }, false);
 846   }
 847 }