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 }