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 }