1 /* 2 * Copyright (c) 2001, 2022, 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 /** <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 // class metadata space 62 protected long klassPtrSize; 63 protected long narrowKlassBase; // heap base for compressed klass ptrs. 64 protected int narrowKlassShift; // shift to decode compressed klass ptrs. 65 // Should be initialized if desired by calling initCache() 66 private PageCache cache; 67 private long pageSize; 68 69 // State for faster accessors that don't allocate memory on each read 70 private boolean useFastAccessors; 71 private boolean bigEndian; 72 73 // Page-fetching functionality for LRU cache 74 class Fetcher implements PageFetcher { 75 public Page fetchPage(long pageBaseAddress, long numBytes) { 76 // This assumes that if any byte is unmapped, that the entire 77 // page is. The common case, however, is that the page is 78 // mapped, so we always fetch the entire thing all at once to 79 // avoid two round-trip communications per page fetch, even 80 // though fetching of unmapped pages will be slow. 81 ReadResult res = readBytesFromProcess(pageBaseAddress, numBytes); 82 if (res.getData() == null) { 83 return new Page(pageBaseAddress, numBytes); 84 } 85 return new Page(pageBaseAddress, res.getData()); 86 } 87 } 88 89 protected DebuggerBase() { 90 } 91 92 /** From the JVMDebugger interface. This is the only public method 93 of this class. */ 94 public void configureJavaPrimitiveTypeSizes(long jbooleanSize, 95 long jbyteSize, 96 long jcharSize, 97 long jdoubleSize, 98 long jfloatSize, 99 long jintSize, 100 long jlongSize, 101 long jshortSize) { 102 this.jbooleanSize = jbooleanSize; 103 this.jbyteSize = jbyteSize; 104 this.jcharSize = jcharSize; 105 this.jdoubleSize = jdoubleSize; 106 this.jfloatSize = jfloatSize; 107 this.jintSize = jintSize; 108 this.jlongSize = jlongSize; 109 this.jshortSize = jshortSize; 110 111 if (jbooleanSize < 1) { 112 throw new RuntimeException("jboolean size is too small"); 113 } 114 115 if (jbyteSize < 1) { 116 throw new RuntimeException("jbyte size is too small"); 117 } 118 119 if (jcharSize < 2) { 120 throw new RuntimeException("jchar size is too small"); 121 } 122 123 if (jdoubleSize < 8) { 124 throw new RuntimeException("jdouble size is too small"); 125 } 126 127 if (jfloatSize < 4) { 128 throw new RuntimeException("jfloat size is too small"); 129 } 130 131 if (jintSize < 4) { 132 throw new RuntimeException("jint size is too small"); 133 } 134 135 if (jlongSize < 8) { 136 throw new RuntimeException("jlong size is too small"); 137 } 138 139 if (jshortSize < 2) { 140 throw new RuntimeException("jshort size is too small"); 141 } 142 143 if (jintSize != jfloatSize) { 144 // If dataToJFloat were rewritten, this wouldn't be necessary 145 throw new RuntimeException("jint size and jfloat size must be equal"); 146 } 147 148 if (jlongSize != jdoubleSize) { 149 // If dataToJDouble were rewritten, this wouldn't be necessary 150 throw new RuntimeException("jlong size and jdouble size must be equal"); 151 } 152 153 useFastAccessors = 154 ((cache != null) && 155 (jbooleanSize == 1) && 156 (jbyteSize == 1) && 157 (jcharSize == 2) && 158 (jdoubleSize == 8) && 159 (jfloatSize == 4) && 160 (jintSize == 4) && 161 (jlongSize == 8) && 162 (jshortSize == 2)); 163 164 javaPrimitiveTypesConfigured = true; 165 } 166 167 public void putHeapConst(long heapOopSize, long klassPtrSize, long narrowOopBase, int narrowOopShift, 168 long narrowKlassBase, int narrowKlassShift) { 169 this.heapOopSize = heapOopSize; 170 this.klassPtrSize = klassPtrSize; 171 this.narrowOopBase = narrowOopBase; 172 this.narrowOopShift = narrowOopShift; 173 this.narrowKlassBase = narrowKlassBase; 174 this.narrowKlassShift = narrowKlassShift; 175 } 176 177 /** May be called by subclasses if desired to initialize the page 178 cache but may not be overridden */ 179 protected final void initCache(long pageSize, long maxNumPages) { 180 cache = new PageCache(pageSize, maxNumPages, new Fetcher()); 181 this.pageSize = pageSize; 182 if (machDesc != null) { 183 bigEndian = machDesc.isBigEndian(); 184 } 185 } 186 187 /** May be called by subclasses if needed (if the machine 188 description is not available at the time of cache 189 initialization, as on Solaris) but may not be overridden */ 190 protected final void setBigEndian(boolean bigEndian) { 191 this.bigEndian = bigEndian; 192 } 193 194 /** May be called by subclasses to clear out the cache but may not 195 be overridden. For convenience, this can be called even if the 196 cache has not been initialized. */ 197 protected final void clearCache() { 198 if (cache != null) { 199 cache.clear(); 200 } 201 } 202 203 /** May be called by subclasses to disable the cache (for example, 204 when the target process has been resumed) 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 disableCache() { 208 if (cache != null) { 209 cache.disable(); 210 } 211 } 212 213 /** May be called by subclasses to re-enable the cache (for example, 214 when the target process has been suspended) but may not be 215 overridden. For convenience, this can be called even if the 216 cache has not been initialized. */ 217 protected final void enableCache() { 218 if (cache != null) { 219 cache.enable(); 220 } 221 } 222 223 /** May be called by subclasses directly but may not be overridden */ 224 protected final byte[] readBytes(long address, long numBytes) 225 throws UnmappedAddressException, DebuggerException { 226 if (cache != null) { 227 return cache.getData(address, numBytes); 228 } else { 229 ReadResult res = readBytesFromProcess(address, numBytes); 230 if (res.getData() != null) { 231 return res.getData(); 232 } 233 throw new UnmappedAddressException(res.getFailureAddress()); 234 } 235 } 236 237 /** If an address for a 64-bit value starts on the last 32-bit word of a 238 page, then we can't use the page cache to read it because it will cause 239 an ArrayIndexOutOfBoundsException when reading past the end of the page. */ 240 private boolean canUsePageCacheFor64bitRead(long address) { 241 long pageMask = ~(pageSize - 1); 242 if ((address & pageMask) != ((address + 4) & pageMask)) { 243 // This address starts on the last 32-bit word of the page. 244 // Cannot use the page cache in that case. 245 return false; 246 } 247 return true; 248 } 249 250 public boolean readJBoolean(long address) 251 throws UnmappedAddressException, UnalignedAddressException { 252 checkJavaConfigured(); 253 utils.checkAlignment(address, jbooleanSize); 254 if (useFastAccessors) { 255 return (cache.getByte(address) != 0); 256 } else { 257 byte[] data = readBytes(address, jbooleanSize); 258 return utils.dataToJBoolean(data, jbooleanSize); 259 } 260 } 261 262 public byte readJByte(long address) 263 throws UnmappedAddressException, UnalignedAddressException { 264 checkJavaConfigured(); 265 utils.checkAlignment(address, jbyteSize); 266 if (useFastAccessors) { 267 return cache.getByte(address); 268 } else { 269 byte[] data = readBytes(address, jbyteSize); 270 return utils.dataToJByte(data, jbyteSize); 271 } 272 } 273 274 public char readJChar(long address) 275 throws UnmappedAddressException, UnalignedAddressException { 276 checkJavaConfigured(); 277 utils.checkAlignment(address, jcharSize); 278 if (useFastAccessors) { 279 return cache.getChar(address, bigEndian); 280 } else { 281 byte[] data = readBytes(address, jcharSize); 282 return (char) utils.dataToJChar(data, jcharSize); 283 } 284 } 285 286 public double readJDouble(long address) 287 throws UnmappedAddressException, UnalignedAddressException { 288 checkJavaConfigured(); 289 utils.checkAlignment(address, jdoubleSize); 290 if (useFastAccessors && canUsePageCacheFor64bitRead(address)) { 291 return cache.getDouble(address, bigEndian); 292 } else { 293 byte[] data = readBytes(address, jdoubleSize); 294 return utils.dataToJDouble(data, jdoubleSize); 295 } 296 } 297 298 public float readJFloat(long address) 299 throws UnmappedAddressException, UnalignedAddressException { 300 checkJavaConfigured(); 301 utils.checkAlignment(address, jfloatSize); 302 if (useFastAccessors) { 303 return cache.getFloat(address, bigEndian); 304 } else { 305 byte[] data = readBytes(address, jfloatSize); 306 return utils.dataToJFloat(data, jfloatSize); 307 } 308 } 309 310 public int readJInt(long address) 311 throws UnmappedAddressException, UnalignedAddressException { 312 checkJavaConfigured(); 313 utils.checkAlignment(address, jintSize); 314 if (useFastAccessors) { 315 return cache.getInt(address, bigEndian); 316 } else { 317 byte[] data = readBytes(address, jintSize); 318 return utils.dataToJInt(data, jintSize); 319 } 320 } 321 322 public long readJLong(long address) 323 throws UnmappedAddressException, UnalignedAddressException { 324 checkJavaConfigured(); 325 utils.checkAlignment(address, jlongSize); 326 if (useFastAccessors && canUsePageCacheFor64bitRead(address)) { 327 return cache.getLong(address, bigEndian); 328 } else { 329 byte[] data = readBytes(address, jlongSize); 330 return utils.dataToJLong(data, jlongSize); 331 } 332 } 333 334 public short readJShort(long address) 335 throws UnmappedAddressException, UnalignedAddressException { 336 checkJavaConfigured(); 337 utils.checkAlignment(address, jshortSize); 338 if (useFastAccessors) { 339 return cache.getShort(address, bigEndian); 340 } else { 341 byte[] data = readBytes(address, jshortSize); 342 return utils.dataToJShort(data, jshortSize); 343 } 344 } 345 346 public long readCInteger(long address, long numBytes, boolean isUnsigned) 347 throws UnmappedAddressException, UnalignedAddressException { 348 checkConfigured(); 349 utils.checkAlignment(address, numBytes); 350 if (useFastAccessors && (numBytes != 8 || canUsePageCacheFor64bitRead(address))) { 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 protected long readAddressValue(long address) 381 throws UnmappedAddressException, UnalignedAddressException { 382 return readCInteger(address, machDesc.getAddressSize(), true); 383 } 384 385 protected long readCompOopAddressValue(long address) 386 throws UnmappedAddressException, UnalignedAddressException { 387 long value = readCInteger(address, getHeapOopSize(), true); 388 if (value != 0) { 389 // See oop.inline.hpp decode_heap_oop 390 value = (long)(narrowOopBase + (long)(value << narrowOopShift)); 391 } 392 return value; 393 } 394 395 protected long readCompKlassAddressValue(long address) 396 throws UnmappedAddressException, UnalignedAddressException { 397 long value = readCInteger(address, getKlassPtrSize(), true); 398 if (value != 0) { 399 value = (long)(narrowKlassBase + (long)(value << narrowKlassShift)); 400 } 401 return value; 402 } 403 404 /** Can be called by subclasses but can not be overridden */ 405 protected final void checkConfigured() { 406 if (machDesc == null) { 407 throw new RuntimeException("MachineDescription must have been set by this point"); 408 } 409 if (utils == null) { 410 throw new RuntimeException("DebuggerUtilities must have been set by this point"); 411 } 412 } 413 414 /** Can be called by subclasses but can not be overridden */ 415 protected final void checkJavaConfigured() { 416 checkConfigured(); 417 418 if (!javaPrimitiveTypesConfigured) { 419 throw new RuntimeException("Java primitive type sizes have not yet been configured"); 420 } 421 } 422 423 /** Possibly override page cache size with user-specified property */ 424 protected int parseCacheNumPagesProperty(int defaultNum) { 425 String cacheNumPagesString = System.getProperty("cacheNumPages"); 426 if (cacheNumPagesString != null) { 427 try { 428 return Integer.parseInt(cacheNumPagesString); 429 } catch (Exception e) { 430 System.err.println("Error parsing cacheNumPages property:"); 431 e.printStackTrace(); 432 } 433 } 434 return defaultNum; 435 } 436 437 /** Interim solution for allowing subclasses to write bytes to 438 process until we make that functionality available in the basic 439 Address interface */ 440 protected void invalidatePageCache(long startAddress, long numBytes) { 441 cache.clear(startAddress, numBytes); 442 } 443 444 @Override 445 public String findSymbol(String symbol) { 446 Address addr = lookup(null, symbol); 447 if (addr == null && getOS().equals("win32")) { 448 // On win32 symbols are prefixed with the dll name. Do the user 449 // a favor and see if this is a symbol in jvm.dll or java.dll. 450 addr = lookup(null, "jvm!" + symbol); 451 if (addr == null) { 452 addr = lookup(null, "java!" + symbol); 453 } 454 } 455 if (addr == null) { 456 return null; 457 } 458 var builder = new StringBuilder(addr.toString()); 459 var cdbg = getCDebugger(); 460 var loadObject = cdbg.loadObjectContainingPC(addr); 461 // Print the shared library path and the offset of the symbol 462 if (loadObject != null) { 463 builder.append(": ").append(loadObject.getName()); 464 long diff = addr.minus(loadObject.getBase()); 465 if (diff != 0L) { 466 builder.append(" + 0x").append(Long.toHexString(diff)); 467 } 468 } 469 return builder.toString(); 470 } 471 472 public long getJBooleanSize() { 473 return jbooleanSize; 474 } 475 476 public long getJByteSize() { 477 return jbyteSize; 478 } 479 480 public long getJCharSize() { 481 return jcharSize; 482 } 483 484 public long getJDoubleSize() { 485 return jdoubleSize; 486 } 487 488 public long getJFloatSize() { 489 return jfloatSize; 490 } 491 492 public long getJIntSize() { 493 return jintSize; 494 } 495 496 public long getJLongSize() { 497 return jlongSize; 498 } 499 500 public long getJShortSize() { 501 return jshortSize; 502 } 503 504 public long getHeapOopSize() { 505 return heapOopSize; 506 } 507 508 public long getNarrowOopBase() { 509 return narrowOopBase; 510 } 511 public int getNarrowOopShift() { 512 return narrowOopShift; 513 } 514 515 public long getKlassPtrSize() { 516 return klassPtrSize; 517 } 518 519 public long getNarrowKlassBase() { 520 return narrowKlassBase; 521 } 522 public int getNarrowKlassShift() { 523 return narrowKlassShift; 524 } 525 }