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