Mercurial > hg > truffle
diff agent/src/share/classes/sun/jvm/hotspot/debugger/PageCache.java @ 0:a61af66fc99e jdk7-b24
Initial load
author | duke |
---|---|
date | Sat, 01 Dec 2007 00:00:00 +0000 |
parents | |
children | c18cbe5936b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/share/classes/sun/jvm/hotspot/debugger/PageCache.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,289 @@ +/* + * Copyright 2000-2002 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger; + +/** This class implements an LRU page-level cache of configurable page + size and number of pages. It is configured with a PageFetcher + which enables it to transparently satisfy requests which span + multiple pages when one or more of those pages is not in the + cache. It is generic enough to be sharable among debugger + implementations. */ + +import sun.jvm.hotspot.utilities.*; + +public class PageCache { + /** The pageSize must be a power of two and implicitly specifies the + alignment of pages. numPages specifies how many pages should be + cached. */ + public PageCache(long pageSize, + long maxNumPages, + PageFetcher fetcher) { + checkPageInfo(pageSize, maxNumPages); + this.pageSize = pageSize; + this.maxNumPages = maxNumPages; + this.fetcher = fetcher; + addressToPageMap = new LongHashMap(); + enabled = true; + } + + /** This handles fetches which span multiple pages by virtue of the + presence of the PageFetcher. Throws UnmappedAddressException if + a page on which data was requested was unmapped. This can not + really handle numBytes > 32 bits. */ + public synchronized byte[] getData(long startAddress, long numBytes) + throws UnmappedAddressException { + byte[] data = new byte[(int) numBytes]; + long numRead = 0; + + while (numBytes > 0) { + long pageBaseAddress = startAddress & pageMask; + // Look up this page + Page page = checkPage(getPage(pageBaseAddress), startAddress); + // Figure out how many bytes to read from this page + long pageOffset = startAddress - pageBaseAddress; + long numBytesFromPage = Math.min(pageSize - pageOffset, numBytes); + // Read them starting at the appropriate offset in the + // destination buffer + page.getDataAsBytes(startAddress, numBytesFromPage, data, numRead); + // Increment offsets + numRead += numBytesFromPage; + numBytes -= numBytesFromPage; + startAddress += numBytesFromPage; + } + + return data; + } + + public synchronized boolean getBoolean(long address) { + return (getByte(address) != 0); + } + + public synchronized byte getByte(long address) { + return checkPage(getPage(address & pageMask), address).getByte(address); + } + + public synchronized short getShort(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getShort(address, bigEndian); + } + + public synchronized char getChar(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getChar(address, bigEndian); + } + + public synchronized int getInt(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getInt(address, bigEndian); + } + + public synchronized long getLong(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getLong(address, bigEndian); + } + + public synchronized float getFloat(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getFloat(address, bigEndian); + } + + public synchronized double getDouble(long address, boolean bigEndian) { + return checkPage(getPage(address & pageMask), address).getDouble(address, bigEndian); + } + + /** A mechanism for clearing cached data covering the given region */ + public synchronized void clear(long startAddress, long numBytes) { + long pageBaseAddress = startAddress & pageMask; + long endAddress = startAddress + numBytes; + while (pageBaseAddress < endAddress) { + flushPage(pageBaseAddress); + pageBaseAddress += pageSize; + } + } + + /** A mechanism for clearing out the cache is necessary to handle + detaching and reattaching */ + public synchronized void clear() { + // Should probably break next/prev links in list as well + addressToPageMap.clear(); + lruList = null; + numPages = 0; + } + + /** Disables the page cache; no further pages will be added to the + cache and all existing pages will be flushed. Call this when the + target process has been resumed. */ + public synchronized void disable() { + enabled = false; + clear(); + } + + /** Enables the page cache; fetched pages will be added to the + cache. Call this when the target process has been suspended. */ + public synchronized void enable() { + enabled = true; + } + + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + // This is implemented with two data structures: a hash table for + // fast lookup by a page's base address and a circular doubly-linked + // list for implementing LRU behavior. + + private boolean enabled; + private long pageSize; + private long maxNumPages; + private long pageMask; + private long numPages; + private PageFetcher fetcher; + private LongHashMap addressToPageMap; // Map<long, Page> + private Page lruList; // Most recently fetched page, or null + + /** Page fetcher plus LRU functionality */ + private Page getPage(long pageBaseAddress) { + // Check head of LRU list first to avoid hash table lookup and + // extra list work if possible + if (lruList != null) { + if (lruList.getBaseAddress() == pageBaseAddress) { + // Hit. Return it. + return lruList; + } + } + // Long key = new Long(pageBaseAddress); + long key = pageBaseAddress; + Page page = (Page) addressToPageMap.get(key); + if (page == null) { + // System.err.println("** Cache miss at address 0x" + Long.toHexString(pageBaseAddress) + " **"); + // Fetch new page + page = fetcher.fetchPage(pageBaseAddress, pageSize); + if (enabled) { + // Add to cache, evicting last element if necessary + addressToPageMap.put(key, page); + if (Assert.ASSERTS_ENABLED) { + Assert.that(page == (Page) addressToPageMap.get(pageBaseAddress), + "must have found page in cache!"); + } + addPageToList(page); + // See whether eviction of oldest is necessary + if (numPages == maxNumPages) { + Page evictedPage = lruList.getPrev(); + // System.err.println("-> Evicting page at 0x" + Long.toHexString(evictedPage.getBaseAddress()) + + // "; " + countPages() + " pages left (expect " + numPages + ")"); + removePageFromList(evictedPage); + addressToPageMap.remove(evictedPage.getBaseAddress()); + } else { + ++numPages; + } + } + } else { + // Page already in cache, move to front of list + removePageFromList(page); + addPageToList(page); + } + return page; + } + + private Page checkPage(Page page, long startAddress) { + if (!page.isMapped()) { + throw new UnmappedAddressException(startAddress); + } + return page; + } + + private int countPages() { + Page page = lruList; + int num = 0; + if (page == null) { + return num; + } + do { + ++num; + page = page.getNext(); + } while (page != lruList); + return num; + } + + private void flushPage(long pageBaseAddress) { + long key = pageBaseAddress; + Page page = (Page) addressToPageMap.remove(key); + if (page != null) { + removePageFromList(page); + } + } + + // Adds given page to head of list + private void addPageToList(Page page) { + if (lruList == null) { + lruList = page; + page.setNext(page); + page.setPrev(page); + } else { + // Add to front of list + page.setNext(lruList); + page.setPrev(lruList.getPrev()); + lruList.getPrev().setNext(page); + lruList.setPrev(page); + lruList = page; + } + } + + // Removes given page from list + private void removePageFromList(Page page) { + if (page.getNext() == page) { + lruList = null; + } else { + if (lruList == page) { + lruList = page.getNext(); + } + page.getPrev().setNext(page.getNext()); + page.getNext().setPrev(page.getPrev()); + } + page.setPrev(null); + page.setNext(null); + } + + /** Ensure that page size fits within 32 bits and is a power of two, and that maxNumPages > 0 */ + private void checkPageInfo(long pageSize, long maxNumPages) { + if ((pageSize <= 0) || maxNumPages <= 0) { + throw new IllegalArgumentException("pageSize and maxNumPages must both be greater than zero"); + } + long tmpPageSize = pageSize >>> 32; + if (tmpPageSize != 0) { + throw new IllegalArgumentException("pageSize " + pageSize + " too big (must fit within 32 bits)"); + } + int numNonZeroBits = 0; + for (int i = 0; i < 32; ++i) { + if ((pageSize & 1L) != 0) { + ++numNonZeroBits; + if ((numNonZeroBits > 1) || (i == 0)) { + throw new IllegalArgumentException("pageSize " + pageSize + " must be a power of two"); + } + } + pageSize >>>= 1; + if (numNonZeroBits == 0) { + pageMask = (pageMask << 1) | 1L; + } + } + pageMask = ~pageMask; + } +}