1 /* 2 * Copyright (c) 2000, 2002, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.debugger; 26 27 /** This class implements an LRU page-level cache of configurable page 28 size and number of pages. It is configured with a PageFetcher 29 which enables it to transparently satisfy requests which span 30 multiple pages when one or more of those pages is not in the 31 cache. It is generic enough to be sharable among debugger 32 implementations. */ 33 34 import sun.jvm.hotspot.utilities.*; 35 36 public class PageCache { 37 /** The pageSize must be a power of two and implicitly specifies the 38 alignment of pages. numPages specifies how many pages should be 39 cached. */ PageCache(long pageSize, long maxNumPages, PageFetcher fetcher)40 public PageCache(long pageSize, 41 long maxNumPages, 42 PageFetcher fetcher) { 43 checkPageInfo(pageSize, maxNumPages); 44 this.pageSize = pageSize; 45 this.maxNumPages = maxNumPages; 46 this.fetcher = fetcher; 47 addressToPageMap = new LongHashMap(); 48 enabled = true; 49 } 50 51 /** This handles fetches which span multiple pages by virtue of the 52 presence of the PageFetcher. Throws UnmappedAddressException if 53 a page on which data was requested was unmapped. This can not 54 really handle numBytes > 32 bits. */ getData(long startAddress, long numBytes)55 public synchronized byte[] getData(long startAddress, long numBytes) 56 throws UnmappedAddressException { 57 byte[] data = new byte[(int) numBytes]; 58 long numRead = 0; 59 60 while (numBytes > 0) { 61 long pageBaseAddress = startAddress & pageMask; 62 // Look up this page 63 Page page = checkPage(getPage(pageBaseAddress), startAddress); 64 // Figure out how many bytes to read from this page 65 long pageOffset = startAddress - pageBaseAddress; 66 long numBytesFromPage = Math.min(pageSize - pageOffset, numBytes); 67 // Read them starting at the appropriate offset in the 68 // destination buffer 69 page.getDataAsBytes(startAddress, numBytesFromPage, data, numRead); 70 // Increment offsets 71 numRead += numBytesFromPage; 72 numBytes -= numBytesFromPage; 73 startAddress += numBytesFromPage; 74 } 75 76 return data; 77 } 78 getBoolean(long address)79 public synchronized boolean getBoolean(long address) { 80 return (getByte(address) != 0); 81 } 82 getByte(long address)83 public synchronized byte getByte(long address) { 84 return checkPage(getPage(address & pageMask), address).getByte(address); 85 } 86 getShort(long address, boolean bigEndian)87 public synchronized short getShort(long address, boolean bigEndian) { 88 return checkPage(getPage(address & pageMask), address).getShort(address, bigEndian); 89 } 90 getChar(long address, boolean bigEndian)91 public synchronized char getChar(long address, boolean bigEndian) { 92 return checkPage(getPage(address & pageMask), address).getChar(address, bigEndian); 93 } 94 getInt(long address, boolean bigEndian)95 public synchronized int getInt(long address, boolean bigEndian) { 96 return checkPage(getPage(address & pageMask), address).getInt(address, bigEndian); 97 } 98 getLong(long address, boolean bigEndian)99 public synchronized long getLong(long address, boolean bigEndian) { 100 return checkPage(getPage(address & pageMask), address).getLong(address, bigEndian); 101 } 102 getFloat(long address, boolean bigEndian)103 public synchronized float getFloat(long address, boolean bigEndian) { 104 return checkPage(getPage(address & pageMask), address).getFloat(address, bigEndian); 105 } 106 getDouble(long address, boolean bigEndian)107 public synchronized double getDouble(long address, boolean bigEndian) { 108 return checkPage(getPage(address & pageMask), address).getDouble(address, bigEndian); 109 } 110 111 /** A mechanism for clearing cached data covering the given region */ clear(long startAddress, long numBytes)112 public synchronized void clear(long startAddress, long numBytes) { 113 long pageBaseAddress = startAddress & pageMask; 114 long endAddress = startAddress + numBytes; 115 while (pageBaseAddress < endAddress) { 116 flushPage(pageBaseAddress); 117 pageBaseAddress += pageSize; 118 } 119 } 120 121 /** A mechanism for clearing out the cache is necessary to handle 122 detaching and reattaching */ clear()123 public synchronized void clear() { 124 // Should probably break next/prev links in list as well 125 addressToPageMap.clear(); 126 lruList = null; 127 numPages = 0; 128 } 129 130 /** Disables the page cache; no further pages will be added to the 131 cache and all existing pages will be flushed. Call this when the 132 target process has been resumed. */ disable()133 public synchronized void disable() { 134 enabled = false; 135 clear(); 136 } 137 138 /** Enables the page cache; fetched pages will be added to the 139 cache. Call this when the target process has been suspended. */ enable()140 public synchronized void enable() { 141 enabled = true; 142 } 143 144 145 //-------------------------------------------------------------------------------- 146 // Internals only below this point 147 // 148 149 // This is implemented with two data structures: a hash table for 150 // fast lookup by a page's base address and a circular doubly-linked 151 // list for implementing LRU behavior. 152 153 private boolean enabled; 154 private long pageSize; 155 private long maxNumPages; 156 private long pageMask; 157 private long numPages; 158 private PageFetcher fetcher; 159 private LongHashMap addressToPageMap; // Map<long, Page> 160 private Page lruList; // Most recently fetched page, or null 161 162 /** Page fetcher plus LRU functionality */ getPage(long pageBaseAddress)163 private Page getPage(long pageBaseAddress) { 164 // Check head of LRU list first to avoid hash table lookup and 165 // extra list work if possible 166 if (lruList != null) { 167 if (lruList.getBaseAddress() == pageBaseAddress) { 168 // Hit. Return it. 169 return lruList; 170 } 171 } 172 // Long key = new Long(pageBaseAddress); 173 long key = pageBaseAddress; 174 Page page = (Page) addressToPageMap.get(key); 175 if (page == null) { 176 // System.err.println("** Cache miss at address 0x" + Long.toHexString(pageBaseAddress) + " **"); 177 // Fetch new page 178 page = fetcher.fetchPage(pageBaseAddress, pageSize); 179 if (enabled) { 180 // Add to cache, evicting last element if necessary 181 addressToPageMap.put(key, page); 182 if (Assert.ASSERTS_ENABLED) { 183 Assert.that(page == (Page) addressToPageMap.get(pageBaseAddress), 184 "must have found page in cache!"); 185 } 186 addPageToList(page); 187 // See whether eviction of oldest is necessary 188 if (numPages == maxNumPages) { 189 Page evictedPage = lruList.getPrev(); 190 // System.err.println("-> Evicting page at 0x" + Long.toHexString(evictedPage.getBaseAddress()) + 191 // "; " + countPages() + " pages left (expect " + numPages + ")"); 192 removePageFromList(evictedPage); 193 addressToPageMap.remove(evictedPage.getBaseAddress()); 194 } else { 195 ++numPages; 196 } 197 } 198 } else { 199 // Page already in cache, move to front of list 200 removePageFromList(page); 201 addPageToList(page); 202 } 203 return page; 204 } 205 checkPage(Page page, long startAddress)206 private Page checkPage(Page page, long startAddress) { 207 if (!page.isMapped()) { 208 throw new UnmappedAddressException(startAddress); 209 } 210 return page; 211 } 212 countPages()213 private int countPages() { 214 Page page = lruList; 215 int num = 0; 216 if (page == null) { 217 return num; 218 } 219 do { 220 ++num; 221 page = page.getNext(); 222 } while (page != lruList); 223 return num; 224 } 225 flushPage(long pageBaseAddress)226 private void flushPage(long pageBaseAddress) { 227 long key = pageBaseAddress; 228 Page page = (Page) addressToPageMap.remove(key); 229 if (page != null) { 230 removePageFromList(page); 231 } 232 } 233 234 // Adds given page to head of list addPageToList(Page page)235 private void addPageToList(Page page) { 236 if (lruList == null) { 237 lruList = page; 238 page.setNext(page); 239 page.setPrev(page); 240 } else { 241 // Add to front of list 242 page.setNext(lruList); 243 page.setPrev(lruList.getPrev()); 244 lruList.getPrev().setNext(page); 245 lruList.setPrev(page); 246 lruList = page; 247 } 248 } 249 250 // Removes given page from list removePageFromList(Page page)251 private void removePageFromList(Page page) { 252 if (page.getNext() == page) { 253 lruList = null; 254 } else { 255 if (lruList == page) { 256 lruList = page.getNext(); 257 } 258 page.getPrev().setNext(page.getNext()); 259 page.getNext().setPrev(page.getPrev()); 260 } 261 page.setPrev(null); 262 page.setNext(null); 263 } 264 265 /** Ensure that page size fits within 32 bits and is a power of two, and that maxNumPages > 0 */ checkPageInfo(long pageSize, long maxNumPages)266 private void checkPageInfo(long pageSize, long maxNumPages) { 267 if ((pageSize <= 0) || maxNumPages <= 0) { 268 throw new IllegalArgumentException("pageSize and maxNumPages must both be greater than zero"); 269 } 270 long tmpPageSize = pageSize >>> 32; 271 if (tmpPageSize != 0) { 272 throw new IllegalArgumentException("pageSize " + pageSize + " too big (must fit within 32 bits)"); 273 } 274 int numNonZeroBits = 0; 275 for (int i = 0; i < 32; ++i) { 276 if ((pageSize & 1L) != 0) { 277 ++numNonZeroBits; 278 if ((numNonZeroBits > 1) || (i == 0)) { 279 throw new IllegalArgumentException("pageSize " + pageSize + " must be a power of two"); 280 } 281 } 282 pageSize >>>= 1; 283 if (numNonZeroBits == 0) { 284 pageMask = (pageMask << 1) | 1L; 285 } 286 } 287 pageMask = ~pageMask; 288 } 289 } 290