1 /*
   2  * Copyright 2001-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.debugger;
  26 
  27 /** <P> DebuggerBase is a recommended base class for debugger
  28     implementations. It can use a PageCache to cache data from the
  29     target process. Note that this class would not be suitable if the
  30     system were used to reflect upon itself; it would never be safe to
  31     store the value in an OopHandle in anything but an OopHandle.
  32     However, it provides a fair amount of code sharing to the current
  33     dbx and win32 implementations. </P>
  34 
  35     <P> NOTE that much of the code sharing is achieved by having this
  36     class implement many of the methods in the Win32Debugger and
  37     DbxDebugger interfaces. </P> */
  38 
  39 public abstract class DebuggerBase implements Debugger {
  40 
  41   // May be set lazily, but must be set before calling any of the read
  42   // routines below
  43   protected MachineDescription machDesc;
  44   protected DebuggerUtilities utils;
  45   // Java primitive type sizes, set during bootstrapping. Do not call
  46   // any of the Java read routines until these are set up.
  47   protected long jbooleanSize;
  48   protected long jbyteSize;
  49   protected long jcharSize;
  50   protected long jdoubleSize;
  51   protected long jfloatSize;
  52   protected long jintSize;
  53   protected long jlongSize;
  54   protected long jshortSize;
  55   protected boolean javaPrimitiveTypesConfigured;
  56   // heap data.
  57   protected long oopSize;
  58   protected long heapOopSize;
  59   protected long narrowOopBase;  // heap base for compressed oops.
  60   protected int  narrowOopShift; // shift to decode compressed oops.
  61   // Should be initialized if desired by calling initCache()
  62   private PageCache cache;
  63 
  64   // State for faster accessors that don't allocate memory on each read
  65   private boolean useFastAccessors;
  66   private boolean bigEndian;
  67 
  68   // Page-fetching functionality for LRU cache
  69   class Fetcher implements PageFetcher {
  70     public Page fetchPage(long pageBaseAddress, long numBytes) {
  71       // This assumes that if any byte is unmapped, that the entire
  72       // page is. The common case, however, is that the page is
  73       // mapped, so we always fetch the entire thing all at once to
  74       // avoid two round-trip communications per page fetch, even
  75       // though fetching of unmapped pages will be slow.
  76       ReadResult res = readBytesFromProcess(pageBaseAddress, numBytes);
  77       if (res.getData() == null) {
  78         return new Page(pageBaseAddress, numBytes);
  79       }
  80       return new Page(pageBaseAddress, res.getData());
  81     }
  82   }
  83 
  84   protected DebuggerBase() {
  85   }
  86 
  87   /** From the JVMDebugger interface. This is the only public method
  88       of this class. */
  89   public void configureJavaPrimitiveTypeSizes(long jbooleanSize,
  90                                               long jbyteSize,
  91                                               long jcharSize,
  92                                               long jdoubleSize,
  93                                               long jfloatSize,
  94                                               long jintSize,
  95                                               long jlongSize,
  96                                               long jshortSize) {
  97     this.jbooleanSize = jbooleanSize;
  98     this.jbyteSize = jbyteSize;
  99     this.jcharSize = jcharSize;
 100     this.jdoubleSize = jdoubleSize;
 101     this.jfloatSize = jfloatSize;
 102     this.jintSize = jintSize;
 103     this.jlongSize = jlongSize;
 104     this.jshortSize = jshortSize;
 105 
 106     if (jbooleanSize < 1) {
 107       throw new RuntimeException("jboolean size is too small");
 108     }
 109 
 110     if (jbyteSize < 1) {
 111       throw new RuntimeException("jbyte size is too small");
 112     }
 113 
 114     if (jcharSize < 2) {
 115       throw new RuntimeException("jchar size is too small");
 116     }
 117 
 118     if (jdoubleSize < 8) {
 119       throw new RuntimeException("jdouble size is too small");
 120     }
 121 
 122     if (jfloatSize < 4) {
 123       throw new RuntimeException("jfloat size is too small");
 124     }
 125 
 126     if (jintSize < 4) {
 127       throw new RuntimeException("jint size is too small");
 128     }
 129 
 130     if (jlongSize < 8) {
 131       throw new RuntimeException("jlong size is too small");
 132     }
 133 
 134     if (jshortSize < 2) {
 135       throw new RuntimeException("jshort size is too small");
 136     }
 137 
 138     if (jintSize != jfloatSize) {
 139       // If dataToJFloat were rewritten, this wouldn't be necessary
 140       throw new RuntimeException("jint size and jfloat size must be equal");
 141     }
 142 
 143     if (jlongSize != jdoubleSize) {
 144       // If dataToJDouble were rewritten, this wouldn't be necessary
 145       throw new RuntimeException("jlong size and jdouble size must be equal");
 146     }
 147 
 148     useFastAccessors =
 149       ((cache != null) &&
 150        (jbooleanSize == 1) &&
 151        (jbyteSize    == 1) &&
 152        (jcharSize    == 2) &&
 153        (jdoubleSize  == 8) &&
 154        (jfloatSize   == 4) &&
 155        (jintSize     == 4) &&
 156        (jlongSize    == 8) &&
 157        (jshortSize   == 2));
 158 
 159     javaPrimitiveTypesConfigured = true;
 160   }
 161 
 162   public void putHeapConst(long heapOopSize, long narrowOopBase, int narrowOopShift) {
 163     this.heapOopSize = heapOopSize;
 164     this.narrowOopBase = narrowOopBase;
 165     this.narrowOopShift = narrowOopShift;
 166   }
 167 
 168   /** May be called by subclasses if desired to initialize the page
 169       cache but may not be overridden */
 170   protected final void initCache(long pageSize, long maxNumPages) {
 171     cache = new PageCache(pageSize, maxNumPages, new Fetcher());
 172     if (machDesc != null) {
 173       bigEndian = machDesc.isBigEndian();
 174     }
 175   }
 176 
 177   /** May be called by subclasses if needed (if the machine
 178       description is not available at the time of cache
 179       initialization, as on Solaris) but may not be overridden */
 180   protected final void setBigEndian(boolean bigEndian) {
 181     this.bigEndian = bigEndian;
 182   }
 183 
 184   /** May be called by subclasses to clear out the cache but may not
 185       be overridden. For convenience, this can be called even if the
 186       cache has not been initialized. */
 187   protected final void clearCache() {
 188     if (cache != null) {
 189       cache.clear();
 190     }
 191   }
 192 
 193   /** May be called by subclasses to disable the cache (for example,
 194       when the target process has been resumed) but may not be
 195       overridden. For convenience, this can be called even if the
 196       cache has not been initialized. */
 197   protected final void disableCache() {
 198     if (cache != null) {
 199       cache.disable();
 200     }
 201   }
 202 
 203   /** May be called by subclasses to re-enable the cache (for example,
 204       when the target process has been suspended) but may not be
 205       overridden. For convenience, this can be called even if the
 206       cache has not been initialized. */
 207   protected final void enableCache() {
 208     if (cache != null) {
 209       cache.enable();
 210     }
 211   }
 212 
 213   /** May be called by subclasses directly but may not be overridden */
 214   protected final byte[] readBytes(long address, long numBytes)
 215     throws UnmappedAddressException, DebuggerException {
 216     if (cache != null) {
 217       return cache.getData(address, numBytes);
 218     } else {
 219       ReadResult res = readBytesFromProcess(address, numBytes);
 220       if (res.getData() != null) {
 221         return res.getData();
 222       }
 223       throw new UnmappedAddressException(res.getFailureAddress());
 224     }
 225   }
 226 
 227   /** May be called by subclasses directly but may not be overridden */
 228   protected final void writeBytes(long address, long numBytes, byte[] data)
 229     throws UnmappedAddressException, DebuggerException {
 230     if (cache != null) {
 231       cache.clear(address, numBytes);
 232     }
 233     writeBytesToProcess(address, numBytes, data);
 234   }
 235 
 236   public boolean readJBoolean(long address)
 237     throws UnmappedAddressException, UnalignedAddressException {
 238     checkJavaConfigured();
 239     utils.checkAlignment(address, jbooleanSize);
 240     if (useFastAccessors) {
 241       return (cache.getByte(address) != 0);
 242     } else {
 243       byte[] data = readBytes(address, jbooleanSize);
 244       return utils.dataToJBoolean(data, jbooleanSize);
 245     }
 246   }
 247 
 248   public byte readJByte(long address)
 249     throws UnmappedAddressException, UnalignedAddressException {
 250     checkJavaConfigured();
 251     utils.checkAlignment(address, jbyteSize);
 252     if (useFastAccessors) {
 253       return cache.getByte(address);
 254     } else {
 255       byte[] data = readBytes(address, jbyteSize);
 256       return utils.dataToJByte(data, jbyteSize);
 257     }
 258   }
 259 
 260   // NOTE: assumes value does not span pages (may be bad assumption on
 261   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
 262   public char readJChar(long address)
 263     throws UnmappedAddressException, UnalignedAddressException {
 264     checkJavaConfigured();
 265     utils.checkAlignment(address, jcharSize);
 266     if (useFastAccessors) {
 267       return cache.getChar(address, bigEndian);
 268     } else {
 269       byte[] data = readBytes(address, jcharSize);
 270       return (char) utils.dataToJChar(data, jcharSize);
 271     }
 272   }
 273 
 274   // NOTE: assumes value does not span pages (may be bad assumption on
 275   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
 276   public double readJDouble(long address)
 277     throws UnmappedAddressException, UnalignedAddressException {
 278     checkJavaConfigured();
 279     utils.checkAlignment(address, jdoubleSize);
 280     if (useFastAccessors) {
 281       return cache.getDouble(address, bigEndian);
 282     } else {
 283       byte[] data = readBytes(address, jdoubleSize);
 284       return utils.dataToJDouble(data, jdoubleSize);
 285     }
 286   }
 287 
 288   // NOTE: assumes value does not span pages (may be bad assumption on
 289   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
 290   public float readJFloat(long address)
 291     throws UnmappedAddressException, UnalignedAddressException {
 292     checkJavaConfigured();
 293     utils.checkAlignment(address, jfloatSize);
 294     if (useFastAccessors) {
 295       return cache.getFloat(address, bigEndian);
 296     } else {
 297       byte[] data = readBytes(address, jfloatSize);
 298       return utils.dataToJFloat(data, jfloatSize);
 299     }
 300   }
 301 
 302   // NOTE: assumes value does not span pages (may be bad assumption on
 303   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
 304   public int readJInt(long address)
 305     throws UnmappedAddressException, UnalignedAddressException {
 306     checkJavaConfigured();
 307     utils.checkAlignment(address, jintSize);
 308     if (useFastAccessors) {
 309       return cache.getInt(address, bigEndian);
 310     } else {
 311       byte[] data = readBytes(address, jintSize);
 312       return utils.dataToJInt(data, jintSize);
 313     }
 314   }
 315 
 316   // NOTE: assumes value does not span pages (may be bad assumption on
 317   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
 318   public long readJLong(long address)
 319     throws UnmappedAddressException, UnalignedAddressException {
 320     checkJavaConfigured();
 321     utils.checkAlignment(address, jlongSize);
 322     if (useFastAccessors) {
 323       return cache.getLong(address, bigEndian);
 324     } else {
 325       byte[] data = readBytes(address, jlongSize);
 326       return utils.dataToJLong(data, jlongSize);
 327     }
 328   }
 329 
 330   // NOTE: assumes value does not span pages (may be bad assumption on
 331   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
 332   public short readJShort(long address)
 333     throws UnmappedAddressException, UnalignedAddressException {
 334     checkJavaConfigured();
 335     utils.checkAlignment(address, jshortSize);
 336     if (useFastAccessors) {
 337       return cache.getShort(address, bigEndian);
 338     } else {
 339       byte[] data = readBytes(address, jshortSize);
 340       return utils.dataToJShort(data, jshortSize);
 341     }
 342   }
 343 
 344   // NOTE: assumes value does not span pages (may be bad assumption on
 345   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
 346   public long readCInteger(long address, long numBytes, boolean isUnsigned)
 347     throws UnmappedAddressException, UnalignedAddressException {
 348     checkConfigured();
 349     utils.checkAlignment(address, numBytes);
 350     if (useFastAccessors) {
 351       if (isUnsigned) {
 352         switch((int) numBytes) {
 353         case 1: return cache.getByte(address) & 0xFF;
 354         case 2: return cache.getShort(address, bigEndian) & 0xFFFF;
 355         case 4: return cache.getInt(address, bigEndian) & 0xFFFFFFFFL;
 356         case 8: return cache.getLong(address, bigEndian);
 357         default: {
 358           byte[] data = readBytes(address, numBytes);
 359           return utils.dataToCInteger(data, isUnsigned);
 360         }
 361         }
 362       } else {
 363         switch((int) numBytes) {
 364         case 1: return cache.getByte(address);
 365         case 2: return cache.getShort(address, bigEndian);
 366         case 4: return cache.getInt(address, bigEndian);
 367         case 8: return cache.getLong(address, bigEndian);
 368         default: {
 369           byte[] data = readBytes(address, numBytes);
 370           return utils.dataToCInteger(data, isUnsigned);
 371         }
 372         }
 373       }
 374     } else {
 375       byte[] data = readBytes(address, numBytes);
 376       return utils.dataToCInteger(data, isUnsigned);
 377     }
 378   }
 379 
 380   public void writeJBoolean(long address, boolean value)
 381     throws UnmappedAddressException, UnalignedAddressException {
 382     checkJavaConfigured();
 383     utils.checkAlignment(address, jbooleanSize);
 384     byte[] data = utils.jbooleanToData(value);
 385     writeBytes(address, jbooleanSize, data);
 386   }
 387 
 388   public void writeJByte(long address, byte value)
 389     throws UnmappedAddressException, UnalignedAddressException {
 390     checkJavaConfigured();
 391     utils.checkAlignment(address, jbyteSize);
 392     byte[] data = utils.jbyteToData(value);
 393     writeBytes(address, jbyteSize, data);
 394   }
 395 
 396   public void writeJChar(long address, char value)
 397     throws UnmappedAddressException, UnalignedAddressException {
 398     checkJavaConfigured();
 399     utils.checkAlignment(address, jcharSize);
 400     byte[] data = utils.jcharToData(value);
 401     writeBytes(address, jcharSize, data);
 402   }
 403 
 404   public void writeJDouble(long address, double value)
 405     throws UnmappedAddressException, UnalignedAddressException {
 406     checkJavaConfigured();
 407     utils.checkAlignment(address, jdoubleSize);
 408     byte[] data = utils.jdoubleToData(value);
 409     writeBytes(address, jdoubleSize, data);
 410   }
 411 
 412   public void writeJFloat(long address, float value)
 413     throws UnmappedAddressException, UnalignedAddressException {
 414     checkJavaConfigured();
 415     utils.checkAlignment(address, jfloatSize);
 416     byte[] data = utils.jfloatToData(value);
 417     writeBytes(address, jfloatSize, data);
 418   }
 419 
 420   public void writeJInt(long address, int value)
 421     throws UnmappedAddressException, UnalignedAddressException {
 422     checkJavaConfigured();
 423     utils.checkAlignment(address, jintSize);
 424     byte[] data = utils.jintToData(value);
 425     writeBytes(address, jintSize, data);
 426   }
 427 
 428   public void writeJLong(long address, long value)
 429     throws UnmappedAddressException, UnalignedAddressException {
 430     checkJavaConfigured();
 431     utils.checkAlignment(address, jlongSize);
 432     byte[] data = utils.jlongToData(value);
 433     writeBytes(address, jlongSize, data);
 434   }
 435 
 436   public void writeJShort(long address, short value)
 437     throws UnmappedAddressException, UnalignedAddressException {
 438     checkJavaConfigured();
 439     utils.checkAlignment(address, jshortSize);
 440     byte[] data = utils.jshortToData(value);
 441     writeBytes(address, jshortSize, data);
 442   }
 443 
 444   public void writeCInteger(long address, long numBytes, long value)
 445     throws UnmappedAddressException, UnalignedAddressException {
 446     checkConfigured();
 447     utils.checkAlignment(address, numBytes);
 448     byte[] data = utils.cIntegerToData(numBytes, value);
 449     writeBytes(address, numBytes, data);
 450   }
 451 
 452   protected long readAddressValue(long address)
 453     throws UnmappedAddressException, UnalignedAddressException {
 454     return readCInteger(address, machDesc.getAddressSize(), true);
 455   }
 456 
 457   protected long readCompOopAddressValue(long address)
 458     throws UnmappedAddressException, UnalignedAddressException {
 459     long value = readCInteger(address, getHeapOopSize(), true);
 460     if (value != 0) {
 461       // See oop.inline.hpp decode_heap_oop
 462       value = (long)(narrowOopBase + (long)(value << narrowOopShift));
 463     }
 464     return value;
 465   }
 466 
 467   protected void writeAddressValue(long address, long value)
 468     throws UnmappedAddressException, UnalignedAddressException {
 469     writeCInteger(address, machDesc.getAddressSize(), value);
 470   }
 471 
 472   /** Can be called by subclasses but can not be overridden */
 473   protected final void checkConfigured() {
 474     if (machDesc == null) {
 475       throw new RuntimeException("MachineDescription must have been set by this point");
 476     }
 477     if (utils == null) {
 478       throw new RuntimeException("DebuggerUtilities must have been set by this point");
 479     }
 480   }
 481 
 482   /** Can be called by subclasses but can not be overridden */
 483   protected final void checkJavaConfigured() {
 484     checkConfigured();
 485 
 486     if (!javaPrimitiveTypesConfigured) {
 487       throw new RuntimeException("Java primitive type sizes have not yet been configured");
 488     }
 489   }
 490 
 491   /** Possibly override page cache size with user-specified property */
 492   protected int parseCacheNumPagesProperty(int defaultNum) {
 493     String cacheNumPagesString = System.getProperty("cacheNumPages");
 494     if (cacheNumPagesString != null) {
 495       try {
 496         return Integer.parseInt(cacheNumPagesString);
 497       } catch (Exception e) {
 498         System.err.println("Error parsing cacheNumPages property:");
 499         e.printStackTrace();
 500       }
 501     }
 502     return defaultNum;
 503   }
 504 
 505   /** Interim solution for allowing subclasses to write bytes to
 506       process until we make that functionality available in the basic
 507       Address interface */
 508   protected void invalidatePageCache(long startAddress, long numBytes) {
 509     cache.clear(startAddress, numBytes);
 510   }
 511 
 512   public long getJBooleanSize() {
 513     return jbooleanSize;
 514   }
 515 
 516   public long getJByteSize() {
 517     return jbyteSize;
 518   }
 519 
 520   public long getJCharSize() {
 521     return jcharSize;
 522   }
 523 
 524   public long getJDoubleSize() {
 525     return jdoubleSize;
 526   }
 527 
 528   public long getJFloatSize() {
 529     return jfloatSize;
 530   }
 531 
 532   public long getJIntSize() {
 533     return jintSize;
 534   }
 535 
 536   public long getJLongSize() {
 537     return jlongSize;
 538   }
 539 
 540   public long getJShortSize() {
 541     return jshortSize;
 542   }
 543 
 544   public long getHeapOopSize() {
 545     return heapOopSize;
 546   }
 547 
 548   public long getNarrowOopBase() {
 549     return narrowOopBase;
 550   }
 551   public int getNarrowOopShift() {
 552     return narrowOopShift;
 553   }
 554 }