1 /*
   2  * Copyright (c) 2000, 2011, 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 
  34 // A MethodData provides interpreter profiling information
  35 
  36 public class MethodData extends Oop {
  37   static int TypeProfileWidth = 2;
  38   static int BciProfileWidth = 2;
  39   static int CompileThreshold;
  40 
  41   static int Reason_many;                 // indicates presence of several reasons
  42   static int Reason_none;                 // indicates absence of a relevant deopt.
  43   static int Reason_LIMIT;
  44   static int Reason_RECORDED_LIMIT;       // some are not recorded per bc
  45 
  46   private static String[] trapReasonName;
  47 
  48   static String trapReasonName(int reason) {
  49     if (reason == Reason_many)  return "many";
  50     if (reason < Reason_LIMIT)
  51       return trapReasonName[reason];
  52     return "reason" + reason;
  53   }
  54 
  55 
  56   static int trapStateReason(int trapState) {
  57     // This assert provides the link between the width of DataLayout.trapBits
  58     // and the encoding of "recorded" reasons.  It ensures there are enough
  59     // bits to store all needed reasons in the per-BCI MDO profile.
  60     // assert(dsReasonMask >= reasonRecordedLimit, "enough bits");
  61     int recompileBit = (trapState & dsRecompileBit);
  62     trapState -= recompileBit;
  63     if (trapState == dsReasonMask) {
  64       return Reason_many;
  65     } else {
  66       // assert((int)reasonNone == 0, "state=0 => Reason_none");
  67       return trapState;
  68     }
  69   }
  70 
  71 
  72   static final int dsReasonMask   = DataLayout.trapMask >> 1;
  73   static final int dsRecompileBit = DataLayout.trapMask - dsReasonMask;
  74 
  75   static boolean trapStateIsRecompiled(int trapState) {
  76     return (trapState & dsRecompileBit) != 0;
  77   }
  78 
  79   static boolean reasonIsRecordedPerBytecode(int reason) {
  80     return reason > Reason_none && reason < Reason_RECORDED_LIMIT;
  81   }
  82   static int trapStateAddReason(int trapState, int reason) {
  83     // assert(reasonIsRecordedPerBytecode((DeoptReason)reason) || reason == reasonMany, "valid reason");
  84     int recompileBit = (trapState & dsRecompileBit);
  85     trapState -= recompileBit;
  86     if (trapState == dsReasonMask) {
  87       return trapState + recompileBit;     // already at state lattice bottom
  88     } else if (trapState == reason) {
  89       return trapState + recompileBit;     // the condition is already true
  90     } else if (trapState == 0) {
  91       return reason + recompileBit;          // no condition has yet been true
  92     } else {
  93       return dsReasonMask + recompileBit;  // fall to state lattice bottom
  94     }
  95   }
  96   static int trapStateSetRecompiled(int trapState, boolean z) {
  97     if (z)  return trapState |  dsRecompileBit;
  98     else    return trapState & ~dsRecompileBit;
  99   }
 100 
 101   static String formatTrapState(int trapState) {
 102     int reason      = trapStateReason(trapState);
 103     boolean     recompFlag = trapStateIsRecompiled(trapState);
 104     // Re-encode the state from its decoded components.
 105     int decodedState = 0;
 106     if (reasonIsRecordedPerBytecode(reason) || reason == Reason_many)
 107       decodedState = trapStateAddReason(decodedState, reason);
 108     if (recompFlag)
 109       decodedState = trapStateSetRecompiled(decodedState, recompFlag);
 110     // If the state re-encodes properly, format it symbolically.
 111     // Because this routine is used for debugging and diagnostics,
 112     // be robust even if the state is a strange value.
 113     if (decodedState != trapState) {
 114       // Random buggy state that doesn't decode??
 115       return "#" + trapState;
 116     } else {
 117       return trapReasonName(reason) + (recompFlag ? " recompiled" : "");
 118     }
 119   }
 120 
 121 
 122 
 123   static {
 124     VM.registerVMInitializedObserver(new Observer() {
 125         public void update(Observable o, Object data) {
 126           initialize(VM.getVM().getTypeDataBase());
 127         }
 128       });
 129   }
 130 
 131   private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
 132     Type type      = db.lookupType("methodDataOopDesc");
 133     baseOffset     = type.getSize();
 134 
 135     size           = new CIntField(type.getCIntegerField("_size"), 0);
 136     method         = new OopField(type.getOopField("_method"), 0);
 137 
 138     VM.Flag[] flags = VM.getVM().getCommandLineFlags();
 139     for (int f = 0; f < flags.length; f++) {
 140       VM.Flag flag = flags[f];
 141       if (flag.getName().equals("TypeProfileWidth")) {
 142         TypeProfileWidth = (int)flag.getIntx();
 143       } else if (flag.getName().equals("BciProfileWidth")) {
 144         BciProfileWidth = (int)flag.getIntx();
 145       } else if (flag.getName().equals("CompileThreshold")) {
 146         CompileThreshold = (int)flag.getIntx();
 147       }
 148     }
 149 
 150     cellSize = (int)VM.getVM().getAddressSize();
 151 
 152     dataSize     = new CIntField(type.getCIntegerField("_data_size"), 0);
 153     data         = type.getAddressField("_data[0]");
 154 
 155     sizeofMethodDataOopDesc = (int)type.getSize();;
 156 
 157     Reason_many            = db.lookupIntConstant("Deoptimization::Reason_many").intValue();
 158     Reason_none            = db.lookupIntConstant("Deoptimization::Reason_none").intValue();
 159     Reason_LIMIT           = db.lookupIntConstant("Deoptimization::Reason_LIMIT").intValue();
 160     Reason_RECORDED_LIMIT  = db.lookupIntConstant("Deoptimization::Reason_RECORDED_LIMIT").intValue();
 161 
 162     trapReasonName = new String[Reason_LIMIT];
 163 
 164     // Find Deopt reasons
 165     Iterator i = db.getIntConstants();
 166     String prefix = "Deoptimization::Reason_";
 167     while (i.hasNext()) {
 168       String name = (String)i.next();
 169       if (name.startsWith(prefix)) {
 170         // Strip prefix
 171         if (!name.endsWith("Reason_many") &&
 172             !name.endsWith("Reason_LIMIT") &&
 173             !name.endsWith("Reason_RECORDED_LIMIT")) {
 174           String trimmed = name.substring(prefix.length());
 175           int value = db.lookupIntConstant(name).intValue();
 176           if (trapReasonName[value] != null) {
 177             throw new InternalError("duplicate reasons: " + trapReasonName[value] + " " + trimmed);
 178           }
 179           trapReasonName[value] = trimmed;
 180         }
 181       }
 182     }
 183     for (int index = 0; index < trapReasonName.length; index++) {
 184       if (trapReasonName[index] == null) {
 185         throw new InternalError("missing reason for " + index);
 186       }
 187       System.out.println(trapReasonName[index]);
 188     }
 189   }
 190 
 191   MethodData(OopHandle handle, ObjectHeap heap) {
 192     super(handle, heap);
 193   }
 194 
 195   public boolean isMethodData()        { return true; }
 196 
 197   private static long baseOffset;
 198   private static CIntField size;
 199   private static OopField  method;
 200   private static CIntField dataSize;
 201   private static AddressField data;
 202 
 203   public static int sizeofMethodDataOopDesc;
 204   public static int cellSize;
 205 
 206   public long getObjectSize() {
 207     return alignObjectSize(size.getValue(this));
 208   }
 209 
 210   public Method getMethod() {
 211     return (Method) method.getValue(this);
 212   }
 213 
 214   public void printValueOn(PrintStream tty) {
 215     Method m = getMethod();
 216     tty.print("MethodData for " + m.getName().asString() + m.getSignature().asString());
 217   }
 218 
 219   public void iterateFields(OopVisitor visitor, boolean doVMFields) {
 220     super.iterateFields(visitor, doVMFields);
 221     if (doVMFields) {
 222       visitor.doOop(method, true);
 223       visitor.doCInt(size, true);
 224     }
 225   }
 226 
 227   int dataSize() {
 228     if (dataSize == null) {
 229       return 0;
 230     } else {
 231       return (int)dataSize.getValue(this);
 232     }
 233   }
 234 
 235   boolean outOfBounds(int dataIndex) {
 236     return dataIndex >= dataSize();
 237   }
 238 
 239   ProfileData dataAt(int dataIndex) {
 240     if (outOfBounds(dataIndex)) {
 241       return null;
 242     }
 243     DataLayout dataLayout = new DataLayout(this, dataIndex + (int)data.getOffset());
 244 
 245     switch (dataLayout.tag()) {
 246     case DataLayout.noTag:
 247     default:
 248       throw new InternalError(dataIndex + " " + dataSize() + " " + dataLayout.tag());
 249     case DataLayout.bitDataTag:
 250       return new BitData(dataLayout);
 251     case DataLayout.counterDataTag:
 252       return new CounterData(dataLayout);
 253     case DataLayout.jumpDataTag:
 254       return new JumpData(dataLayout);
 255     case DataLayout.receiverTypeDataTag:
 256       return new ReceiverTypeData(dataLayout);
 257     case DataLayout.virtualCallDataTag:
 258       return new VirtualCallData(dataLayout);
 259     case DataLayout.retDataTag:
 260       return new RetData(dataLayout);
 261     case DataLayout.branchDataTag:
 262       return new BranchData(dataLayout);
 263     case DataLayout.multiBranchDataTag:
 264       return new MultiBranchData(dataLayout);
 265     }
 266   }
 267 
 268   int dpToDi(int dp) {
 269     // this in an offset from the base of the MDO, so convert to offset into _data
 270     return dp - (int)data.getOffset();
 271   }
 272 
 273   int firstDi() { return 0; }
 274   public ProfileData firstData() { return dataAt(firstDi()); }
 275   public ProfileData nextData(ProfileData current) {
 276     int currentIndex = dpToDi(current.dp());
 277     int nextIndex = currentIndex + current.sizeInBytes();
 278     return dataAt(nextIndex);
 279   }
 280   boolean isValid(ProfileData current) { return current != null; }
 281 
 282   public void printDataOn(PrintStream st) {
 283     ProfileData data = firstData();
 284     for ( ; isValid(data); data = nextData(data)) {
 285       st.print(dpToDi(data.dp()));
 286       st.print(" ");
 287       // st->fillTo(6);
 288       data.printDataOn(st);
 289     }
 290   }
 291 
 292   private byte[] fetchDataAt(Address base, long offset, long size) {
 293     byte[] result = new byte[(int)size];
 294     for (int i = 0; i < size; i++) {
 295       result[i] = base.getJByteAt(offset + i);
 296     }
 297     return result;
 298   }
 299 
 300   public byte[] orig() {
 301     // fetch the orig methodDataOopDesc data between header and dataSize
 302     return fetchDataAt(this.getHandle(), 0, sizeofMethodDataOopDesc);
 303   }
 304 
 305   public long[] data() {
 306     // Read the data as an array of intptr_t elements
 307     OopHandle base = getHandle();
 308     long offset = data.getOffset();
 309     int elements = dataSize() / cellSize;
 310     long[] result = new long[elements];
 311     for (int i = 0; i < elements; i++) {
 312       Address value = base.getAddressAt(offset + i * MethodData.cellSize);
 313       if (value != null) {
 314         result[i] = value.minus(null);
 315       }
 316     }
 317     return result;
 318   }
 319 
 320   // Get a measure of how much mileage the method has on it.
 321   int mileageOf(Method method) {
 322     long mileage = 0;
 323     int iic = method.interpreterInvocationCount();
 324     if (mileage < iic)  mileage = iic;
 325 
 326     long ic = method.getInvocationCounter();
 327     long bc = method.getBackedgeCounter();
 328 
 329     long icval = ic >> 3;
 330     if ((ic & 4) != 0) icval += CompileThreshold;
 331     if (mileage < icval)  mileage = icval;
 332     long bcval = bc >> 3;
 333     if ((bc & 4) != 0) bcval += CompileThreshold;
 334     if (mileage < bcval)  mileage = bcval;
 335     return (int)mileage;
 336   }
 337 
 338   public int currentMileage() {
 339     return 20000;
 340   }
 341 }