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