While dumping all registers (and doing a findpc on each), the following exception occurred for r8:
r8: 0x000000750e4fdffc Error: java.lang.ArrayIndexOutOfBoundsException: Index 4099 out of bounds for length 4096 java.lang.ArrayIndexOutOfBoundsException: Index 4099 out of bounds for length 4096 at jdk.hotspot.agent/sun.jvm.hotspot.debugger.Page.getLong(Page.java:182) at jdk.hotspot.agent/sun.jvm.hotspot.debugger.PageCache.getLong(PageCache.java:100) at jdk.hotspot.agent/sun.jvm.hotspot.debugger.DebuggerBase.readCInteger(DebuggerBase.java:364) at jdk.hotspot.agent/sun.jvm.hotspot.debugger.DebuggerBase.readAddressValue(DebuggerBase.java:462) at jdk.hotspot.agent/sun.jvm.hotspot.debugger.windbg.WindbgDebuggerLocal.readAddress(WindbgDebuggerLocal.java:312) at jdk.hotspot.agent/sun.jvm.hotspot.debugger.windbg.WindbgAddress.getAddressAt(WindbgAddress.java:71) at jdk.hotspot.agent/sun.jvm.hotspot.utilities.PointerFinder.find(PointerFinder.java:58) at jdk.hotspot.agent/sun.jvm.hotspot.runtime.JavaThread.printThreadContextOn(JavaThread.java:493) This exception is the result of using PointerFinder ("findpc") on invalid address that starts on the last 32-bit word of a page. "page" in this case is referring to a page in SA's PageCache, which are always 4k in size. Note findpc is frequently done using an invalid address. In this test case it is being called on each x64 register, which could contain anything. findpc relies on getting some sort of AddressException when this happens, and will then say the address points to something that is unknown. However, in the case of the address pointing to the last 32-bot word of a page, it results in an ArrayIndexOutOfBoundsException when the page cache tries to read past the end of the page. This is happening in Page.getLong(), which you can see in the stack trace. The main culprit here is some weakening of the alignment checks being done. The alignment check should have resulted in an UnalignedAddressException long before we ever got to Page.getLong(). However, we have the following override, which is allowing the unaligned address to pass the alignment check. public void checkAlignment(long address, long alignment) { // Need to override default checkAlignment because we need to // relax alignment constraints on Bsd/x86 if ( (address % alignment != 0) &&(alignment != 8 || address % 4 != 0)) { throw new UnalignedAddressException( "Trying to read at address: " + addressValueToString(address) + " with alignment: " + alignment, address); } } }; This allows a pointer to a 64-bit value to only be 32-bit aligned. But there's more to it than that. I modified ClhsdbFindPC.java to fetch a tid (a thread address), and did a findpc on the address OR'd with 0xffc. `findpc` uses PointerFinder. This forced a read of a 64-bit value that starts in the last 32-bits of a page. It passed on linux-x64 but failed on windowx-x64 in the same manner described in the description of this CR. The difference between the two implementations is that windows relies on the default implementation of DebuggerBase.readCInteger() whereas linux has an override. DebuggerBase.readCInteger() does the following: public long readCInteger(long address, long numBytes, boolean isUnsigned) { 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); ... There is an alignment check here, but it is the "relaxed" override shown above, which allows 64-bit addresses on 32-bit boundaries. The override in LinuxDebuggerLocal is: /** Need to override this to relax alignment checks on x86. */ public long readCInteger(long address, long numBytes, boolean isUnsigned) throws UnmappedAddressException, UnalignedAddressException { // Only slightly relaxed semantics -- this is a hack, but is // necessary on x86 where it seems the compiler is // putting some global 64-bit data on 32-bit boundaries if (numBytes == 8) { utils.checkAlignment(address, 4); } else { utils.checkAlignment(address, numBytes); } byte[] data = readBytes(address, numBytes); return utils.dataToCInteger(data, isUnsigned); Although there is a relaxed alignment check here also, the code that reads from the address does not assume all the bytes are on the same page. It uses readBytes() intead of cache.getLong(), so it won't run into the PageCache issue with reading a 64-bit value that starts in the last 32-bit word of a page. I think the introduction of these relaxed alignment checks has a muddled history, probably made more muddled by ports cloning code that maybe wasn't necessary. Probably also initial fixes (the relaxed alignment check) seemed to work at first, but later the PageCache issue was discovered, leading to readBytes() workaround in the LinuxDebuggerLocal.readCInteger() override, but was not also done on other ports (so we got this CR for windows). For 64-bit support it's clear this easing of the 64-bit alignment is not needed, and getting rid of it would result in the proper UnalignedAddressException being thrown. The question is whether it is still needed for 32-bit x86, and if so is it needed on all ports. I can't test linux-x86, so I can't tell if it still allows 64-bit values on 32-bit aligned addresses, so for now I'll assume it does. So the approach being taken is whenever an address of a 64-bit type points to the last 32-bit word of a page, use readBytes() to get the 64-bit value one byte at a time. It still uses the page cache in the end, but it doesn't try to get all 8 bytes from the same page. Note for 64-bit systems, the conditoin will never arise because of the removal of the relaxed alignment check. Instead there will be an UnalignedAddressException at an early point when the alignment check is made. One windfall of this change is now we always read 64-bit values from the page cache in a way that is much more efficient than reading a byte at a time. This has resulted in about a 25% performance improvement in a test I have that does a heap dump. ------------- Commit messages: - Fix jcheck error - Undo some temp test changes. - Fix 64-bit alignment requirements Changes: https://git.openjdk.org/jdk/pull/10090/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=10090&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8292201 Stats: 227 lines in 11 files changed: 52 ins; 154 del; 21 mod Patch: https://git.openjdk.org/jdk/pull/10090.diff Fetch: git fetch https://git.openjdk.org/jdk pull/10090/head:pull/10090 PR: https://git.openjdk.org/jdk/pull/10090