1 /*
  2  * Copyright (c) 2001, 2021, 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 
 68   // State for faster accessors that don't allocate memory on each read
 69   private boolean useFastAccessors;
 70   private boolean bigEndian;
 71 
 72   // Page-fetching functionality for LRU cache
 73   class Fetcher implements PageFetcher {
 74     public Page fetchPage(long pageBaseAddress, long numBytes) {
 75       // This assumes that if any byte is unmapped, that the entire
 76       // page is. The common case, however, is that the page is
 77       // mapped, so we always fetch the entire thing all at once to
 78       // avoid two round-trip communications per page fetch, even
 79       // though fetching of unmapped pages will be slow.
 80       ReadResult res = readBytesFromProcess(pageBaseAddress, numBytes);
 81       if (res.getData() == null) {
 82         return new Page(pageBaseAddress, numBytes);
 83       }
 84       return new Page(pageBaseAddress, res.getData());
 85     }
 86   }
 87 
 88   protected DebuggerBase() {
 89   }
 90 
 91   /** From the JVMDebugger interface. This is the only public method
 92       of this class. */
 93   public void configureJavaPrimitiveTypeSizes(long jbooleanSize,
 94                                               long jbyteSize,
 95                                               long jcharSize,
 96                                               long jdoubleSize,
 97                                               long jfloatSize,
 98                                               long jintSize,
 99                                               long jlongSize,
100                                               long jshortSize) {
101     this.jbooleanSize = jbooleanSize;
102     this.jbyteSize = jbyteSize;
103     this.jcharSize = jcharSize;
104     this.jdoubleSize = jdoubleSize;
105     this.jfloatSize = jfloatSize;
106     this.jintSize = jintSize;
107     this.jlongSize = jlongSize;
108     this.jshortSize = jshortSize;
109 
110     if (jbooleanSize < 1) {
111       throw new RuntimeException("jboolean size is too small");
112     }
113 
114     if (jbyteSize < 1) {
115       throw new RuntimeException("jbyte size is too small");
116     }
117 
118     if (jcharSize < 2) {
119       throw new RuntimeException("jchar size is too small");
120     }
121 
122     if (jdoubleSize < 8) {
123       throw new RuntimeException("jdouble size is too small");
124     }
125 
126     if (jfloatSize < 4) {
127       throw new RuntimeException("jfloat size is too small");
128     }
129 
130     if (jintSize < 4) {
131       throw new RuntimeException("jint size is too small");
132     }
133 
134     if (jlongSize < 8) {
135       throw new RuntimeException("jlong size is too small");
136     }
137 
138     if (jshortSize < 2) {
139       throw new RuntimeException("jshort size is too small");
140     }
141 
142     if (jintSize != jfloatSize) {
143       // If dataToJFloat were rewritten, this wouldn't be necessary
144       throw new RuntimeException("jint size and jfloat size must be equal");
145     }
146 
147     if (jlongSize != jdoubleSize) {
148       // If dataToJDouble were rewritten, this wouldn't be necessary
149       throw new RuntimeException("jlong size and jdouble size must be equal");
150     }
151 
152     useFastAccessors =
153       ((cache != null) &&
154        (jbooleanSize == 1) &&
155        (jbyteSize    == 1) &&
156        (jcharSize    == 2) &&
157        (jdoubleSize  == 8) &&
158        (jfloatSize   == 4) &&
159        (jintSize     == 4) &&
160        (jlongSize    == 8) &&
161        (jshortSize   == 2));
162 
163     javaPrimitiveTypesConfigured = true;
164   }
165 
166   public void putHeapConst(long heapOopSize, long klassPtrSize, long narrowOopBase, int narrowOopShift,
167                            long narrowKlassBase, int narrowKlassShift) {
168     this.heapOopSize = heapOopSize;
169     this.klassPtrSize = klassPtrSize;
170     this.narrowOopBase = narrowOopBase;
171     this.narrowOopShift = narrowOopShift;
172     this.narrowKlassBase = narrowKlassBase;
173     this.narrowKlassShift = narrowKlassShift;
174   }
175 
176   /** May be called by subclasses if desired to initialize the page
177       cache but may not be overridden */
178   protected final void initCache(long pageSize, long maxNumPages) {
179     cache = new PageCache(pageSize, maxNumPages, new Fetcher());
180     if (machDesc != null) {
181       bigEndian = machDesc.isBigEndian();
182     }
183   }
184 
185   /** May be called by subclasses if needed (if the machine
186       description is not available at the time of cache
187       initialization, as on Solaris) but may not be overridden */
188   protected final void setBigEndian(boolean bigEndian) {
189     this.bigEndian = bigEndian;
190   }
191 
192   /** May be called by subclasses to clear out the cache but may not
193       be overridden. For convenience, this can be called even if the
194       cache has not been initialized. */
195   protected final void clearCache() {
196     if (cache != null) {
197       cache.clear();
198     }
199   }
200 
201   /** May be called by subclasses to disable the cache (for example,
202       when the target process has been resumed) but may not be
203       overridden. For convenience, this can be called even if the
204       cache has not been initialized. */
205   protected final void disableCache() {
206     if (cache != null) {
207       cache.disable();
208     }
209   }
210 
211   /** May be called by subclasses to re-enable the cache (for example,
212       when the target process has been suspended) but may not be
213       overridden. For convenience, this can be called even if the
214       cache has not been initialized. */
215   protected final void enableCache() {
216     if (cache != null) {
217       cache.enable();
218     }
219   }
220 
221   /** May be called by subclasses directly but may not be overridden */
222   protected final byte[] readBytes(long address, long numBytes)
223     throws UnmappedAddressException, DebuggerException {
224     if (cache != null) {
225       return cache.getData(address, numBytes);
226     } else {
227       ReadResult res = readBytesFromProcess(address, numBytes);
228       if (res.getData() != null) {
229         return res.getData();
230       }
231       throw new UnmappedAddressException(res.getFailureAddress());
232     }
233   }
234 
235   /** May be called by subclasses directly but may not be overridden */
236   protected final void writeBytes(long address, long numBytes, byte[] data)
237     throws UnmappedAddressException, DebuggerException {
238     if (cache != null) {
239       cache.clear(address, numBytes);
240     }
241     writeBytesToProcess(address, numBytes, data);
242   }
243 
244   public boolean readJBoolean(long address)
245     throws UnmappedAddressException, UnalignedAddressException {
246     checkJavaConfigured();
247     utils.checkAlignment(address, jbooleanSize);
248     if (useFastAccessors) {
249       return (cache.getByte(address) != 0);
250     } else {
251       byte[] data = readBytes(address, jbooleanSize);
252       return utils.dataToJBoolean(data, jbooleanSize);
253     }
254   }
255 
256   public byte readJByte(long address)
257     throws UnmappedAddressException, UnalignedAddressException {
258     checkJavaConfigured();
259     utils.checkAlignment(address, jbyteSize);
260     if (useFastAccessors) {
261       return cache.getByte(address);
262     } else {
263       byte[] data = readBytes(address, jbyteSize);
264       return utils.dataToJByte(data, jbyteSize);
265     }
266   }
267 
268   // NOTE: assumes value does not span pages (may be bad assumption on
269   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
270   public char readJChar(long address)
271     throws UnmappedAddressException, UnalignedAddressException {
272     checkJavaConfigured();
273     utils.checkAlignment(address, jcharSize);
274     if (useFastAccessors) {
275       return cache.getChar(address, bigEndian);
276     } else {
277       byte[] data = readBytes(address, jcharSize);
278       return (char) utils.dataToJChar(data, jcharSize);
279     }
280   }
281 
282   // NOTE: assumes value does not span pages (may be bad assumption on
283   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
284   public double readJDouble(long address)
285     throws UnmappedAddressException, UnalignedAddressException {
286     checkJavaConfigured();
287     utils.checkAlignment(address, jdoubleSize);
288     if (useFastAccessors) {
289       return cache.getDouble(address, bigEndian);
290     } else {
291       byte[] data = readBytes(address, jdoubleSize);
292       return utils.dataToJDouble(data, jdoubleSize);
293     }
294   }
295 
296   // NOTE: assumes value does not span pages (may be bad assumption on
297   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
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   // NOTE: assumes value does not span pages (may be bad assumption on
311   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
312   public int readJInt(long address)
313     throws UnmappedAddressException, UnalignedAddressException {
314     checkJavaConfigured();
315     utils.checkAlignment(address, jintSize);
316     if (useFastAccessors) {
317       return cache.getInt(address, bigEndian);
318     } else {
319       byte[] data = readBytes(address, jintSize);
320       return utils.dataToJInt(data, jintSize);
321     }
322   }
323 
324   // NOTE: assumes value does not span pages (may be bad assumption on
325   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
326   public long readJLong(long address)
327     throws UnmappedAddressException, UnalignedAddressException {
328     checkJavaConfigured();
329     utils.checkAlignment(address, jlongSize);
330     if (useFastAccessors) {
331       return cache.getLong(address, bigEndian);
332     } else {
333       byte[] data = readBytes(address, jlongSize);
334       return utils.dataToJLong(data, jlongSize);
335     }
336   }
337 
338   // NOTE: assumes value does not span pages (may be bad assumption on
339   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
340   public short readJShort(long address)
341     throws UnmappedAddressException, UnalignedAddressException {
342     checkJavaConfigured();
343     utils.checkAlignment(address, jshortSize);
344     if (useFastAccessors) {
345       return cache.getShort(address, bigEndian);
346     } else {
347       byte[] data = readBytes(address, jshortSize);
348       return utils.dataToJShort(data, jshortSize);
349     }
350   }
351 
352   // NOTE: assumes value does not span pages (may be bad assumption on
353   // Solaris/x86; see unalignedAccessesOkay in DbxDebugger hierarchy)
354   public long readCInteger(long address, long numBytes, boolean isUnsigned)
355     throws UnmappedAddressException, UnalignedAddressException {
356     checkConfigured();
357     utils.checkAlignment(address, numBytes);
358     if (useFastAccessors) {
359       if (isUnsigned) {
360         switch((int) numBytes) {
361         case 1: return cache.getByte(address) & 0xFF;
362         case 2: return cache.getShort(address, bigEndian) & 0xFFFF;
363         case 4: return cache.getInt(address, bigEndian) & 0xFFFFFFFFL;
364         case 8: return cache.getLong(address, bigEndian);
365         default: {
366           byte[] data = readBytes(address, numBytes);
367           return utils.dataToCInteger(data, isUnsigned);
368         }
369         }
370       } else {
371         switch((int) numBytes) {
372         case 1: return cache.getByte(address);
373         case 2: return cache.getShort(address, bigEndian);
374         case 4: return cache.getInt(address, bigEndian);
375         case 8: return cache.getLong(address, bigEndian);
376         default: {
377           byte[] data = readBytes(address, numBytes);
378           return utils.dataToCInteger(data, isUnsigned);
379         }
380         }
381       }
382     } else {
383       byte[] data = readBytes(address, numBytes);
384       return utils.dataToCInteger(data, isUnsigned);
385     }
386   }
387 
388   public void writeJBoolean(long address, boolean value)
389     throws UnmappedAddressException, UnalignedAddressException {
390     checkJavaConfigured();
391     utils.checkAlignment(address, jbooleanSize);
392     byte[] data = utils.jbooleanToData(value);
393     writeBytes(address, jbooleanSize, data);
394   }
395 
396   public void writeJByte(long address, byte value)
397     throws UnmappedAddressException, UnalignedAddressException {
398     checkJavaConfigured();
399     utils.checkAlignment(address, jbyteSize);
400     byte[] data = utils.jbyteToData(value);
401     writeBytes(address, jbyteSize, data);
402   }
403 
404   public void writeJChar(long address, char value)
405     throws UnmappedAddressException, UnalignedAddressException {
406     checkJavaConfigured();
407     utils.checkAlignment(address, jcharSize);
408     byte[] data = utils.jcharToData(value);
409     writeBytes(address, jcharSize, data);
410   }
411 
412   public void writeJDouble(long address, double value)
413     throws UnmappedAddressException, UnalignedAddressException {
414     checkJavaConfigured();
415     utils.checkAlignment(address, jdoubleSize);
416     byte[] data = utils.jdoubleToData(value);
417     writeBytes(address, jdoubleSize, data);
418   }
419 
420   public void writeJFloat(long address, float value)
421     throws UnmappedAddressException, UnalignedAddressException {
422     checkJavaConfigured();
423     utils.checkAlignment(address, jfloatSize);
424     byte[] data = utils.jfloatToData(value);
425     writeBytes(address, jfloatSize, data);
426   }
427 
428   public void writeJInt(long address, int value)
429     throws UnmappedAddressException, UnalignedAddressException {
430     checkJavaConfigured();
431     utils.checkAlignment(address, jintSize);
432     byte[] data = utils.jintToData(value);
433     writeBytes(address, jintSize, data);
434   }
435 
436   public void writeJLong(long address, long value)
437     throws UnmappedAddressException, UnalignedAddressException {
438     checkJavaConfigured();
439     utils.checkAlignment(address, jlongSize);
440     byte[] data = utils.jlongToData(value);
441     writeBytes(address, jlongSize, data);
442   }
443 
444   public void writeJShort(long address, short value)
445     throws UnmappedAddressException, UnalignedAddressException {
446     checkJavaConfigured();
447     utils.checkAlignment(address, jshortSize);
448     byte[] data = utils.jshortToData(value);
449     writeBytes(address, jshortSize, data);
450   }
451 
452   public void writeCInteger(long address, long numBytes, long value)
453     throws UnmappedAddressException, UnalignedAddressException {
454     checkConfigured();
455     utils.checkAlignment(address, numBytes);
456     byte[] data = utils.cIntegerToData(numBytes, value);
457     writeBytes(address, numBytes, data);
458   }
459 
460   protected long readAddressValue(long address)
461     throws UnmappedAddressException, UnalignedAddressException {
462     return readCInteger(address, machDesc.getAddressSize(), true);
463   }
464 
465   protected long readCompOopAddressValue(long address)
466     throws UnmappedAddressException, UnalignedAddressException {
467     long value = readCInteger(address, getHeapOopSize(), true);
468     if (value != 0) {
469       // See oop.inline.hpp decode_heap_oop
470       value = (long)(narrowOopBase + (long)(value << narrowOopShift));
471     }
472     return value;
473   }
474 
475   protected long readCompKlassAddressValue(long address)
476     throws UnmappedAddressException, UnalignedAddressException {
477     long value = readCInteger(address, getKlassPtrSize(), true);
478     // Todo: Lilliput: this is a hack. The real problem is the assumption that size
479     //  of a narrow Klass pointer can be expressed in number of bytes (getKlassPtrSize).
480     //  That assumption is present in a number of files here. Better would be
481     //  to change this to getKlassPtrSizeInBits, or to do it some other way.
482     value &= (1 << 22) - 1; // narrow klass pointer size is 22 bits.
483     if (value != 0) {
484       value = (long)(narrowKlassBase + (long)(value << narrowKlassShift));
485     }
486     return value;
487   }
488 
489   protected void writeAddressValue(long address, long value)
490     throws UnmappedAddressException, UnalignedAddressException {
491     writeCInteger(address, machDesc.getAddressSize(), value);
492   }
493 
494   /** Can be called by subclasses but can not be overridden */
495   protected final void checkConfigured() {
496     if (machDesc == null) {
497       throw new RuntimeException("MachineDescription must have been set by this point");
498     }
499     if (utils == null) {
500       throw new RuntimeException("DebuggerUtilities must have been set by this point");
501     }
502   }
503 
504   /** Can be called by subclasses but can not be overridden */
505   protected final void checkJavaConfigured() {
506     checkConfigured();
507 
508     if (!javaPrimitiveTypesConfigured) {
509       throw new RuntimeException("Java primitive type sizes have not yet been configured");
510     }
511   }
512 
513   /** Possibly override page cache size with user-specified property */
514   protected int parseCacheNumPagesProperty(int defaultNum) {
515     String cacheNumPagesString = System.getProperty("cacheNumPages");
516     if (cacheNumPagesString != null) {
517       try {
518         return Integer.parseInt(cacheNumPagesString);
519       } catch (Exception e) {
520         System.err.println("Error parsing cacheNumPages property:");
521         e.printStackTrace();
522       }
523     }
524     return defaultNum;
525   }
526 
527   /** Interim solution for allowing subclasses to write bytes to
528       process until we make that functionality available in the basic
529       Address interface */
530   protected void invalidatePageCache(long startAddress, long numBytes) {
531     cache.clear(startAddress, numBytes);
532   }
533 
534   @Override
535   public String findSymbol(String symbol) {
536     Address addr = lookup(null, symbol);
537     if (addr == null && getOS().equals("win32")) {
538       // On win32 symbols are prefixed with the dll name. Do the user
539       // a favor and see if this is a symbol in jvm.dll or java.dll.
540       addr = lookup(null, "jvm!" + symbol);
541       if (addr == null) {
542         addr = lookup(null, "java!" + symbol);
543       }
544     }
545     if (addr == null) {
546       return null;
547     }
548     var builder = new StringBuilder(addr.toString());
549     var cdbg = getCDebugger();
550     var loadObject = cdbg.loadObjectContainingPC(addr);
551     // Print the shared library path and the offset of the symbol
552     if (loadObject != null) {
553       builder.append(": ").append(loadObject.getName());
554       long diff = addr.minus(loadObject.getBase());
555       if (diff != 0L) {
556         builder.append(" + 0x").append(Long.toHexString(diff));
557       }
558     }
559     return builder.toString();
560   }
561 
562   public long getJBooleanSize() {
563     return jbooleanSize;
564   }
565 
566   public long getJByteSize() {
567     return jbyteSize;
568   }
569 
570   public long getJCharSize() {
571     return jcharSize;
572   }
573 
574   public long getJDoubleSize() {
575     return jdoubleSize;
576   }
577 
578   public long getJFloatSize() {
579     return jfloatSize;
580   }
581 
582   public long getJIntSize() {
583     return jintSize;
584   }
585 
586   public long getJLongSize() {
587     return jlongSize;
588   }
589 
590   public long getJShortSize() {
591     return jshortSize;
592   }
593 
594   public long getHeapOopSize() {
595     return heapOopSize;
596   }
597 
598   public long getNarrowOopBase() {
599     return narrowOopBase;
600   }
601   public int getNarrowOopShift() {
602     return narrowOopShift;
603   }
604 
605   public long getKlassPtrSize() {
606     return klassPtrSize;
607   }
608 
609   public long getNarrowKlassBase() {
610     return narrowKlassBase;
611   }
612   public int getNarrowKlassShift() {
613     return narrowKlassShift;
614   }
615 }