1 /*
   2  * Copyright (c) 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 package jdk.vm.ci.hotspot;
  24 
  25 import static java.lang.String.*;
  26 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.*;
  27 import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
  28 
  29 import java.util.*;
  30 
  31 import jdk.vm.ci.hotspot.HotSpotMethodDataAccessor.*;
  32 import jdk.vm.ci.meta.*;
  33 import jdk.vm.ci.meta.JavaMethodProfile.*;
  34 import jdk.vm.ci.meta.JavaTypeProfile.*;
  35 import sun.misc.*;
  36 
  37 /**
  38  * Access to a HotSpot MethodData structure (defined in methodData.hpp).
  39  */
  40 public final class HotSpotMethodData {
  41 
  42     private static final HotSpotVMConfig config = runtime().getConfig();
  43     private static final HotSpotMethodDataAccessor NO_DATA_NO_EXCEPTION_ACCESSOR = new NoMethodData(TriState.FALSE);
  44     private static final HotSpotMethodDataAccessor NO_DATA_EXCEPTION_POSSIBLY_NOT_RECORDED_ACCESSOR = new NoMethodData(TriState.UNKNOWN);
  45 
  46     // sorted by tag
  47     // @formatter:off
  48     private static final HotSpotMethodDataAccessor[] PROFILE_DATA_ACCESSORS = {
  49         null,
  50         new BitData(),
  51         new CounterData(),
  52         new JumpData(),
  53         new TypeCheckData(),
  54         new VirtualCallData(),
  55         new RetData(),
  56         new BranchData(),
  57         new MultiBranchData(),
  58         new ArgInfoData(),
  59         null, // call_type_data_tag
  60         null, // virtual_call_type_data_tag
  61         null, // parameters_type_data_tag
  62         null, // speculative_trap_data_tag
  63     };
  64     // @formatter:on
  65 
  66     /**
  67      * Reference to the C++ MethodData object.
  68      */
  69     private final long metaspaceMethodData;
  70     @SuppressWarnings("unused") private final HotSpotResolvedJavaMethodImpl method;
  71 
  72     public HotSpotMethodData(long metaspaceMethodData, HotSpotResolvedJavaMethodImpl method) {
  73         this.metaspaceMethodData = metaspaceMethodData;
  74         this.method = method;
  75     }
  76 
  77     /**
  78      * @return value of the MethodData::_data_size field
  79      */
  80     private int normalDataSize() {
  81         return UNSAFE.getInt(metaspaceMethodData + config.methodDataDataSize);
  82     }
  83 
  84     /**
  85      * Returns the size of the extra data records. This method does the same calculation as
  86      * MethodData::extra_data_size().
  87      *
  88      * @return size of extra data records
  89      */
  90     private int extraDataSize() {
  91         final int extraDataBase = config.methodDataOopDataOffset + normalDataSize();
  92         final int extraDataLimit = UNSAFE.getInt(metaspaceMethodData + config.methodDataSize);
  93         return extraDataLimit - extraDataBase;
  94     }
  95 
  96     public boolean hasNormalData() {
  97         return normalDataSize() > 0;
  98     }
  99 
 100     public boolean hasExtraData() {
 101         return extraDataSize() > 0;
 102     }
 103 
 104     public int getExtraDataBeginOffset() {
 105         return normalDataSize();
 106     }
 107 
 108     public boolean isWithin(int position) {
 109         return position >= 0 && position < normalDataSize() + extraDataSize();
 110     }
 111 
 112     public int getDeoptimizationCount(DeoptimizationReason reason) {
 113         HotSpotMetaAccessProvider metaAccess = (HotSpotMetaAccessProvider) runtime().getHostJVMCIBackend().getMetaAccess();
 114         int reasonIndex = metaAccess.convertDeoptReason(reason);
 115         return UNSAFE.getByte(metaspaceMethodData + config.methodDataOopTrapHistoryOffset + reasonIndex) & 0xFF;
 116     }
 117 
 118     public int getOSRDeoptimizationCount(DeoptimizationReason reason) {
 119         HotSpotMetaAccessProvider metaAccess = (HotSpotMetaAccessProvider) runtime().getHostJVMCIBackend().getMetaAccess();
 120         int reasonIndex = metaAccess.convertDeoptReason(reason);
 121         return UNSAFE.getByte(metaspaceMethodData + config.methodDataOopTrapHistoryOffset + config.deoptReasonOSROffset + reasonIndex) & 0xFF;
 122     }
 123 
 124     public HotSpotMethodDataAccessor getNormalData(int position) {
 125         if (position >= normalDataSize()) {
 126             return null;
 127         }
 128 
 129         HotSpotMethodDataAccessor result = getData(position);
 130         assert result != null : "NO_DATA tag is not allowed";
 131         return result;
 132     }
 133 
 134     public HotSpotMethodDataAccessor getExtraData(int position) {
 135         if (position >= normalDataSize() + extraDataSize()) {
 136             return null;
 137         }
 138         HotSpotMethodDataAccessor data = getData(position);
 139         if (data != null) {
 140             return data;
 141         }
 142         return data;
 143     }
 144 
 145     public static HotSpotMethodDataAccessor getNoDataAccessor(boolean exceptionPossiblyNotRecorded) {
 146         if (exceptionPossiblyNotRecorded) {
 147             return NO_DATA_EXCEPTION_POSSIBLY_NOT_RECORDED_ACCESSOR;
 148         } else {
 149             return NO_DATA_NO_EXCEPTION_ACCESSOR;
 150         }
 151     }
 152 
 153     private HotSpotMethodDataAccessor getData(int position) {
 154         assert position >= 0 : "out of bounds";
 155         final Tag tag = AbstractMethodData.readTag(this, position);
 156         HotSpotMethodDataAccessor accessor = PROFILE_DATA_ACCESSORS[tag.getValue()];
 157         assert accessor == null || accessor.getTag() == tag : "wrong data accessor " + accessor + " for tag " + tag;
 158         return accessor;
 159     }
 160 
 161     private int readUnsignedByte(int position, int offsetInBytes) {
 162         long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
 163         return UNSAFE.getByte(metaspaceMethodData + fullOffsetInBytes) & 0xFF;
 164     }
 165 
 166     private int readUnsignedShort(int position, int offsetInBytes) {
 167         long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
 168         return UNSAFE.getShort(metaspaceMethodData + fullOffsetInBytes) & 0xFFFF;
 169     }
 170 
 171     /**
 172      * Since the values are stored in cells (platform words) this method uses
 173      * {@link Unsafe#getAddress} to read the right value on both little and big endian machines.
 174      */
 175     private long readUnsignedInt(int position, int offsetInBytes) {
 176         long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
 177         return UNSAFE.getAddress(metaspaceMethodData + fullOffsetInBytes) & 0xFFFFFFFFL;
 178     }
 179 
 180     private int readUnsignedIntAsSignedInt(int position, int offsetInBytes) {
 181         long value = readUnsignedInt(position, offsetInBytes);
 182         return truncateLongToInt(value);
 183     }
 184 
 185     /**
 186      * Since the values are stored in cells (platform words) this method uses
 187      * {@link Unsafe#getAddress} to read the right value on both little and big endian machines.
 188      */
 189     private int readInt(int position, int offsetInBytes) {
 190         long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
 191         return (int) UNSAFE.getAddress(metaspaceMethodData + fullOffsetInBytes);
 192     }
 193 
 194     private HotSpotResolvedJavaMethod readMethod(int position, int offsetInBytes) {
 195         long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
 196         return runtime().compilerToVm.getResolvedJavaMethod(null, metaspaceMethodData + fullOffsetInBytes);
 197     }
 198 
 199     private HotSpotResolvedObjectTypeImpl readKlass(int position, int offsetInBytes) {
 200         long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
 201         return runtime().compilerToVm.getResolvedJavaType(null, metaspaceMethodData + fullOffsetInBytes, false);
 202     }
 203 
 204     private static int truncateLongToInt(long value) {
 205         return value > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) value;
 206     }
 207 
 208     private static int computeFullOffset(int position, int offsetInBytes) {
 209         return config.methodDataOopDataOffset + position + offsetInBytes;
 210     }
 211 
 212     private static int cellIndexToOffset(int cells) {
 213         return config.dataLayoutHeaderSize + cellsToBytes(cells);
 214     }
 215 
 216     private static int cellsToBytes(int cells) {
 217         return cells * config.dataLayoutCellSize;
 218     }
 219 
 220     /**
 221      * Returns whether profiling ran long enough that the profile information is mature. Other
 222      * informational data will still be valid even if the profile isn't mature.
 223      */
 224     public boolean isProfileMature() {
 225         return runtime().getCompilerToVM().isMature(metaspaceMethodData);
 226     }
 227 
 228     @Override
 229     public String toString() {
 230         StringBuilder sb = new StringBuilder();
 231         String nl = String.format("%n");
 232         String nlIndent = String.format("%n%38s", "");
 233         if (hasNormalData()) {
 234             int pos = 0;
 235             HotSpotMethodDataAccessor data;
 236             while ((data = getNormalData(pos)) != null) {
 237                 if (pos != 0) {
 238                     sb.append(nl);
 239                 }
 240                 int bci = data.getBCI(this, pos);
 241                 sb.append(String.format("%-6d bci: %-6d%-20s", pos, bci, data.getClass().getSimpleName()));
 242                 sb.append(data.appendTo(new StringBuilder(), this, pos).toString().replace(nl, nlIndent));
 243                 pos = pos + data.getSize(this, pos);
 244             }
 245         }
 246 
 247         if (hasExtraData()) {
 248             int pos = getExtraDataBeginOffset();
 249             HotSpotMethodDataAccessor data;
 250             while ((data = getExtraData(pos)) != null) {
 251                 if (pos == getExtraDataBeginOffset()) {
 252                     sb.append(nl).append("--- Extra data:");
 253                 }
 254                 int bci = data.getBCI(this, pos);
 255                 sb.append(String.format("%n%-6d bci: %-6d%-20s", pos, bci, data.getClass().getSimpleName()));
 256                 sb.append(data.appendTo(new StringBuilder(), this, pos).toString().replace(nl, nlIndent));
 257                 pos = pos + data.getSize(this, pos);
 258             }
 259 
 260         }
 261         return sb.toString();
 262     }
 263 
 264     private abstract static class AbstractMethodData implements HotSpotMethodDataAccessor {
 265 
 266         /**
 267          * Corresponds to {@code exception_seen_flag}.
 268          */
 269         private static final int EXCEPTIONS_MASK = 0x2;
 270 
 271         private final Tag tag;
 272         private final int staticSize;
 273 
 274         protected AbstractMethodData(Tag tag, int staticSize) {
 275             this.tag = tag;
 276             this.staticSize = staticSize;
 277         }
 278 
 279         public Tag getTag() {
 280             return tag;
 281         }
 282 
 283         public static Tag readTag(HotSpotMethodData data, int position) {
 284             final int tag = data.readUnsignedByte(position, config.dataLayoutTagOffset);
 285             return Tag.getEnum(tag);
 286         }
 287 
 288         @Override
 289         public int getBCI(HotSpotMethodData data, int position) {
 290             return data.readUnsignedShort(position, config.dataLayoutBCIOffset);
 291         }
 292 
 293         @Override
 294         public int getSize(HotSpotMethodData data, int position) {
 295             return staticSize + getDynamicSize(data, position);
 296         }
 297 
 298         @Override
 299         public TriState getExceptionSeen(HotSpotMethodData data, int position) {
 300             return TriState.get((getFlags(data, position) & EXCEPTIONS_MASK) != 0);
 301         }
 302 
 303         @Override
 304         public JavaTypeProfile getTypeProfile(HotSpotMethodData data, int position) {
 305             return null;
 306         }
 307 
 308         @Override
 309         public JavaMethodProfile getMethodProfile(HotSpotMethodData data, int position) {
 310             return null;
 311         }
 312 
 313         @Override
 314         public double getBranchTakenProbability(HotSpotMethodData data, int position) {
 315             return -1;
 316         }
 317 
 318         @Override
 319         public double[] getSwitchProbabilities(HotSpotMethodData data, int position) {
 320             return null;
 321         }
 322 
 323         @Override
 324         public int getExecutionCount(HotSpotMethodData data, int position) {
 325             return -1;
 326         }
 327 
 328         @Override
 329         public TriState getNullSeen(HotSpotMethodData data, int position) {
 330             return TriState.UNKNOWN;
 331         }
 332 
 333         protected int getFlags(HotSpotMethodData data, int position) {
 334             return data.readUnsignedByte(position, config.dataLayoutFlagsOffset);
 335         }
 336 
 337         /**
 338          * @param data
 339          * @param position
 340          */
 341         protected int getDynamicSize(HotSpotMethodData data, int position) {
 342             return 0;
 343         }
 344 
 345         public abstract StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos);
 346     }
 347 
 348     private static class NoMethodData extends AbstractMethodData {
 349 
 350         private static final int NO_DATA_SIZE = cellIndexToOffset(0);
 351 
 352         private final TriState exceptionSeen;
 353 
 354         protected NoMethodData(TriState exceptionSeen) {
 355             super(Tag.No, NO_DATA_SIZE);
 356             this.exceptionSeen = exceptionSeen;
 357         }
 358 
 359         @Override
 360         public int getBCI(HotSpotMethodData data, int position) {
 361             return -1;
 362         }
 363 
 364         @Override
 365         public TriState getExceptionSeen(HotSpotMethodData data, int position) {
 366             return exceptionSeen;
 367         }
 368 
 369         @Override
 370         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
 371             return sb;
 372         }
 373     }
 374 
 375     private static class BitData extends AbstractMethodData {
 376 
 377         private static final int BIT_DATA_SIZE = cellIndexToOffset(0);
 378         private static final int BIT_DATA_NULL_SEEN_FLAG = 0x01;
 379 
 380         private BitData() {
 381             super(Tag.BitData, BIT_DATA_SIZE);
 382         }
 383 
 384         protected BitData(Tag tag, int staticSize) {
 385             super(tag, staticSize);
 386         }
 387 
 388         @Override
 389         public TriState getNullSeen(HotSpotMethodData data, int position) {
 390             return TriState.get((getFlags(data, position) & BIT_DATA_NULL_SEEN_FLAG) != 0);
 391         }
 392 
 393         @Override
 394         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
 395             return sb.append(format("exception_seen(%s)", getExceptionSeen(data, pos)));
 396         }
 397     }
 398 
 399     private static class CounterData extends BitData {
 400 
 401         private static final int COUNTER_DATA_SIZE = cellIndexToOffset(1);
 402         private static final int COUNTER_DATA_COUNT_OFFSET = cellIndexToOffset(0);
 403 
 404         public CounterData() {
 405             super(Tag.CounterData, COUNTER_DATA_SIZE);
 406         }
 407 
 408         protected CounterData(Tag tag, int staticSize) {
 409             super(tag, staticSize);
 410         }
 411 
 412         @Override
 413         public int getExecutionCount(HotSpotMethodData data, int position) {
 414             return getCounterValue(data, position);
 415         }
 416 
 417         protected int getCounterValue(HotSpotMethodData data, int position) {
 418             return data.readUnsignedIntAsSignedInt(position, COUNTER_DATA_COUNT_OFFSET);
 419         }
 420 
 421         @Override
 422         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
 423             return sb.append(format("count(%d) null_seen(%s) exception_seen(%s)", getCounterValue(data, pos), getNullSeen(data, pos), getExceptionSeen(data, pos)));
 424         }
 425     }
 426 
 427     private static class JumpData extends AbstractMethodData {
 428 
 429         private static final int JUMP_DATA_SIZE = cellIndexToOffset(2);
 430         protected static final int TAKEN_COUNT_OFFSET = cellIndexToOffset(0);
 431         protected static final int TAKEN_DISPLACEMENT_OFFSET = cellIndexToOffset(1);
 432 
 433         public JumpData() {
 434             super(Tag.JumpData, JUMP_DATA_SIZE);
 435         }
 436 
 437         protected JumpData(Tag tag, int staticSize) {
 438             super(tag, staticSize);
 439         }
 440 
 441         @Override
 442         public double getBranchTakenProbability(HotSpotMethodData data, int position) {
 443             return getExecutionCount(data, position) != 0 ? 1 : 0;
 444         }
 445 
 446         @Override
 447         public int getExecutionCount(HotSpotMethodData data, int position) {
 448             return data.readUnsignedIntAsSignedInt(position, TAKEN_COUNT_OFFSET);
 449         }
 450 
 451         public int getTakenDisplacement(HotSpotMethodData data, int position) {
 452             return data.readInt(position, TAKEN_DISPLACEMENT_OFFSET);
 453         }
 454 
 455         @Override
 456         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
 457             return sb.append(format("taken(%d) displacement(%d)", getExecutionCount(data, pos), getTakenDisplacement(data, pos)));
 458         }
 459     }
 460 
 461     static class RawItemProfile<T> {
 462         final int entries;
 463         final T[] items;
 464         final long[] counts;
 465         final long totalCount;
 466 
 467         public RawItemProfile(int entries, T[] items, long[] counts, long totalCount) {
 468             this.entries = entries;
 469             this.items = items;
 470             this.counts = counts;
 471             this.totalCount = totalCount;
 472         }
 473     }
 474 
 475     private abstract static class AbstractTypeData extends CounterData {
 476 
 477         protected static final int TYPE_DATA_ROW_SIZE = cellsToBytes(2);
 478 
 479         protected static final int NONPROFILED_COUNT_OFFSET = cellIndexToOffset(1);
 480         protected static final int TYPE_DATA_FIRST_TYPE_OFFSET = cellIndexToOffset(2);
 481         protected static final int TYPE_DATA_FIRST_TYPE_COUNT_OFFSET = cellIndexToOffset(3);
 482 
 483         protected AbstractTypeData(Tag tag, int staticSize) {
 484             super(tag, staticSize);
 485         }
 486 
 487         @Override
 488         public JavaTypeProfile getTypeProfile(HotSpotMethodData data, int position) {
 489             return createTypeProfile(getNullSeen(data, position), getRawTypeProfile(data, position));
 490         }
 491 
 492         private RawItemProfile<ResolvedJavaType> getRawTypeProfile(HotSpotMethodData data, int position) {
 493             int typeProfileWidth = config.typeProfileWidth;
 494 
 495             ResolvedJavaType[] types = new ResolvedJavaType[typeProfileWidth];
 496             long[] counts = new long[typeProfileWidth];
 497             long totalCount = 0;
 498             int entries = 0;
 499 
 500             outer: for (int i = 0; i < typeProfileWidth; i++) {
 501                 HotSpotResolvedObjectTypeImpl receiverKlass = data.readKlass(position, getTypeOffset(i));
 502                 if (receiverKlass != null) {
 503                     HotSpotResolvedObjectTypeImpl klass = receiverKlass;
 504                     long count = data.readUnsignedInt(position, getTypeCountOffset(i));
 505                     /*
 506                      * Because of races in the profile collection machinery it's possible for a
 507                      * class to appear multiple times so merge them to make the profile look
 508                      * rational.
 509                      */
 510                     for (int j = 0; j < entries; j++) {
 511                         if (types[j].equals(klass)) {
 512                             totalCount += count;
 513                             counts[j] += count;
 514                             continue outer;
 515                         }
 516                     }
 517                     types[entries] = klass;
 518                     totalCount += count;
 519                     counts[entries] = count;
 520                     entries++;
 521                 }
 522             }
 523 
 524             totalCount += getTypesNotRecordedExecutionCount(data, position);
 525             return new RawItemProfile<>(entries, types, counts, totalCount);
 526         }
 527 
 528         protected abstract long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position);
 529 
 530         private static JavaTypeProfile createTypeProfile(TriState nullSeen, RawItemProfile<ResolvedJavaType> profile) {
 531             if (profile.entries <= 0 || profile.totalCount <= 0) {
 532                 return null;
 533             }
 534 
 535             ProfiledType[] ptypes = new ProfiledType[profile.entries];
 536             double totalProbability = 0.0;
 537             for (int i = 0; i < profile.entries; i++) {
 538                 double p = profile.counts[i];
 539                 p = p / profile.totalCount;
 540                 totalProbability += p;
 541                 ptypes[i] = new ProfiledType(profile.items[i], p);
 542             }
 543 
 544             Arrays.sort(ptypes);
 545 
 546             double notRecordedTypeProbability = profile.entries < config.typeProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
 547             assert notRecordedTypeProbability == 0 || profile.entries == config.typeProfileWidth;
 548             return new JavaTypeProfile(nullSeen, notRecordedTypeProbability, ptypes);
 549         }
 550 
 551         private static int getTypeOffset(int row) {
 552             return TYPE_DATA_FIRST_TYPE_OFFSET + row * TYPE_DATA_ROW_SIZE;
 553         }
 554 
 555         protected static int getTypeCountOffset(int row) {
 556             return TYPE_DATA_FIRST_TYPE_COUNT_OFFSET + row * TYPE_DATA_ROW_SIZE;
 557         }
 558 
 559         @Override
 560         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
 561             RawItemProfile<ResolvedJavaType> profile = getRawTypeProfile(data, pos);
 562             TriState nullSeen = getNullSeen(data, pos);
 563             TriState exceptionSeen = getExceptionSeen(data, pos);
 564             sb.append(format("count(%d) null_seen(%s) exception_seen(%s) nonprofiled_count(%d) entries(%d)", getCounterValue(data, pos), nullSeen, exceptionSeen,
 565                             getTypesNotRecordedExecutionCount(data, pos), profile.entries));
 566             for (int i = 0; i < profile.entries; i++) {
 567                 long count = profile.counts[i];
 568                 sb.append(format("%n  %s (%d, %4.2f)", profile.items[i].toJavaName(), count, (double) count / profile.totalCount));
 569             }
 570             return sb;
 571         }
 572     }
 573 
 574     private static class TypeCheckData extends AbstractTypeData {
 575 
 576         private static final int TYPE_CHECK_DATA_SIZE = cellIndexToOffset(2) + TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
 577 
 578         public TypeCheckData() {
 579             super(Tag.ReceiverTypeData, TYPE_CHECK_DATA_SIZE);
 580         }
 581 
 582         @Override
 583         public int getExecutionCount(HotSpotMethodData data, int position) {
 584             return -1;
 585         }
 586 
 587         @Override
 588         protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) {
 589             return data.readUnsignedIntAsSignedInt(position, NONPROFILED_COUNT_OFFSET);
 590         }
 591     }
 592 
 593     private static class VirtualCallData extends AbstractTypeData {
 594 
 595         private static final int VIRTUAL_CALL_DATA_SIZE = cellIndexToOffset(2) + TYPE_DATA_ROW_SIZE * (config.typeProfileWidth + config.methodProfileWidth);
 596         private static final int VIRTUAL_CALL_DATA_FIRST_METHOD_OFFSET = TYPE_DATA_FIRST_TYPE_OFFSET + TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
 597         private static final int VIRTUAL_CALL_DATA_FIRST_METHOD_COUNT_OFFSET = TYPE_DATA_FIRST_TYPE_COUNT_OFFSET + TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
 598 
 599         public VirtualCallData() {
 600             super(Tag.VirtualCallData, VIRTUAL_CALL_DATA_SIZE);
 601         }
 602 
 603         @Override
 604         public int getExecutionCount(HotSpotMethodData data, int position) {
 605             final int typeProfileWidth = config.typeProfileWidth;
 606 
 607             long total = 0;
 608             for (int i = 0; i < typeProfileWidth; i++) {
 609                 total += data.readUnsignedInt(position, getTypeCountOffset(i));
 610             }
 611 
 612             total += getCounterValue(data, position);
 613             return truncateLongToInt(total);
 614         }
 615 
 616         @Override
 617         protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) {
 618             return getCounterValue(data, position);
 619         }
 620 
 621         private static long getMethodsNotRecordedExecutionCount(HotSpotMethodData data, int position) {
 622             return data.readUnsignedIntAsSignedInt(position, NONPROFILED_COUNT_OFFSET);
 623         }
 624 
 625         @Override
 626         public JavaMethodProfile getMethodProfile(HotSpotMethodData data, int position) {
 627             return createMethodProfile(getRawMethodProfile(data, position));
 628         }
 629 
 630         private static RawItemProfile<ResolvedJavaMethod> getRawMethodProfile(HotSpotMethodData data, int position) {
 631             int profileWidth = config.methodProfileWidth;
 632 
 633             ResolvedJavaMethod[] methods = new ResolvedJavaMethod[profileWidth];
 634             long[] counts = new long[profileWidth];
 635             long totalCount = 0;
 636             int entries = 0;
 637 
 638             for (int i = 0; i < profileWidth; i++) {
 639                 HotSpotResolvedJavaMethod method = data.readMethod(position, getMethodOffset(i));
 640                 if (method != null) {
 641                     methods[entries] = method;
 642                     long count = data.readUnsignedInt(position, getMethodCountOffset(i));
 643                     totalCount += count;
 644                     counts[entries] = count;
 645 
 646                     entries++;
 647                 }
 648             }
 649 
 650             totalCount += getMethodsNotRecordedExecutionCount(data, position);
 651             return new RawItemProfile<>(entries, methods, counts, totalCount);
 652         }
 653 
 654         private static JavaMethodProfile createMethodProfile(RawItemProfile<ResolvedJavaMethod> profile) {
 655             if (profile.entries <= 0 || profile.totalCount <= 0) {
 656                 return null;
 657             }
 658 
 659             ProfiledMethod[] pmethods = new ProfiledMethod[profile.entries];
 660             double totalProbability = 0.0;
 661             for (int i = 0; i < profile.entries; i++) {
 662                 double p = profile.counts[i];
 663                 p = p / profile.totalCount;
 664                 totalProbability += p;
 665                 pmethods[i] = new ProfiledMethod(profile.items[i], p);
 666             }
 667 
 668             Arrays.sort(pmethods);
 669 
 670             double notRecordedMethodProbability = profile.entries < config.methodProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
 671             assert notRecordedMethodProbability == 0 || profile.entries == config.methodProfileWidth;
 672             return new JavaMethodProfile(notRecordedMethodProbability, pmethods);
 673         }
 674 
 675         private static int getMethodOffset(int row) {
 676             return VIRTUAL_CALL_DATA_FIRST_METHOD_OFFSET + row * TYPE_DATA_ROW_SIZE;
 677         }
 678 
 679         private static int getMethodCountOffset(int row) {
 680             return VIRTUAL_CALL_DATA_FIRST_METHOD_COUNT_OFFSET + row * TYPE_DATA_ROW_SIZE;
 681         }
 682 
 683         @Override
 684         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
 685             RawItemProfile<ResolvedJavaMethod> profile = getRawMethodProfile(data, pos);
 686             super.appendTo(sb.append(format("exception_seen(%s) ", getExceptionSeen(data, pos))), data, pos).append(format("%nmethod_entries(%d)", profile.entries));
 687             for (int i = 0; i < profile.entries; i++) {
 688                 long count = profile.counts[i];
 689                 sb.append(format("%n  %s (%d, %4.2f)", profile.items[i].format("%H.%n(%p)"), count, (double) count / profile.totalCount));
 690             }
 691             return sb;
 692         }
 693     }
 694 
 695     private static class RetData extends CounterData {
 696 
 697         private static final int RET_DATA_ROW_SIZE = cellsToBytes(3);
 698         private static final int RET_DATA_SIZE = cellIndexToOffset(1) + RET_DATA_ROW_SIZE * config.bciProfileWidth;
 699 
 700         public RetData() {
 701             super(Tag.RetData, RET_DATA_SIZE);
 702         }
 703     }
 704 
 705     private static class BranchData extends JumpData {
 706 
 707         private static final int BRANCH_DATA_SIZE = cellIndexToOffset(3);
 708         private static final int NOT_TAKEN_COUNT_OFFSET = cellIndexToOffset(2);
 709 
 710         public BranchData() {
 711             super(Tag.BranchData, BRANCH_DATA_SIZE);
 712         }
 713 
 714         @Override
 715         public double getBranchTakenProbability(HotSpotMethodData data, int position) {
 716             long takenCount = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET);
 717             long notTakenCount = data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET);
 718             long total = takenCount + notTakenCount;
 719 
 720             return total <= 0 ? -1 : takenCount / (double) total;
 721         }
 722 
 723         @Override
 724         public int getExecutionCount(HotSpotMethodData data, int position) {
 725             long count = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET) + data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET);
 726             return truncateLongToInt(count);
 727         }
 728 
 729         @Override
 730         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
 731             long taken = data.readUnsignedInt(pos, TAKEN_COUNT_OFFSET);
 732             long notTaken = data.readUnsignedInt(pos, NOT_TAKEN_COUNT_OFFSET);
 733             double takenProbability = getBranchTakenProbability(data, pos);
 734             return sb.append(format("taken(%d, %4.2f) not_taken(%d, %4.2f) displacement(%d)", taken, takenProbability, notTaken, 1.0D - takenProbability, getTakenDisplacement(data, pos)));
 735         }
 736     }
 737 
 738     private static class ArrayData extends AbstractMethodData {
 739 
 740         private static final int ARRAY_DATA_LENGTH_OFFSET = cellIndexToOffset(0);
 741         protected static final int ARRAY_DATA_START_OFFSET = cellIndexToOffset(1);
 742 
 743         public ArrayData(Tag tag, int staticSize) {
 744             super(tag, staticSize);
 745         }
 746 
 747         @Override
 748         protected int getDynamicSize(HotSpotMethodData data, int position) {
 749             return cellsToBytes(getLength(data, position));
 750         }
 751 
 752         protected static int getLength(HotSpotMethodData data, int position) {
 753             return data.readInt(position, ARRAY_DATA_LENGTH_OFFSET);
 754         }
 755 
 756         @Override
 757         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
 758             return sb.append(format("length(%d)", getLength(data, pos)));
 759         }
 760     }
 761 
 762     private static class MultiBranchData extends ArrayData {
 763 
 764         private static final int MULTI_BRANCH_DATA_SIZE = cellIndexToOffset(1);
 765         private static final int MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS = 2;
 766         private static final int MULTI_BRANCH_DATA_ROW_SIZE = cellsToBytes(MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS);
 767         private static final int MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(0);
 768         private static final int MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(1);
 769 
 770         public MultiBranchData() {
 771             super(Tag.MultiBranchData, MULTI_BRANCH_DATA_SIZE);
 772         }
 773 
 774         @Override
 775         public double[] getSwitchProbabilities(HotSpotMethodData data, int position) {
 776             int arrayLength = getLength(data, position);
 777             assert arrayLength > 0 : "switch must have at least the default case";
 778             assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows";
 779 
 780             int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS;
 781             long totalCount = 0;
 782             double[] result = new double[length];
 783 
 784             // default case is first in HotSpot but last for the compiler
 785             long count = readCount(data, position, 0);
 786             totalCount += count;
 787             result[length - 1] = count;
 788 
 789             for (int i = 1; i < length; i++) {
 790                 count = readCount(data, position, i);
 791                 totalCount += count;
 792                 result[i - 1] = count;
 793             }
 794 
 795             if (totalCount <= 0) {
 796                 return null;
 797             } else {
 798                 for (int i = 0; i < length; i++) {
 799                     result[i] = result[i] / totalCount;
 800                 }
 801                 return result;
 802             }
 803         }
 804 
 805         private static long readCount(HotSpotMethodData data, int position, int i) {
 806             int offset;
 807             long count;
 808             offset = getCountOffset(i);
 809             count = data.readUnsignedInt(position, offset);
 810             return count;
 811         }
 812 
 813         @Override
 814         public int getExecutionCount(HotSpotMethodData data, int position) {
 815             int arrayLength = getLength(data, position);
 816             assert arrayLength > 0 : "switch must have at least the default case";
 817             assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows";
 818 
 819             int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS;
 820             long totalCount = 0;
 821             for (int i = 0; i < length; i++) {
 822                 int offset = getCountOffset(i);
 823                 totalCount += data.readUnsignedInt(position, offset);
 824             }
 825 
 826             return truncateLongToInt(totalCount);
 827         }
 828 
 829         private static int getCountOffset(int index) {
 830             return MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE;
 831         }
 832 
 833         private static int getDisplacementOffset(int index) {
 834             return MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE;
 835         }
 836 
 837         @Override
 838         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
 839             int entries = getLength(data, pos) / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS;
 840             sb.append(format("entries(%d)", entries));
 841             for (int i = 0; i < entries; i++) {
 842                 sb.append(format("%n  %d: count(%d) displacement(%d)", i, data.readUnsignedInt(pos, getCountOffset(i)), data.readUnsignedInt(pos, getDisplacementOffset(i))));
 843             }
 844             return sb;
 845         }
 846     }
 847 
 848     private static class ArgInfoData extends ArrayData {
 849 
 850         private static final int ARG_INFO_DATA_SIZE = cellIndexToOffset(1);
 851 
 852         public ArgInfoData() {
 853             super(Tag.ArgInfoData, ARG_INFO_DATA_SIZE);
 854         }
 855     }
 856 
 857     public void setCompiledIRSize(int size) {
 858         UNSAFE.putInt(metaspaceMethodData + config.methodDataIRSizeOffset, size);
 859     }
 860 
 861     public int getCompiledIRSize() {
 862         return UNSAFE.getInt(metaspaceMethodData + config.methodDataIRSizeOffset);
 863     }
 864 }