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