1 /*
2  * Copyright (c) 2000, 2020, 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.oops;
26 
27 import java.io.*;
28 import java.util.*;
29 import sun.jvm.hotspot.debugger.*;
30 import sun.jvm.hotspot.runtime.*;
31 import sun.jvm.hotspot.types.*;
32 import sun.jvm.hotspot.utilities.*;
33 import sun.jvm.hotspot.utilities.Observable;
34 import sun.jvm.hotspot.utilities.Observer;
35 
36 // A MethodData provides interpreter profiling information
37 
38 public class MethodData extends Metadata implements MethodDataInterface<Klass,Method> {
39   static int TypeProfileWidth = 2;
40   static int BciProfileWidth = 2;
41   static int CompileThreshold;
42 
43   static int Reason_many;                 // indicates presence of several reasons
44   static int Reason_none;                 // indicates absence of a relevant deopt.
45   static int Reason_LIMIT;
46   static int Reason_RECORDED_LIMIT;       // some are not recorded per bc
47 
48   private static String[] trapReasonName;
49 
trapReasonName(int reason)50   static String trapReasonName(int reason) {
51     if (reason == Reason_many)  return "many";
52     if (reason < Reason_LIMIT)
53       return trapReasonName[reason];
54     return "reason" + reason;
55   }
56 
57 
trapStateReason(int trapState)58   static int trapStateReason(int trapState) {
59     // This assert provides the link between the width of DataLayout.trapBits
60     // and the encoding of "recorded" reasons.  It ensures there are enough
61     // bits to store all needed reasons in the per-BCI MDO profile.
62     // assert(dsReasonMask >= reasonRecordedLimit, "enough bits");
63     int recompileBit = (trapState & dsRecompileBit);
64     trapState -= recompileBit;
65     if (trapState == dsReasonMask) {
66       return Reason_many;
67     } else {
68       // assert((int)reasonNone == 0, "state=0 => Reason_none");
69       return trapState;
70     }
71   }
72 
73 
74   static final int dsReasonMask   = DataLayout.trapMask >> 1;
75   static final int dsRecompileBit = DataLayout.trapMask - dsReasonMask;
76 
trapStateIsRecompiled(int trapState)77   static boolean trapStateIsRecompiled(int trapState) {
78     return (trapState & dsRecompileBit) != 0;
79   }
80 
reasonIsRecordedPerBytecode(int reason)81   static boolean reasonIsRecordedPerBytecode(int reason) {
82     return reason > Reason_none && reason < Reason_RECORDED_LIMIT;
83   }
trapStateAddReason(int trapState, int reason)84   static int trapStateAddReason(int trapState, int reason) {
85     // assert(reasonIsRecordedPerBytecode((DeoptReason)reason) || reason == reasonMany, "valid reason");
86     int recompileBit = (trapState & dsRecompileBit);
87     trapState -= recompileBit;
88     if (trapState == dsReasonMask) {
89       return trapState + recompileBit;     // already at state lattice bottom
90     } else if (trapState == reason) {
91       return trapState + recompileBit;     // the condition is already true
92     } else if (trapState == 0) {
93       return reason + recompileBit;          // no condition has yet been true
94     } else {
95       return dsReasonMask + recompileBit;  // fall to state lattice bottom
96     }
97   }
trapStateSetRecompiled(int trapState, boolean z)98   static int trapStateSetRecompiled(int trapState, boolean z) {
99     if (z)  return trapState |  dsRecompileBit;
100     else    return trapState & ~dsRecompileBit;
101   }
102 
formatTrapState(int trapState)103   static String formatTrapState(int trapState) {
104     int reason      = trapStateReason(trapState);
105     boolean     recompFlag = trapStateIsRecompiled(trapState);
106     // Re-encode the state from its decoded components.
107     int decodedState = 0;
108     if (reasonIsRecordedPerBytecode(reason) || reason == Reason_many)
109       decodedState = trapStateAddReason(decodedState, reason);
110     if (recompFlag)
111       decodedState = trapStateSetRecompiled(decodedState, recompFlag);
112     // If the state re-encodes properly, format it symbolically.
113     // Because this routine is used for debugging and diagnostics,
114     // be robust even if the state is a strange value.
115     if (decodedState != trapState) {
116       // Random buggy state that doesn't decode??
117       return "#" + trapState;
118     } else {
119       return trapReasonName(reason) + (recompFlag ? " recompiled" : "");
120     }
121   }
122 
123 
124 
125   static {
VM.registerVMInitializedObserver(new Observer() { public void update(Observable o, Object data) { initialize(VM.getVM().getTypeDataBase()); } })126     VM.registerVMInitializedObserver(new Observer() {
127         public void update(Observable o, Object data) {
128           initialize(VM.getVM().getTypeDataBase());
129         }
130       });
131   }
132 
initialize(TypeDataBase db)133   private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
134     Type type      = db.lookupType("MethodData");
135     baseOffset     = type.getSize();
136 
137     size           = new CIntField(type.getCIntegerField("_size"), 0);
138     method         = new MetadataField(type.getAddressField("_method"), 0);
139 
140     VM.Flag[] flags = VM.getVM().getCommandLineFlags();
141     for (int f = 0; f < flags.length; f++) {
142       VM.Flag flag = flags[f];
143       if (flag.getName().equals("TypeProfileWidth")) {
144         TypeProfileWidth = (int)flag.getIntx();
145       } else if (flag.getName().equals("BciProfileWidth")) {
146         BciProfileWidth = (int)flag.getIntx();
147       } else if (flag.getName().equals("CompileThreshold")) {
148         CompileThreshold = (int)flag.getIntx();
149       }
150     }
151 
152     cellSize = (int)VM.getVM().getAddressSize();
153 
154     dataSize     = new CIntField(type.getCIntegerField("_data_size"), 0);
155     data         = type.getAddressField("_data[0]");
156 
157     parametersTypeDataDi = new CIntField(type.getCIntegerField("_parameters_type_data_di"), 0);
158 
159     sizeofMethodDataOopDesc = (int)type.getSize();
160 
161     Reason_many            = db.lookupIntConstant("Deoptimization::Reason_many").intValue();
162     Reason_none            = db.lookupIntConstant("Deoptimization::Reason_none").intValue();
163     Reason_LIMIT           = db.lookupIntConstant("Deoptimization::Reason_LIMIT").intValue();
164     Reason_RECORDED_LIMIT  = db.lookupIntConstant("Deoptimization::Reason_RECORDED_LIMIT").intValue();
165 
166     trapReasonName = new String[Reason_LIMIT];
167 
168     // Find Deopt reasons
169     Iterator i = db.getIntConstants();
170     String prefix = "Deoptimization::Reason_";
171     while (i.hasNext()) {
172       String name = (String)i.next();
173       if (name.startsWith(prefix)) {
174         // Strip prefix
175         if (!name.endsWith("Reason_many") &&
176             !name.endsWith("Reason_LIMIT") &&
177             !name.endsWith("Reason_RECORDED_LIMIT")) {
178           String trimmed = name.substring(prefix.length());
179           int value = db.lookupIntConstant(name).intValue();
180           if (trapReasonName[value] != null) {
181             throw new InternalError("duplicate reasons: " + trapReasonName[value] + " " + trimmed);
182           }
183           trapReasonName[value] = trimmed;
184         }
185       }
186     }
187     for (int index = 0; index < trapReasonName.length; index++) {
188       if (trapReasonName[index] == null) {
189         throw new InternalError("missing reason for " + index);
190       }
191     }
192   }
193 
MethodData(Address addr)194   public MethodData(Address addr) {
195     super(addr);
196   }
197 
getKlassAtAddress(Address addr)198   public Klass getKlassAtAddress(Address addr) {
199     return (Klass)Metadata.instantiateWrapperFor(addr);
200   }
201 
getMethodAtAddress(Address addr)202   public Method getMethodAtAddress(Address addr) {
203     return (Method)Metadata.instantiateWrapperFor(addr);
204   }
205 
printKlassValueOn(Klass klass, PrintStream st)206   public void printKlassValueOn(Klass klass, PrintStream st) {
207     klass.printValueOn(st);
208   }
209 
printMethodValueOn(Method method, PrintStream st)210   public void printMethodValueOn(Method method, PrintStream st) {
211     method.printValueOn(st);
212   }
213 
isMethodData()214   public boolean isMethodData()        { return true; }
215 
216   private static long baseOffset;
217   private static CIntField size;
218   private static MetadataField  method;
219   private static CIntField dataSize;
220   private static AddressField data;
221   private static CIntField parametersTypeDataDi;
222   public static int sizeofMethodDataOopDesc;
223   public static int cellSize;
224 
getMethod()225   public Method getMethod() {
226     return (Method) method.getValue(this);
227   }
228 
printValueOn(PrintStream tty)229   public void printValueOn(PrintStream tty) {
230     Method m = getMethod();
231     tty.print("MethodData for " + m.getName().asString() + m.getSignature().asString());
232   }
233 
iterateFields(MetadataVisitor visitor)234   public void iterateFields(MetadataVisitor visitor) {
235     super.iterateFields(visitor);
236     visitor.doMetadata(method, true);
237       visitor.doCInt(size, true);
238     }
239 
dataSize()240   int dataSize() {
241     if (dataSize == null) {
242       return 0;
243     } else {
244       return (int)dataSize.getValue(getAddress());
245     }
246   }
247 
sizeInBytes()248   int sizeInBytes() {
249     if (size == null) {
250       return 0;
251     } else {
252       return (int)size.getValue(getAddress());
253     }
254   }
255 
size()256   int size() {
257     return (int)alignSize(VM.getVM().alignUp(sizeInBytes(), VM.getVM().getBytesPerWord())/VM.getVM().getBytesPerWord());
258   }
259 
parametersTypeData()260   ParametersTypeData<Klass,Method> parametersTypeData() {
261     int di = (int)parametersTypeDataDi.getValue(getAddress());
262     if (di == -1 || di == -2) {
263       return null;
264     }
265     DataLayout dataLayout = new DataLayout(this, di + (int)data.getOffset());
266     return new ParametersTypeData<Klass,Method>(this, dataLayout);
267   }
268 
outOfBounds(int dataIndex)269   boolean outOfBounds(int dataIndex) {
270     return dataIndex >= dataSize();
271   }
272 
dataAt(int dataIndex)273   ProfileData dataAt(int dataIndex) {
274     if (outOfBounds(dataIndex)) {
275       return null;
276     }
277     DataLayout dataLayout = new DataLayout(this, dataIndex + (int)data.getOffset());
278 
279     switch (dataLayout.tag()) {
280     case DataLayout.noTag:
281     default:
282       throw new InternalError(dataIndex + " " + dataSize() + " " + dataLayout.tag());
283     case DataLayout.bitDataTag:
284       return new BitData(dataLayout);
285     case DataLayout.counterDataTag:
286       return new CounterData(dataLayout);
287     case DataLayout.jumpDataTag:
288       return new JumpData(dataLayout);
289     case DataLayout.receiverTypeDataTag:
290       return new ReceiverTypeData<Klass,Method>(this, dataLayout);
291     case DataLayout.virtualCallDataTag:
292       return new VirtualCallData<Klass,Method>(this, dataLayout);
293     case DataLayout.retDataTag:
294       return new RetData(dataLayout);
295     case DataLayout.branchDataTag:
296       return new BranchData(dataLayout);
297     case DataLayout.multiBranchDataTag:
298       return new MultiBranchData(dataLayout);
299     case DataLayout.callTypeDataTag:
300       return new CallTypeData<Klass,Method>(this, dataLayout);
301     case DataLayout.virtualCallTypeDataTag:
302       return new VirtualCallTypeData<Klass,Method>(this, dataLayout);
303     case DataLayout.parametersTypeDataTag:
304       return new ParametersTypeData<Klass,Method>(this, dataLayout);
305     }
306   }
307 
dpToDi(int dp)308   int dpToDi(int dp) {
309     // this in an offset from the base of the MDO, so convert to offset into _data
310     return dp - (int)data.getOffset();
311   }
312 
firstDi()313   int firstDi() { return 0; }
firstData()314   public ProfileData firstData() { return dataAt(firstDi()); }
nextData(ProfileData current)315   public ProfileData nextData(ProfileData current) {
316     int currentIndex = dpToDi(current.dp());
317     int nextIndex = currentIndex + current.sizeInBytes();
318     return dataAt(nextIndex);
319   }
isValid(ProfileData current)320   boolean isValid(ProfileData current) { return current != null; }
321 
limitDataPosition()322   DataLayout limitDataPosition() {
323     return new DataLayout(this, dataSize() + (int)data.getOffset());
324   }
325 
extraDataBase()326   DataLayout extraDataBase() {
327     return limitDataPosition();
328   }
329 
extraDataLimit()330   DataLayout extraDataLimit() {
331     return new DataLayout(this, sizeInBytes());
332   }
333 
extraNbCells(DataLayout dataLayout)334   static public int extraNbCells(DataLayout dataLayout) {
335     int nbCells = 0;
336     switch(dataLayout.tag()) {
337     case DataLayout.bitDataTag:
338     case DataLayout.noTag:
339       nbCells = BitData.staticCellCount();
340       break;
341     case DataLayout.speculativeTrapDataTag:
342       nbCells = SpeculativeTrapData.staticCellCount();
343       break;
344     default:
345       throw new InternalError("unexpected tag " +  dataLayout.tag());
346     }
347     return nbCells;
348   }
349 
nextExtra(DataLayout dataLayout)350   DataLayout nextExtra(DataLayout dataLayout) {
351     return new DataLayout(this, dataLayout.dp() + DataLayout.computeSizeInBytes(extraNbCells(dataLayout)));
352   }
353 
printDataOn(PrintStream st)354   public void printDataOn(PrintStream st) {
355     if (parametersTypeData() != null) {
356       parametersTypeData().printDataOn(st);
357     }
358     ProfileData data = firstData();
359     for ( ; isValid(data); data = nextData(data)) {
360       st.print(dpToDi(data.dp()));
361       st.print(" ");
362       // st->fillTo(6);
363       data.printDataOn(st);
364     }
365     st.println("--- Extra data:");
366     DataLayout dp    = extraDataBase();
367     DataLayout end   = extraDataLimit();
368     for (;; dp = nextExtra(dp)) {
369       switch(dp.tag()) {
370       case DataLayout.noTag:
371         continue;
372       case DataLayout.bitDataTag:
373         data = new BitData(dp);
374         break;
375       case DataLayout.speculativeTrapDataTag:
376         data = new SpeculativeTrapData<Klass,Method>(this, dp);
377         break;
378       case DataLayout.argInfoDataTag:
379         data = new ArgInfoData(dp);
380         dp = end; // ArgInfoData is at the end of extra data section.
381         break;
382       default:
383         throw new InternalError("unexpected tag " +  dp.tag());
384       }
385       st.print(dpToDi(data.dp()));
386       st.print(" ");
387       data.printDataOn(st);
388       if (dp == end) return;
389     }
390   }
391 
fetchDataAt(Address base, long offset, long size)392   private byte[] fetchDataAt(Address base, long offset, long size) {
393     byte[] result = new byte[(int)size];
394     for (int i = 0; i < size; i++) {
395       result[i] = base.getJByteAt(offset + i);
396     }
397     return result;
398   }
399 
orig()400   public byte[] orig() {
401     // fetch the orig MethodData data between header and dataSize
402     return fetchDataAt(getAddress(), 0, sizeofMethodDataOopDesc);
403   }
404 
data()405   public long[] data() {
406     // Read the data as an array of intptr_t elements
407     Address base = getAddress();
408     long offset = data.getOffset();
409     int elements = dataSize() / cellSize;
410     long[] result = new long[elements];
411     for (int i = 0; i < elements; i++) {
412       Address value = base.getAddressAt(offset + i * MethodData.cellSize);
413       if (value != null) {
414         result[i] = value.minus(null);
415       }
416     }
417     return result;
418   }
419 
420   // Get a measure of how much mileage the method has on it.
mileageOf(Method method)421   int mileageOf(Method method) {
422     long mileage = 0;
423     int iic = method.interpreterInvocationCount();
424     if (mileage < iic)  mileage = iic;
425 
426     long ic = method.getInvocationCount();
427     long bc = method.getBackedgeCount();
428 
429     long icval = ic >> 3;
430     if ((ic & 4) != 0) icval += CompileThreshold;
431     if (mileage < icval)  mileage = icval;
432     long bcval = bc >> 3;
433     if ((bc & 4) != 0) bcval += CompileThreshold;
434     if (mileage < bcval)  mileage = bcval;
435     return (int)mileage;
436   }
437 
currentMileage()438   public int currentMileage() {
439     return 20000;
440   }
441 
dumpReplayDataTypeHelper(PrintStream out, int round, int count, int index, ProfileData pdata, Klass k)442   int dumpReplayDataTypeHelper(PrintStream out, int round, int count, int index, ProfileData pdata, Klass k) {
443     if (k != null) {
444       if (round == 0) count++;
445       else out.print(" " +
446                      (dpToDi(pdata.dp() +
447                              pdata.cellOffset(index)) / cellSize) + " " +
448                      k.getName().asString());
449     }
450     return count;
451   }
452 
dumpReplayDataReceiverTypeHelper(PrintStream out, int round, int count, ReceiverTypeData<Klass,Method> vdata)453   int dumpReplayDataReceiverTypeHelper(PrintStream out, int round, int count, ReceiverTypeData<Klass,Method> vdata) {
454     for (int i = 0; i < vdata.rowLimit(); i++) {
455       Klass k = vdata.receiver(i);
456       count = dumpReplayDataTypeHelper(out, round, count, vdata.receiverCellIndex(i), vdata, k);
457     }
458     return count;
459   }
460 
dumpReplayDataCallTypeHelper(PrintStream out, int round, int count, CallTypeDataInterface<Klass> callTypeData)461   int dumpReplayDataCallTypeHelper(PrintStream out, int round, int count, CallTypeDataInterface<Klass> callTypeData) {
462     if (callTypeData.hasArguments()) {
463       for (int i = 0; i < callTypeData.numberOfArguments(); i++) {
464         count = dumpReplayDataTypeHelper(out, round, count, callTypeData.argumentTypeIndex(i), (ProfileData)callTypeData, callTypeData.argumentType(i));
465       }
466     }
467     if (callTypeData.hasReturn()) {
468       count = dumpReplayDataTypeHelper(out, round, count, callTypeData.returnTypeIndex(), (ProfileData)callTypeData, callTypeData.returnType());
469     }
470     return count;
471   }
472 
dumpReplayDataExtraDataHelper(PrintStream out, int round, int count)473   int dumpReplayDataExtraDataHelper(PrintStream out, int round, int count) {
474     DataLayout dp    = extraDataBase();
475     DataLayout end   = extraDataLimit();
476 
477     for (;dp != end; dp = nextExtra(dp)) {
478       switch(dp.tag()) {
479       case DataLayout.noTag:
480       case DataLayout.argInfoDataTag:
481         return count;
482       case DataLayout.bitDataTag:
483         break;
484       case DataLayout.speculativeTrapDataTag: {
485         SpeculativeTrapData<Klass,Method> data = new SpeculativeTrapData<Klass,Method>(this, dp);
486         Method m = data.method();
487         if (m != null) {
488           if (round == 0) {
489             count++;
490           } else {
491             out.print(" " +  (dpToDi(data.dp() + data.cellOffset(SpeculativeTrapData.methodIndex())) / cellSize) + " " +  m.nameAsAscii());
492           }
493         }
494         break;
495       }
496       default:
497         throw new InternalError("bad tag "  + dp.tag());
498       }
499     }
500     return count;
501   }
502 
dumpReplayData(PrintStream out)503   public void dumpReplayData(PrintStream out) {
504     Method method = getMethod();
505     out.print("ciMethodData " + method.nameAsAscii()
506               + " " + "2" + " " +
507               currentMileage());
508     byte[] orig = orig();
509     out.print(" orig " + orig.length);
510     for (int i = 0; i < orig.length; i++) {
511       out.print(" " + (orig[i] & 0xff));
512     }
513 
514     long[] data = data();
515     out.print(" data " +  data.length);
516     for (int i = 0; i < data.length; i++) {
517       out.print(" 0x" + Long.toHexString(data[i]));
518     }
519     int count = 0;
520     ParametersTypeData<Klass,Method> parameters = parametersTypeData();
521     for (int round = 0; round < 2; round++) {
522       if (round == 1) out.print(" oops " + count);
523       ProfileData pdata = firstData();
524       for ( ; isValid(pdata); pdata = nextData(pdata)) {
525         if (pdata instanceof ReceiverTypeData) {
526           @SuppressWarnings("unchecked")
527           ReceiverTypeData<Klass,Method> receiverTypeData = (ReceiverTypeData<Klass,Method>)pdata;
528           count = dumpReplayDataReceiverTypeHelper(out, round, count, receiverTypeData);
529         }
530         if (pdata instanceof CallTypeDataInterface) {
531           @SuppressWarnings("unchecked")
532           CallTypeDataInterface<Klass> callTypeData = (CallTypeDataInterface<Klass>)pdata;
533           count = dumpReplayDataCallTypeHelper(out, round, count, callTypeData);
534         }
535       }
536       if (parameters != null) {
537         for (int i = 0; i < parameters.numberOfParameters(); i++) {
538           count = dumpReplayDataTypeHelper(out, round, count, ParametersTypeData.typeIndex(i), parameters, parameters.type(i));
539         }
540       }
541     }
542     count = 0;
543     for (int round = 0; round < 2; round++) {
544       if (round == 1) out.print(" methods " + count);
545       count = dumpReplayDataExtraDataHelper(out, round, count);
546     }
547     out.println();
548   }
549 }
550