| /* |
| * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| package sun.jvm.hotspot.debugger; |
| |
| /** <P> DebuggerBase is a recommended base class for debugger |
| implementations. It can use a PageCache to cache data from the |
| target process. Note that this class would not be suitable if the |
| system were used to reflect upon itself; it would never be safe to |
| store the value in an OopHandle in anything but an OopHandle. |
| However, it provides a fair amount of code sharing to the current |
| dbx and win32 implementations. </P> |
| |
| <P> NOTE that much of the code sharing is achieved by having this |
| class implement many of the methods in the Win32Debugger and |
| DbxDebugger interfaces. </P> */ |
| |
| public abstract class DebuggerBase implements Debugger { |
| |
| // May be set lazily, but must be set before calling any of the read |
| // routines below |
| protected MachineDescription machDesc; |
| protected DebuggerUtilities utils; |
| // Java primitive type sizes, set during bootstrapping. Do not call |
| // any of the Java read routines until these are set up. |
| protected long jbooleanSize; |
| protected long jbyteSize; |
| protected long jcharSize; |
| protected long jdoubleSize; |
| protected long jfloatSize; |
| protected long jintSize; |
| protected long jlongSize; |
| protected long jshortSize; |
| protected boolean javaPrimitiveTypesConfigured; |
| // heap data. |
| protected long oopSize; |
| protected long heapOopSize; |
| protected long narrowOopBase; // heap base for compressed oops. |
| protected int narrowOopShift; // shift to decode compressed oops. |
| // class metadata space |
| protected long klassPtrSize; |
| protected long narrowKlassBase; // heap base for compressed klass ptrs. |
| protected int narrowKlassShift; // shift to decode compressed klass ptrs. |
| // Should be initialized if desired by calling initCache() |
| private PageCache cache; |
| |
| // State for faster accessors that don't allocate memory on each read |
| private boolean useFastAccessors; |
| private boolean bigEndian; |
| |
| // Page-fetching functionality for LRU cache |
| class Fetcher implements PageFetcher { |
| public Page fetchPage(long pageBaseAddress, long numBytes) { |
| // This assumes that if any byte is unmapped, that the entire |
| // page is. The common case, however, is that the page is |
| // mapped, so we always fetch the entire thing all at once to |
| // avoid two round-trip communications per page fetch, even |
| // though fetching of unmapped pages will be slow. |
| ReadResult res = readBytesFromProcess(pageBaseAddress, numBytes); |
| if (res.getData() == null) { |
| return new Page(pageBaseAddress, numBytes); |
| } |
| return new Page(pageBaseAddress, res.getData()); |
| } |
| } |
| |
| protected DebuggerBase() { |
| } |
| |
| /** From the JVMDebugger interface. This is the only public method |
| of this class. */ |
| public void configureJavaPrimitiveTypeSizes(long jbooleanSize, |
| long jbyteSize, |
| long jcharSize, |
| long jdoubleSize, |
| long jfloatSize, |
| long jintSize, |
| long jlongSize, |
| long jshortSize) { |
| this.jbooleanSize = jbooleanSize; |
| this.jbyteSize = jbyteSize; |
| this.jcharSize = jcharSize; |
| this.jdoubleSize = jdoubleSize; |
| this.jfloatSize = jfloatSize; |
| this.jintSize = jintSize; |
| this.jlongSize = jlongSize; |
| this.jshortSize = jshortSize; |
| |
| if (jbooleanSize < 1) { |
| throw new RuntimeException("jboolean size is too small"); |
| } |
| |
| if (jbyteSize < 1) { |
| throw new RuntimeException("jbyte size is too small"); |
| } |
| |
| if (jcharSize < 2) { |
| throw new RuntimeException("jchar size is too small"); |
| } |
| |
| if (jdoubleSize < 8) { |
| throw new RuntimeException("jdouble size is too small"); |
| } |
| |
| if (jfloatSize < 4) { |
| throw new RuntimeException("jfloat size is too small"); |
| } |
| |
| if (jintSize < 4) { |
| throw new RuntimeException("jint size is too small"); |
| } |
| |
| if (jlongSize < 8) { |
| throw new RuntimeException("jlong size is too small"); |
| } |
| |
| if (jshortSize < 2) { |
| throw new RuntimeException("jshort size is too small"); |
| } |
| |
| if (jintSize != jfloatSize) { |
| // If dataToJFloat were rewritten, this wouldn't be necessary |
| throw new RuntimeException("jint size and jfloat size must be equal"); |
| } |
| |
| if (jlongSize != jdoubleSize) { |
| // If dataToJDouble were rewritten, this wouldn't be necessary |
| throw new RuntimeException("jlong size and jdouble size must be equal"); |
| } |
| |
| useFastAccessors = |
| ((cache != null) && |
| (jbooleanSize == 1) && |
| (jbyteSize == 1) && |
| (jcharSize == 2) && |
| (jdoubleSize == 8) && |
| (jfloatSize == 4) && |
| (jintSize == 4) && |
| (jlongSize == 8) && |
| (jshortSize == 2)); |
| |
| javaPrimitiveTypesConfigured = true; |
| } |
| |
| public void putHeapConst(long heapOopSize, long klassPtrSize, long narrowOopBase, int narrowOopShift, |
| long narrowKlassBase, int narrowKlassShift) { |
| this.heapOopSize = heapOopSize; |
| this.klassPtrSize = klassPtrSize; |
| this.narrowOopBase = narrowOopBase; |
| this.narrowOopShift = narrowOopShift; |
| this.narrowKlassBase = narrowKlassBase; |
| this.narrowKlassShift = narrowKlassShift; |
| } |
| |
| /** May be called by subclasses if desired to initialize the page |
| cache but may not be overridden */ |
| protected final void initCache(long pageSize, long maxNumPages) { |
| cache = new PageCache(pageSize, maxNumPages, new Fetcher()); |
| if (machDesc != null) { |
| bigEndian = machDesc.isBigEndian(); |
| } |
| } |
| |
| /** May be called by subclasses if needed (if the machine |
| description is not available at the time of cache |
| initialization, as on Solaris) but may not be overridden */ |
| protected final void setBigEndian(boolean bigEndian) { |
| this.bigEndian = bigEndian; |
| } |
| |
| /** May be called by subclasses to clear out the cache but may not |
| be overridden. For convenience, this can be called even if the |
| cache has not been initialized. */ |
| protected final void clearCache() { |
| if (cache != null) { |
| cache.clear(); |
| } |
| } |
| |
| /** May be called by subclasses to disable the cache (for example, |
| when the target process has been resumed) but may not be |
| overridden. For convenience, this can be called even if the |
| cache has not been initialized. */ |
| protected final void disableCache() { |
| if (cache != null) { |
| cache.disable(); |
| } |
| } |
| |
| /** May be called by subclasses to re-enable the cache (for example, |
| when the target process has been suspended) but may not be |
| overridden. For convenience, this can be called even if the |
| cache has not been initialized. */ |
| protected final void enableCache() { |
| if (cache != null) { |
| cache.enable(); |
| } |
| } |
| |
| /** May be called by subclasses directly but may not be overridden */ |
| protected final byte[] readBytes(long address, long numBytes) |
| throws UnmappedAddressException, DebuggerException { |
| if (cache != null) { |
| return cache.getData(address, numBytes); |
| } else { |
| ReadResult res = readBytesFromProcess(address, numBytes); |
| if (res.getData() != null) { |
| return res.getData(); |
| } |
| throw new UnmappedAddressException(res.getFailureAddress()); |
| } |
| } |
| |
| /** May be called by subclasses directly but may not be overridden */ |
| protected final void writeBytes(long address, long numBytes, byte[] data) |
| throws UnmappedAddressException, DebuggerException { |
| if (cache != null) { |
| cache.clear(address, numBytes); |
| } |
| writeBytesToProcess(address, numBytes, data); |
| } |
| |
| public boolean readJBoolean(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jbooleanSize); |
| if (useFastAccessors) { |
| return (cache.getByte(address) != 0); |
| } else { |
| byte[] data = readBytes(address, jbooleanSize); |
| return utils.dataToJBoolean(data, jbooleanSize); |
| } |
| } |
| |
| public byte readJByte(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jbyteSize); |
| if (useFastAccessors) { |
| return cache.getByte(address); |
| } else { |
| byte[] data = readBytes(address, jbyteSize); |
| return utils.dataToJByte(data, jbyteSize); |
| } |
| } |
| |
| // NOTE: assumes value does not span pages (may be bad assumption on |
| // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) |
| public char readJChar(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jcharSize); |
| if (useFastAccessors) { |
| return cache.getChar(address, bigEndian); |
| } else { |
| byte[] data = readBytes(address, jcharSize); |
| return (char) utils.dataToJChar(data, jcharSize); |
| } |
| } |
| |
| // NOTE: assumes value does not span pages (may be bad assumption on |
| // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) |
| public double readJDouble(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jdoubleSize); |
| if (useFastAccessors) { |
| return cache.getDouble(address, bigEndian); |
| } else { |
| byte[] data = readBytes(address, jdoubleSize); |
| return utils.dataToJDouble(data, jdoubleSize); |
| } |
| } |
| |
| // NOTE: assumes value does not span pages (may be bad assumption on |
| // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) |
| public float readJFloat(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jfloatSize); |
| if (useFastAccessors) { |
| return cache.getFloat(address, bigEndian); |
| } else { |
| byte[] data = readBytes(address, jfloatSize); |
| return utils.dataToJFloat(data, jfloatSize); |
| } |
| } |
| |
| // NOTE: assumes value does not span pages (may be bad assumption on |
| // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) |
| public int readJInt(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jintSize); |
| if (useFastAccessors) { |
| return cache.getInt(address, bigEndian); |
| } else { |
| byte[] data = readBytes(address, jintSize); |
| return utils.dataToJInt(data, jintSize); |
| } |
| } |
| |
| // NOTE: assumes value does not span pages (may be bad assumption on |
| // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) |
| public long readJLong(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jlongSize); |
| if (useFastAccessors) { |
| return cache.getLong(address, bigEndian); |
| } else { |
| byte[] data = readBytes(address, jlongSize); |
| return utils.dataToJLong(data, jlongSize); |
| } |
| } |
| |
| // NOTE: assumes value does not span pages (may be bad assumption on |
| // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) |
| public short readJShort(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jshortSize); |
| if (useFastAccessors) { |
| return cache.getShort(address, bigEndian); |
| } else { |
| byte[] data = readBytes(address, jshortSize); |
| return utils.dataToJShort(data, jshortSize); |
| } |
| } |
| |
| // NOTE: assumes value does not span pages (may be bad assumption on |
| // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy) |
| public long readCInteger(long address, long numBytes, boolean isUnsigned) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkConfigured(); |
| utils.checkAlignment(address, numBytes); |
| if (useFastAccessors) { |
| if (isUnsigned) { |
| switch((int) numBytes) { |
| case 1: return cache.getByte(address) & 0xFF; |
| case 2: return cache.getShort(address, bigEndian) & 0xFFFF; |
| case 4: return cache.getInt(address, bigEndian) & 0xFFFFFFFFL; |
| case 8: return cache.getLong(address, bigEndian); |
| default: { |
| byte[] data = readBytes(address, numBytes); |
| return utils.dataToCInteger(data, isUnsigned); |
| } |
| } |
| } else { |
| switch((int) numBytes) { |
| case 1: return cache.getByte(address); |
| case 2: return cache.getShort(address, bigEndian); |
| case 4: return cache.getInt(address, bigEndian); |
| case 8: return cache.getLong(address, bigEndian); |
| default: { |
| byte[] data = readBytes(address, numBytes); |
| return utils.dataToCInteger(data, isUnsigned); |
| } |
| } |
| } |
| } else { |
| byte[] data = readBytes(address, numBytes); |
| return utils.dataToCInteger(data, isUnsigned); |
| } |
| } |
| |
| public void writeJBoolean(long address, boolean value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jbooleanSize); |
| byte[] data = utils.jbooleanToData(value); |
| writeBytes(address, jbooleanSize, data); |
| } |
| |
| public void writeJByte(long address, byte value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jbyteSize); |
| byte[] data = utils.jbyteToData(value); |
| writeBytes(address, jbyteSize, data); |
| } |
| |
| public void writeJChar(long address, char value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jcharSize); |
| byte[] data = utils.jcharToData(value); |
| writeBytes(address, jcharSize, data); |
| } |
| |
| public void writeJDouble(long address, double value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jdoubleSize); |
| byte[] data = utils.jdoubleToData(value); |
| writeBytes(address, jdoubleSize, data); |
| } |
| |
| public void writeJFloat(long address, float value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jfloatSize); |
| byte[] data = utils.jfloatToData(value); |
| writeBytes(address, jfloatSize, data); |
| } |
| |
| public void writeJInt(long address, int value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jintSize); |
| byte[] data = utils.jintToData(value); |
| writeBytes(address, jintSize, data); |
| } |
| |
| public void writeJLong(long address, long value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jlongSize); |
| byte[] data = utils.jlongToData(value); |
| writeBytes(address, jlongSize, data); |
| } |
| |
| public void writeJShort(long address, short value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkJavaConfigured(); |
| utils.checkAlignment(address, jshortSize); |
| byte[] data = utils.jshortToData(value); |
| writeBytes(address, jshortSize, data); |
| } |
| |
| public void writeCInteger(long address, long numBytes, long value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| checkConfigured(); |
| utils.checkAlignment(address, numBytes); |
| byte[] data = utils.cIntegerToData(numBytes, value); |
| writeBytes(address, numBytes, data); |
| } |
| |
| protected long readAddressValue(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| return readCInteger(address, machDesc.getAddressSize(), true); |
| } |
| |
| protected long readCompOopAddressValue(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| long value = readCInteger(address, getHeapOopSize(), true); |
| if (value != 0) { |
| // See oop.inline.hpp decode_heap_oop |
| value = (long)(narrowOopBase + (long)(value << narrowOopShift)); |
| } |
| return value; |
| } |
| |
| protected long readCompKlassAddressValue(long address) |
| throws UnmappedAddressException, UnalignedAddressException { |
| long value = readCInteger(address, getKlassPtrSize(), true); |
| if (value != 0) { |
| value = (long)(narrowKlassBase + (long)(value << narrowKlassShift)); |
| } |
| return value; |
| } |
| |
| protected void writeAddressValue(long address, long value) |
| throws UnmappedAddressException, UnalignedAddressException { |
| writeCInteger(address, machDesc.getAddressSize(), value); |
| } |
| |
| /** Can be called by subclasses but can not be overridden */ |
| protected final void checkConfigured() { |
| if (machDesc == null) { |
| throw new RuntimeException("MachineDescription must have been set by this point"); |
| } |
| if (utils == null) { |
| throw new RuntimeException("DebuggerUtilities must have been set by this point"); |
| } |
| } |
| |
| /** Can be called by subclasses but can not be overridden */ |
| protected final void checkJavaConfigured() { |
| checkConfigured(); |
| |
| if (!javaPrimitiveTypesConfigured) { |
| throw new RuntimeException("Java primitive type sizes have not yet been configured"); |
| } |
| } |
| |
| /** Possibly override page cache size with user-specified property */ |
| protected int parseCacheNumPagesProperty(int defaultNum) { |
| String cacheNumPagesString = System.getProperty("cacheNumPages"); |
| if (cacheNumPagesString != null) { |
| try { |
| return Integer.parseInt(cacheNumPagesString); |
| } catch (Exception e) { |
| System.err.println("Error parsing cacheNumPages property:"); |
| e.printStackTrace(); |
| } |
| } |
| return defaultNum; |
| } |
| |
| /** Interim solution for allowing subclasses to write bytes to |
| process until we make that functionality available in the basic |
| Address interface */ |
| protected void invalidatePageCache(long startAddress, long numBytes) { |
| cache.clear(startAddress, numBytes); |
| } |
| |
| public long getJBooleanSize() { |
| return jbooleanSize; |
| } |
| |
| public long getJByteSize() { |
| return jbyteSize; |
| } |
| |
| public long getJCharSize() { |
| return jcharSize; |
| } |
| |
| public long getJDoubleSize() { |
| return jdoubleSize; |
| } |
| |
| public long getJFloatSize() { |
| return jfloatSize; |
| } |
| |
| public long getJIntSize() { |
| return jintSize; |
| } |
| |
| public long getJLongSize() { |
| return jlongSize; |
| } |
| |
| public long getJShortSize() { |
| return jshortSize; |
| } |
| |
| public long getHeapOopSize() { |
| return heapOopSize; |
| } |
| |
| public long getNarrowOopBase() { |
| return narrowOopBase; |
| } |
| public int getNarrowOopShift() { |
| return narrowOopShift; |
| } |
| |
| public long getKlassPtrSize() { |
| return klassPtrSize; |
| } |
| |
| public long getNarrowKlassBase() { |
| return narrowKlassBase; |
| } |
| public int getNarrowKlassShift() { |
| return narrowKlassShift; |
| } |
| } |