1 /*
   2  * Copyright (c) 2000, 2012, 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 Metadata {
  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("MethodData");
 133     baseOffset     = type.getSize();
 134 
 135     size           = new CIntField(type.getCIntegerField("_size"), 0);
 136     method         = new MetadataField(type.getAddressField("_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     }
 188   }
 189 
 190   public MethodData(Address addr) {
 191     super(addr);
 192   }
 193 
 194   public boolean isMethodData()        { return true; }
 195 
 196   private static long baseOffset;
 197   private static CIntField size;
 198   private static MetadataField  method;
 199   private static CIntField dataSize;
 200   private static AddressField data;
 201 
 202   public static int sizeofMethodDataOopDesc;
 203   public static int cellSize;
 204 
 205   public Method getMethod() {
 206     return (Method) method.getValue(this);
 207   }
 208 
 209   public void printValueOn(PrintStream tty) {
 210     Method m = getMethod();
 211     tty.print("MethodData for " + m.getName().asString() + m.getSignature().asString());
 212   }
 213 
 214   public void iterateFields(MetadataVisitor visitor) {
 215     super.iterateFields(visitor);
 216     visitor.doMetadata(method, true);
 217       visitor.doCInt(size, true);
 218     }
 219 
 220   int dataSize() {
 221     if (dataSize == null) {
 222       return 0;
 223     } else {
 224       return (int)dataSize.getValue(getAddress());
 225     }
 226   }
 227 
 228   boolean outOfBounds(int dataIndex) {
 229     return dataIndex >= dataSize();
 230   }
 231 
 232   ProfileData dataAt(int dataIndex) {
 233     if (outOfBounds(dataIndex)) {
 234       return null;
 235     }
 236     DataLayout dataLayout = new DataLayout(this, dataIndex + (int)data.getOffset());
 237 
 238     switch (dataLayout.tag()) {
 239     case DataLayout.noTag:
 240     default:
 241       throw new InternalError(dataIndex + " " + dataSize() + " " + dataLayout.tag());
 242     case DataLayout.bitDataTag:
 243       return new BitData(dataLayout);
 244     case DataLayout.counterDataTag:
 245       return new CounterData(dataLayout);
 246     case DataLayout.jumpDataTag:
 247       return new JumpData(dataLayout);
 248     case DataLayout.receiverTypeDataTag:
 249       return new ReceiverTypeData(dataLayout);
 250     case DataLayout.virtualCallDataTag:
 251       return new VirtualCallData(dataLayout);
 252     case DataLayout.retDataTag:
 253       return new RetData(dataLayout);
 254     case DataLayout.branchDataTag:
 255       return new BranchData(dataLayout);
 256     case DataLayout.multiBranchDataTag:
 257       return new MultiBranchData(dataLayout);
 258     }
 259   }
 260 
 261   int dpToDi(int dp) {
 262     // this in an offset from the base of the MDO, so convert to offset into _data
 263     return dp - (int)data.getOffset();
 264   }
 265 
 266   int firstDi() { return 0; }
 267   public ProfileData firstData() { return dataAt(firstDi()); }
 268   public ProfileData nextData(ProfileData current) {
 269     int currentIndex = dpToDi(current.dp());
 270     int nextIndex = currentIndex + current.sizeInBytes();
 271     return dataAt(nextIndex);
 272   }
 273   boolean isValid(ProfileData current) { return current != null; }
 274 
 275   public void printDataOn(PrintStream st) {
 276     ProfileData data = firstData();
 277     for ( ; isValid(data); data = nextData(data)) {
 278       st.print(dpToDi(data.dp()));
 279       st.print(" ");
 280       // st->fillTo(6);
 281       data.printDataOn(st);
 282     }
 283   }
 284 
 285   private byte[] fetchDataAt(Address base, long offset, long size) {
 286     byte[] result = new byte[(int)size];
 287     for (int i = 0; i < size; i++) {
 288       result[i] = base.getJByteAt(offset + i);
 289     }
 290     return result;
 291   }
 292 
 293   public byte[] orig() {
 294     // fetch the orig MethodData data between header and dataSize
 295     return fetchDataAt(getAddress(), 0, sizeofMethodDataOopDesc);
 296   }
 297 
 298   public long[] data() {
 299     // Read the data as an array of intptr_t elements
 300     Address base = getAddress();
 301     long offset = data.getOffset();
 302     int elements = dataSize() / cellSize;
 303     long[] result = new long[elements];
 304     for (int i = 0; i < elements; i++) {
 305       Address value = base.getAddressAt(offset + i * MethodData.cellSize);
 306       if (value != null) {
 307         result[i] = value.minus(null);
 308       }
 309     }
 310     return result;
 311   }
 312 
 313   // Get a measure of how much mileage the method has on it.
 314   int mileageOf(Method method) {
 315     long mileage = 0;
 316     int iic = method.interpreterInvocationCount();
 317     if (mileage < iic)  mileage = iic;
 318 
 319     long ic = method.getInvocationCounter();
 320     long bc = method.getBackedgeCounter();
 321 
 322     long icval = ic >> 3;
 323     if ((ic & 4) != 0) icval += CompileThreshold;
 324     if (mileage < icval)  mileage = icval;
 325     long bcval = bc >> 3;
 326     if ((bc & 4) != 0) bcval += CompileThreshold;
 327     if (mileage < bcval)  mileage = bcval;
 328     return (int)mileage;
 329   }
 330 
 331   public int currentMileage() {
 332     return 20000;
 333   }
 334 
 335   public void dumpReplayData(PrintStream out) {
 336     Method method = getMethod();
 337     Klass holder = method.getMethodHolder();
 338     out.print("ciMethodData " +
 339               holder.getName().asString() + " " +
 340               OopUtilities.escapeString(method.getName().asString()) + " " +
 341               method.getSignature().asString() + " " +
 342               "2" + " " +
 343               currentMileage());
 344     byte[] orig = orig();
 345     out.print(" orig " + orig.length);
 346     for (int i = 0; i < orig.length; i++) {
 347       out.print(" " + (orig[i] & 0xff));
 348     }
 349 
 350     long[] data = data();
 351     out.print(" data " +  data.length);
 352     for (int i = 0; i < data.length; i++) {
 353       out.print(" 0x" + Long.toHexString(data[i]));
 354     }
 355     int count = 0;
 356     for (int round = 0; round < 2; round++) {
 357       if (round == 1) out.print(" oops " + count);
 358       ProfileData pdata = firstData();
 359       for ( ; isValid(pdata); pdata = nextData(pdata)) {
 360         if (pdata instanceof ReceiverTypeData) {
 361           ReceiverTypeData vdata = (ReceiverTypeData)pdata;
 362           for (int i = 0; i < vdata.rowLimit(); i++) {
 363             Klass k = vdata.receiver(i);
 364             if (k != null) {
 365               if (round == 0) count++;
 366               else out.print(" " +
 367                              (dpToDi(vdata.dp() +
 368                               vdata.cellOffset(vdata.receiverCellIndex(i))) / cellSize) + " " +
 369                              k.getName().asString());
 370             }
 371           }
 372         } else if (pdata instanceof VirtualCallData) {
 373           VirtualCallData vdata = (VirtualCallData)pdata;
 374           for (int i = 0; i < vdata.rowLimit(); i++) {
 375             Klass k = vdata.receiver(i);
 376             if (k != null) {
 377               if (round == 0) count++;
 378               else out.print(" " +
 379                              (dpToDi(vdata.dp() +
 380                               vdata.cellOffset(vdata.receiverCellIndex(i))) / cellSize) + " " +
 381                              k.getName().asString());
 382             }
 383           }
 384         }
 385       }
 386     }
 387     out.println();
 388   }
 389 }