| /* |
| * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package jdk.vm.ci.hotspot; |
| |
| import static java.lang.String.format; |
| import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM; |
| import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime; |
| import static jdk.vm.ci.hotspot.HotSpotVMConfig.config; |
| import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE; |
| |
| import java.util.Arrays; |
| |
| import jdk.internal.misc.Unsafe; |
| import jdk.vm.ci.meta.DeoptimizationReason; |
| import jdk.vm.ci.meta.JavaMethodProfile; |
| import jdk.vm.ci.meta.JavaMethodProfile.ProfiledMethod; |
| import jdk.vm.ci.meta.JavaTypeProfile; |
| import jdk.vm.ci.meta.JavaTypeProfile.ProfiledType; |
| import jdk.vm.ci.meta.ResolvedJavaMethod; |
| import jdk.vm.ci.meta.ResolvedJavaType; |
| import jdk.vm.ci.meta.TriState; |
| |
| /** |
| * Access to a HotSpot {@code MethodData} structure (defined in methodData.hpp). |
| */ |
| final class HotSpotMethodData { |
| |
| static final HotSpotVMConfig config = config(); |
| static final HotSpotMethodDataAccessor NO_DATA_NO_EXCEPTION_ACCESSOR = new NoMethodData(config, config.dataLayoutNoTag, TriState.FALSE); |
| static final HotSpotMethodDataAccessor NO_DATA_EXCEPTION_POSSIBLY_NOT_RECORDED_ACCESSOR = new NoMethodData(config, config.dataLayoutNoTag, TriState.UNKNOWN); |
| |
| /** |
| * Reference to the C++ MethodData object. |
| */ |
| final long metaspaceMethodData; |
| private final HotSpotResolvedJavaMethodImpl method; |
| |
| HotSpotMethodData(long metaspaceMethodData, HotSpotResolvedJavaMethodImpl method) { |
| this.metaspaceMethodData = metaspaceMethodData; |
| this.method = method; |
| } |
| |
| /** |
| * @return value of the MethodData::_data_size field |
| */ |
| private int normalDataSize() { |
| return UNSAFE.getInt(metaspaceMethodData + config.methodDataDataSize); |
| } |
| |
| /** |
| * Returns the size of the extra data records. This method does the same calculation as |
| * MethodData::extra_data_size(). |
| * |
| * @return size of extra data records |
| */ |
| private int extraDataSize() { |
| final int extraDataBase = config.methodDataOopDataOffset + normalDataSize(); |
| final int extraDataLimit = UNSAFE.getInt(metaspaceMethodData + config.methodDataSize); |
| return extraDataLimit - extraDataBase; |
| } |
| |
| public boolean hasNormalData() { |
| return normalDataSize() > 0; |
| } |
| |
| public boolean hasExtraData() { |
| return extraDataSize() > 0; |
| } |
| |
| public int getExtraDataBeginOffset() { |
| return normalDataSize(); |
| } |
| |
| public boolean isWithin(int position) { |
| return position >= 0 && position < normalDataSize() + extraDataSize(); |
| } |
| |
| public int getDeoptimizationCount(DeoptimizationReason reason) { |
| HotSpotMetaAccessProvider metaAccess = (HotSpotMetaAccessProvider) runtime().getHostJVMCIBackend().getMetaAccess(); |
| int reasonIndex = metaAccess.convertDeoptReason(reason); |
| return UNSAFE.getByte(metaspaceMethodData + config.methodDataOopTrapHistoryOffset + reasonIndex) & 0xFF; |
| } |
| |
| public int getOSRDeoptimizationCount(DeoptimizationReason reason) { |
| HotSpotMetaAccessProvider metaAccess = (HotSpotMetaAccessProvider) runtime().getHostJVMCIBackend().getMetaAccess(); |
| int reasonIndex = metaAccess.convertDeoptReason(reason); |
| return UNSAFE.getByte(metaspaceMethodData + config.methodDataOopTrapHistoryOffset + config.deoptReasonOSROffset + reasonIndex) & 0xFF; |
| } |
| |
| public int getDecompileCount() { |
| return UNSAFE.getInt(metaspaceMethodData + config.methodDataDecompiles); |
| } |
| |
| public int getOverflowRecompileCount() { |
| return UNSAFE.getInt(metaspaceMethodData + config.methodDataOverflowRecompiles); |
| } |
| |
| public int getOverflowTrapCount() { |
| return UNSAFE.getInt(metaspaceMethodData + config.methodDataOverflowTraps); |
| } |
| |
| public HotSpotMethodDataAccessor getNormalData(int position) { |
| if (position >= normalDataSize()) { |
| return null; |
| } |
| |
| return getData(position); |
| } |
| |
| public HotSpotMethodDataAccessor getExtraData(int position) { |
| if (position >= normalDataSize() + extraDataSize()) { |
| return null; |
| } |
| HotSpotMethodDataAccessor data = getData(position); |
| if (data != null) { |
| return data; |
| } |
| return data; |
| } |
| |
| public static HotSpotMethodDataAccessor getNoDataAccessor(boolean exceptionPossiblyNotRecorded) { |
| if (exceptionPossiblyNotRecorded) { |
| return NO_DATA_EXCEPTION_POSSIBLY_NOT_RECORDED_ACCESSOR; |
| } else { |
| return NO_DATA_NO_EXCEPTION_ACCESSOR; |
| } |
| } |
| |
| private HotSpotMethodDataAccessor getData(int position) { |
| assert position >= 0 : "out of bounds"; |
| final int tag = HotSpotMethodDataAccessor.readTag(config, this, position); |
| HotSpotMethodDataAccessor accessor = PROFILE_DATA_ACCESSORS[tag]; |
| assert accessor == null || accessor.getTag() == tag : "wrong data accessor " + accessor + " for tag " + tag; |
| return accessor; |
| } |
| |
| int readUnsignedByte(int position, int offsetInBytes) { |
| long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); |
| return UNSAFE.getByte(metaspaceMethodData + fullOffsetInBytes) & 0xFF; |
| } |
| |
| int readUnsignedShort(int position, int offsetInBytes) { |
| long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); |
| return UNSAFE.getShort(metaspaceMethodData + fullOffsetInBytes) & 0xFFFF; |
| } |
| |
| /** |
| * Since the values are stored in cells (platform words) this method uses |
| * {@link Unsafe#getAddress} to read the right value on both little and big endian machines. |
| */ |
| private long readUnsignedInt(int position, int offsetInBytes) { |
| long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); |
| return UNSAFE.getAddress(metaspaceMethodData + fullOffsetInBytes) & 0xFFFFFFFFL; |
| } |
| |
| private int readUnsignedIntAsSignedInt(int position, int offsetInBytes) { |
| long value = readUnsignedInt(position, offsetInBytes); |
| return truncateLongToInt(value); |
| } |
| |
| /** |
| * Since the values are stored in cells (platform words) this method uses |
| * {@link Unsafe#getAddress} to read the right value on both little and big endian machines. |
| */ |
| private int readInt(int position, int offsetInBytes) { |
| long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); |
| return (int) UNSAFE.getAddress(metaspaceMethodData + fullOffsetInBytes); |
| } |
| |
| private HotSpotResolvedJavaMethod readMethod(int position, int offsetInBytes) { |
| long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); |
| return compilerToVM().getResolvedJavaMethod(null, metaspaceMethodData + fullOffsetInBytes); |
| } |
| |
| private HotSpotResolvedObjectTypeImpl readKlass(int position, int offsetInBytes) { |
| long fullOffsetInBytes = computeFullOffset(position, offsetInBytes); |
| return compilerToVM().getResolvedJavaType(null, metaspaceMethodData + fullOffsetInBytes, false); |
| } |
| |
| private static int truncateLongToInt(long value) { |
| return value > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) value; |
| } |
| |
| private static int computeFullOffset(int position, int offsetInBytes) { |
| return config.methodDataOopDataOffset + position + offsetInBytes; |
| } |
| |
| private static int cellIndexToOffset(int cells) { |
| return config.dataLayoutHeaderSize + cellsToBytes(cells); |
| } |
| |
| private static int cellsToBytes(int cells) { |
| return cells * config.dataLayoutCellSize; |
| } |
| |
| /** |
| * Returns whether profiling ran long enough that the profile information is mature. Other |
| * informational data will still be valid even if the profile isn't mature. |
| */ |
| public boolean isProfileMature() { |
| return runtime().getCompilerToVM().isMature(metaspaceMethodData); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| String nl = String.format("%n"); |
| String nlIndent = String.format("%n%38s", ""); |
| sb.append("Raw method data for "); |
| sb.append(method.format("%H.%n(%p)")); |
| sb.append(":"); |
| sb.append(nl); |
| sb.append(String.format("nof_decompiles(%d) nof_overflow_recompiles(%d) nof_overflow_traps(%d)%n", |
| getDecompileCount(), getOverflowRecompileCount(), getOverflowTrapCount())); |
| if (hasNormalData()) { |
| int pos = 0; |
| HotSpotMethodDataAccessor data; |
| while ((data = getNormalData(pos)) != null) { |
| if (pos != 0) { |
| sb.append(nl); |
| } |
| int bci = data.getBCI(this, pos); |
| sb.append(String.format("%-6d bci: %-6d%-20s", pos, bci, data.getClass().getSimpleName())); |
| sb.append(data.appendTo(new StringBuilder(), this, pos).toString().replace(nl, nlIndent)); |
| pos = pos + data.getSize(this, pos); |
| } |
| } |
| |
| if (hasExtraData()) { |
| int pos = getExtraDataBeginOffset(); |
| HotSpotMethodDataAccessor data; |
| while ((data = getExtraData(pos)) != null) { |
| if (pos == getExtraDataBeginOffset()) { |
| sb.append(nl).append("--- Extra data:"); |
| } |
| int bci = data.getBCI(this, pos); |
| sb.append(String.format("%n%-6d bci: %-6d%-20s", pos, bci, data.getClass().getSimpleName())); |
| sb.append(data.appendTo(new StringBuilder(), this, pos).toString().replace(nl, nlIndent)); |
| pos = pos + data.getSize(this, pos); |
| } |
| |
| } |
| return sb.toString(); |
| } |
| |
| static final int NO_DATA_SIZE = cellIndexToOffset(0); |
| |
| static class NoMethodData extends HotSpotMethodDataAccessor { |
| |
| private final TriState exceptionSeen; |
| |
| protected NoMethodData(HotSpotVMConfig config, int tag, TriState exceptionSeen) { |
| super(config, tag, NO_DATA_SIZE); |
| this.exceptionSeen = exceptionSeen; |
| } |
| |
| @Override |
| public int getBCI(HotSpotMethodData data, int position) { |
| return -1; |
| } |
| |
| @Override |
| public TriState getExceptionSeen(HotSpotMethodData data, int position) { |
| return exceptionSeen; |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| return sb; |
| } |
| } |
| |
| static final int BIT_DATA_SIZE = cellIndexToOffset(0); |
| static final int BIT_DATA_NULL_SEEN_FLAG = 1 << config.bitDataNullSeenFlag; |
| |
| static class BitData extends HotSpotMethodDataAccessor { |
| |
| private BitData(HotSpotVMConfig config, int tag) { |
| super(config, tag, BIT_DATA_SIZE); |
| } |
| |
| protected BitData(HotSpotVMConfig config, int tag, int staticSize) { |
| super(config, tag, staticSize); |
| } |
| |
| @Override |
| public TriState getNullSeen(HotSpotMethodData data, int position) { |
| return TriState.get((getFlags(data, position) & BIT_DATA_NULL_SEEN_FLAG) != 0); |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| return sb.append(format("exception_seen(%s)", getExceptionSeen(data, pos))); |
| } |
| } |
| |
| static final int COUNTER_DATA_SIZE = cellIndexToOffset(1); |
| static final int COUNTER_DATA_COUNT_OFFSET = cellIndexToOffset(config.methodDataCountOffset); |
| |
| static class CounterData extends BitData { |
| |
| CounterData(HotSpotVMConfig config, int tag) { |
| super(config, tag, COUNTER_DATA_SIZE); |
| } |
| |
| protected CounterData(HotSpotVMConfig config, int tag, int staticSize) { |
| super(config, tag, staticSize); |
| } |
| |
| @Override |
| public int getExecutionCount(HotSpotMethodData data, int position) { |
| return getCounterValue(data, position); |
| } |
| |
| protected int getCounterValue(HotSpotMethodData data, int position) { |
| return data.readUnsignedIntAsSignedInt(position, COUNTER_DATA_COUNT_OFFSET); |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| return sb.append(format("count(%d) null_seen(%s) exception_seen(%s)", getCounterValue(data, pos), getNullSeen(data, pos), getExceptionSeen(data, pos))); |
| } |
| } |
| |
| static final int JUMP_DATA_SIZE = cellIndexToOffset(2); |
| static final int TAKEN_COUNT_OFFSET = cellIndexToOffset(config.jumpDataTakenOffset); |
| static final int TAKEN_DISPLACEMENT_OFFSET = cellIndexToOffset(config.jumpDataDisplacementOffset); |
| |
| static class JumpData extends HotSpotMethodDataAccessor { |
| |
| JumpData(HotSpotVMConfig config, int tag) { |
| super(config, tag, JUMP_DATA_SIZE); |
| } |
| |
| protected JumpData(HotSpotVMConfig config, int tag, int staticSize) { |
| super(config, tag, staticSize); |
| } |
| |
| @Override |
| public double getBranchTakenProbability(HotSpotMethodData data, int position) { |
| return getExecutionCount(data, position) != 0 ? 1 : 0; |
| } |
| |
| @Override |
| public int getExecutionCount(HotSpotMethodData data, int position) { |
| return data.readUnsignedIntAsSignedInt(position, TAKEN_COUNT_OFFSET); |
| } |
| |
| public int getTakenDisplacement(HotSpotMethodData data, int position) { |
| return data.readInt(position, TAKEN_DISPLACEMENT_OFFSET); |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| return sb.append(format("taken(%d) displacement(%d)", getExecutionCount(data, pos), getTakenDisplacement(data, pos))); |
| } |
| } |
| |
| static class RawItemProfile<T> { |
| final int entries; |
| final T[] items; |
| final long[] counts; |
| final long totalCount; |
| |
| RawItemProfile(int entries, T[] items, long[] counts, long totalCount) { |
| this.entries = entries; |
| this.items = items; |
| this.counts = counts; |
| this.totalCount = totalCount; |
| } |
| } |
| |
| static final int TYPE_DATA_ROW_SIZE = cellsToBytes(config.receiverTypeDataReceiverTypeRowCellCount); |
| |
| static final int NONPROFILED_COUNT_OFFSET = cellIndexToOffset(config.receiverTypeDataNonprofiledCountOffset); |
| static final int TYPE_DATA_FIRST_TYPE_OFFSET = cellIndexToOffset(config.receiverTypeDataReceiver0Offset); |
| static final int TYPE_DATA_FIRST_TYPE_COUNT_OFFSET = cellIndexToOffset(config.receiverTypeDataCount0Offset); |
| |
| abstract static class AbstractTypeData extends CounterData { |
| |
| protected AbstractTypeData(HotSpotVMConfig config, int tag, int staticSize) { |
| super(config, tag, staticSize); |
| } |
| |
| @Override |
| public JavaTypeProfile getTypeProfile(HotSpotMethodData data, int position) { |
| return createTypeProfile(getNullSeen(data, position), getRawTypeProfile(data, position)); |
| } |
| |
| private RawItemProfile<ResolvedJavaType> getRawTypeProfile(HotSpotMethodData data, int position) { |
| int typeProfileWidth = config.typeProfileWidth; |
| |
| ResolvedJavaType[] types = new ResolvedJavaType[typeProfileWidth]; |
| long[] counts = new long[typeProfileWidth]; |
| long totalCount = 0; |
| int entries = 0; |
| |
| outer: for (int i = 0; i < typeProfileWidth; i++) { |
| HotSpotResolvedObjectTypeImpl receiverKlass = data.readKlass(position, getTypeOffset(i)); |
| if (receiverKlass != null) { |
| HotSpotResolvedObjectTypeImpl klass = receiverKlass; |
| long count = data.readUnsignedInt(position, getTypeCountOffset(i)); |
| /* |
| * Because of races in the profile collection machinery it's possible for a |
| * class to appear multiple times so merge them to make the profile look |
| * rational. |
| */ |
| for (int j = 0; j < entries; j++) { |
| if (types[j].equals(klass)) { |
| totalCount += count; |
| counts[j] += count; |
| continue outer; |
| } |
| } |
| types[entries] = klass; |
| totalCount += count; |
| counts[entries] = count; |
| entries++; |
| } |
| } |
| |
| totalCount += getTypesNotRecordedExecutionCount(data, position); |
| return new RawItemProfile<>(entries, types, counts, totalCount); |
| } |
| |
| protected abstract long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position); |
| |
| public int getNonprofiledCount(HotSpotMethodData data, int position) { |
| return data.readUnsignedIntAsSignedInt(position, NONPROFILED_COUNT_OFFSET); |
| } |
| |
| private JavaTypeProfile createTypeProfile(TriState nullSeen, RawItemProfile<ResolvedJavaType> profile) { |
| if (profile.entries <= 0 || profile.totalCount <= 0) { |
| return null; |
| } |
| |
| ProfiledType[] ptypes = new ProfiledType[profile.entries]; |
| double totalProbability = 0.0; |
| for (int i = 0; i < profile.entries; i++) { |
| double p = profile.counts[i]; |
| p = p / profile.totalCount; |
| totalProbability += p; |
| ptypes[i] = new ProfiledType(profile.items[i], p); |
| } |
| |
| Arrays.sort(ptypes); |
| |
| double notRecordedTypeProbability = profile.entries < config.typeProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability)); |
| assert notRecordedTypeProbability == 0 || profile.entries == config.typeProfileWidth; |
| return new JavaTypeProfile(nullSeen, notRecordedTypeProbability, ptypes); |
| } |
| |
| private static int getTypeOffset(int row) { |
| return TYPE_DATA_FIRST_TYPE_OFFSET + row * TYPE_DATA_ROW_SIZE; |
| } |
| |
| protected static int getTypeCountOffset(int row) { |
| return TYPE_DATA_FIRST_TYPE_COUNT_OFFSET + row * TYPE_DATA_ROW_SIZE; |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| RawItemProfile<ResolvedJavaType> profile = getRawTypeProfile(data, pos); |
| TriState nullSeen = getNullSeen(data, pos); |
| TriState exceptionSeen = getExceptionSeen(data, pos); |
| sb.append(format("count(%d) null_seen(%s) exception_seen(%s) nonprofiled_count(%d) entries(%d)", getCounterValue(data, pos), nullSeen, exceptionSeen, |
| getNonprofiledCount(data, pos), profile.entries)); |
| for (int i = 0; i < profile.entries; i++) { |
| long count = profile.counts[i]; |
| sb.append(format("%n %s (%d, %4.2f)", profile.items[i].toJavaName(), count, (double) count / profile.totalCount)); |
| } |
| return sb; |
| } |
| } |
| |
| static final int TYPE_CHECK_DATA_SIZE = cellIndexToOffset(2) + TYPE_DATA_ROW_SIZE * config.typeProfileWidth; |
| |
| static class ReceiverTypeData extends AbstractTypeData { |
| |
| ReceiverTypeData(HotSpotVMConfig config, int tag) { |
| super(config, tag, TYPE_CHECK_DATA_SIZE); |
| } |
| |
| protected ReceiverTypeData(HotSpotVMConfig config, int tag, int staticSize) { |
| super(config, tag, staticSize); |
| } |
| |
| @Override |
| public int getExecutionCount(HotSpotMethodData data, int position) { |
| return -1; |
| } |
| |
| @Override |
| protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) { |
| return getNonprofiledCount(data, position); |
| } |
| } |
| |
| static final int VIRTUAL_CALL_DATA_SIZE = cellIndexToOffset(2) + TYPE_DATA_ROW_SIZE * (config.typeProfileWidth + config.methodProfileWidth); |
| static final int VIRTUAL_CALL_DATA_FIRST_METHOD_OFFSET = TYPE_DATA_FIRST_TYPE_OFFSET + TYPE_DATA_ROW_SIZE * config.typeProfileWidth; |
| static final int VIRTUAL_CALL_DATA_FIRST_METHOD_COUNT_OFFSET = TYPE_DATA_FIRST_TYPE_COUNT_OFFSET + TYPE_DATA_ROW_SIZE * config.typeProfileWidth; |
| |
| static class VirtualCallData extends ReceiverTypeData { |
| |
| VirtualCallData(HotSpotVMConfig config, int tag) { |
| super(config, tag, VIRTUAL_CALL_DATA_SIZE); |
| } |
| |
| protected VirtualCallData(HotSpotVMConfig config, int tag, int staticSize) { |
| super(config, tag, staticSize); |
| } |
| |
| @Override |
| public int getExecutionCount(HotSpotMethodData data, int position) { |
| final int typeProfileWidth = config.typeProfileWidth; |
| |
| long total = 0; |
| for (int i = 0; i < typeProfileWidth; i++) { |
| total += data.readUnsignedInt(position, getTypeCountOffset(i)); |
| } |
| |
| total += getCounterValue(data, position); |
| return truncateLongToInt(total); |
| } |
| |
| @Override |
| protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) { |
| return getCounterValue(data, position); |
| } |
| |
| private static long getMethodsNotRecordedExecutionCount(HotSpotMethodData data, int position) { |
| return data.readUnsignedIntAsSignedInt(position, NONPROFILED_COUNT_OFFSET); |
| } |
| |
| @Override |
| public JavaMethodProfile getMethodProfile(HotSpotMethodData data, int position) { |
| return createMethodProfile(getRawMethodProfile(data, position)); |
| } |
| |
| private RawItemProfile<ResolvedJavaMethod> getRawMethodProfile(HotSpotMethodData data, int position) { |
| int profileWidth = config.methodProfileWidth; |
| |
| ResolvedJavaMethod[] methods = new ResolvedJavaMethod[profileWidth]; |
| long[] counts = new long[profileWidth]; |
| long totalCount = 0; |
| int entries = 0; |
| |
| for (int i = 0; i < profileWidth; i++) { |
| HotSpotResolvedJavaMethod method = data.readMethod(position, getMethodOffset(i)); |
| if (method != null) { |
| methods[entries] = method; |
| long count = data.readUnsignedInt(position, getMethodCountOffset(i)); |
| totalCount += count; |
| counts[entries] = count; |
| |
| entries++; |
| } |
| } |
| |
| totalCount += getMethodsNotRecordedExecutionCount(data, position); |
| return new RawItemProfile<>(entries, methods, counts, totalCount); |
| } |
| |
| private JavaMethodProfile createMethodProfile(RawItemProfile<ResolvedJavaMethod> profile) { |
| if (profile.entries <= 0 || profile.totalCount <= 0) { |
| return null; |
| } |
| |
| ProfiledMethod[] pmethods = new ProfiledMethod[profile.entries]; |
| double totalProbability = 0.0; |
| for (int i = 0; i < profile.entries; i++) { |
| double p = profile.counts[i]; |
| p = p / profile.totalCount; |
| totalProbability += p; |
| pmethods[i] = new ProfiledMethod(profile.items[i], p); |
| } |
| |
| Arrays.sort(pmethods); |
| |
| double notRecordedMethodProbability = profile.entries < config.methodProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability)); |
| assert notRecordedMethodProbability == 0 || profile.entries == config.methodProfileWidth; |
| return new JavaMethodProfile(notRecordedMethodProbability, pmethods); |
| } |
| |
| private static int getMethodOffset(int row) { |
| return VIRTUAL_CALL_DATA_FIRST_METHOD_OFFSET + row * TYPE_DATA_ROW_SIZE; |
| } |
| |
| private static int getMethodCountOffset(int row) { |
| return VIRTUAL_CALL_DATA_FIRST_METHOD_COUNT_OFFSET + row * TYPE_DATA_ROW_SIZE; |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| RawItemProfile<ResolvedJavaMethod> profile = getRawMethodProfile(data, pos); |
| super.appendTo(sb.append(format("exception_seen(%s) ", getExceptionSeen(data, pos))), data, pos).append(format("%nmethod_entries(%d)", profile.entries)); |
| for (int i = 0; i < profile.entries; i++) { |
| long count = profile.counts[i]; |
| sb.append(format("%n %s (%d, %4.2f)", profile.items[i].format("%H.%n(%p)"), count, (double) count / profile.totalCount)); |
| } |
| return sb; |
| } |
| } |
| |
| static class VirtualCallTypeData extends VirtualCallData { |
| |
| VirtualCallTypeData(HotSpotVMConfig config, int tag) { |
| super(config, tag, 0); |
| } |
| |
| @Override |
| protected int getDynamicSize(HotSpotMethodData data, int position) { |
| assert staticSize == 0; |
| return HotSpotJVMCIRuntime.runtime().compilerToVm.methodDataProfileDataSize(data.metaspaceMethodData, position); |
| } |
| } |
| |
| static final int RET_DATA_ROW_SIZE = cellsToBytes(3); |
| static final int RET_DATA_SIZE = cellIndexToOffset(1) + RET_DATA_ROW_SIZE * config.bciProfileWidth; |
| |
| static class RetData extends CounterData { |
| |
| RetData(HotSpotVMConfig config, int tag) { |
| super(config, tag, RET_DATA_SIZE); |
| } |
| } |
| |
| static final int BRANCH_DATA_SIZE = cellIndexToOffset(3); |
| static final int NOT_TAKEN_COUNT_OFFSET = cellIndexToOffset(config.branchDataNotTakenOffset); |
| |
| static class BranchData extends JumpData { |
| |
| BranchData(HotSpotVMConfig config, int tag) { |
| super(config, tag, BRANCH_DATA_SIZE); |
| } |
| |
| @Override |
| public double getBranchTakenProbability(HotSpotMethodData data, int position) { |
| long takenCount = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET); |
| long notTakenCount = data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET); |
| long total = takenCount + notTakenCount; |
| |
| return total <= 0 ? -1 : takenCount / (double) total; |
| } |
| |
| @Override |
| public int getExecutionCount(HotSpotMethodData data, int position) { |
| long count = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET) + data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET); |
| return truncateLongToInt(count); |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| long taken = data.readUnsignedInt(pos, TAKEN_COUNT_OFFSET); |
| long notTaken = data.readUnsignedInt(pos, NOT_TAKEN_COUNT_OFFSET); |
| double takenProbability = getBranchTakenProbability(data, pos); |
| return sb.append(format("taken(%d, %4.2f) not_taken(%d, %4.2f) displacement(%d)", taken, takenProbability, notTaken, 1.0D - takenProbability, getTakenDisplacement(data, pos))); |
| } |
| } |
| |
| static final int ARRAY_DATA_LENGTH_OFFSET = cellIndexToOffset(config.arrayDataArrayLenOffset); |
| static final int ARRAY_DATA_START_OFFSET = cellIndexToOffset(config.arrayDataArrayStartOffset); |
| |
| static class ArrayData extends HotSpotMethodDataAccessor { |
| |
| ArrayData(HotSpotVMConfig config, int tag, int staticSize) { |
| super(config, tag, staticSize); |
| } |
| |
| @Override |
| protected int getDynamicSize(HotSpotMethodData data, int position) { |
| return cellsToBytes(getLength(data, position)); |
| } |
| |
| protected static int getLength(HotSpotMethodData data, int position) { |
| return data.readInt(position, ARRAY_DATA_LENGTH_OFFSET); |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| return sb.append(format("length(%d)", getLength(data, pos))); |
| } |
| } |
| |
| static final int MULTI_BRANCH_DATA_SIZE = cellIndexToOffset(1); |
| static final int MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS = config.multiBranchDataPerCaseCellCount; |
| static final int MULTI_BRANCH_DATA_ROW_SIZE = cellsToBytes(MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS); |
| static final int MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(0); |
| static final int MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(1); |
| |
| static class MultiBranchData extends ArrayData { |
| |
| MultiBranchData(HotSpotVMConfig config, int tag) { |
| super(config, tag, MULTI_BRANCH_DATA_SIZE); |
| } |
| |
| @Override |
| public double[] getSwitchProbabilities(HotSpotMethodData data, int position) { |
| int arrayLength = getLength(data, position); |
| assert arrayLength > 0 : "switch must have at least the default case"; |
| assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows"; |
| |
| int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS; |
| long totalCount = 0; |
| double[] result = new double[length]; |
| |
| // default case is first in HotSpot but last for the compiler |
| long count = readCount(data, position, 0); |
| totalCount += count; |
| result[length - 1] = count; |
| |
| for (int i = 1; i < length; i++) { |
| count = readCount(data, position, i); |
| totalCount += count; |
| result[i - 1] = count; |
| } |
| |
| if (totalCount <= 0) { |
| return null; |
| } else { |
| for (int i = 0; i < length; i++) { |
| result[i] = result[i] / totalCount; |
| } |
| return result; |
| } |
| } |
| |
| private static long readCount(HotSpotMethodData data, int position, int i) { |
| int offset; |
| long count; |
| offset = getCountOffset(i); |
| count = data.readUnsignedInt(position, offset); |
| return count; |
| } |
| |
| @Override |
| public int getExecutionCount(HotSpotMethodData data, int position) { |
| int arrayLength = getLength(data, position); |
| assert arrayLength > 0 : "switch must have at least the default case"; |
| assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows"; |
| |
| int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS; |
| long totalCount = 0; |
| for (int i = 0; i < length; i++) { |
| int offset = getCountOffset(i); |
| totalCount += data.readUnsignedInt(position, offset); |
| } |
| |
| return truncateLongToInt(totalCount); |
| } |
| |
| private static int getCountOffset(int index) { |
| return MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE; |
| } |
| |
| private static int getDisplacementOffset(int index) { |
| return MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE; |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| int entries = getLength(data, pos) / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS; |
| sb.append(format("entries(%d)", entries)); |
| for (int i = 0; i < entries; i++) { |
| sb.append(format("%n %d: count(%d) displacement(%d)", i, data.readUnsignedInt(pos, getCountOffset(i)), data.readUnsignedInt(pos, getDisplacementOffset(i)))); |
| } |
| return sb; |
| } |
| } |
| |
| static final int ARG_INFO_DATA_SIZE = cellIndexToOffset(1); |
| |
| static class ArgInfoData extends ArrayData { |
| |
| ArgInfoData(HotSpotVMConfig config, int tag) { |
| super(config, tag, ARG_INFO_DATA_SIZE); |
| } |
| } |
| |
| static class UnknownProfileData extends HotSpotMethodDataAccessor { |
| UnknownProfileData(HotSpotVMConfig config, int tag) { |
| super(config, tag, 0); |
| } |
| |
| @Override |
| protected int getDynamicSize(HotSpotMethodData data, int position) { |
| assert staticSize == 0; |
| return HotSpotJVMCIRuntime.runtime().compilerToVm.methodDataProfileDataSize(data.metaspaceMethodData, position); |
| } |
| |
| @Override |
| public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { |
| sb.append("unknown profile data with tag: " + tag); |
| return sb; |
| } |
| } |
| |
| public void setCompiledIRSize(int size) { |
| UNSAFE.putInt(metaspaceMethodData + config.methodDataIRSizeOffset, size); |
| } |
| |
| public int getCompiledIRSize() { |
| return UNSAFE.getInt(metaspaceMethodData + config.methodDataIRSizeOffset); |
| } |
| |
| // sorted by tag |
| // @formatter:off |
| static final HotSpotMethodDataAccessor[] PROFILE_DATA_ACCESSORS = { |
| null, |
| new BitData(config, config.dataLayoutBitDataTag), |
| new CounterData(config, config.dataLayoutCounterDataTag), |
| new JumpData(config, config.dataLayoutJumpDataTag), |
| new ReceiverTypeData(config, config.dataLayoutReceiverTypeDataTag), |
| new VirtualCallData(config, config.dataLayoutVirtualCallDataTag), |
| new RetData(config, config.dataLayoutRetDataTag), |
| new BranchData(config, config.dataLayoutBranchDataTag), |
| new MultiBranchData(config, config.dataLayoutMultiBranchDataTag), |
| new ArgInfoData(config, config.dataLayoutArgInfoDataTag), |
| new UnknownProfileData(config, config.dataLayoutCallTypeDataTag), |
| new VirtualCallTypeData(config, config.dataLayoutVirtualCallTypeDataTag), |
| new UnknownProfileData(config, config.dataLayoutParametersTypeDataTag), |
| new UnknownProfileData(config, config.dataLayoutSpeculativeTrapDataTag), |
| }; |
| |
| private static boolean checkAccessorTags() { |
| int expectedTag = 0; |
| for (HotSpotMethodDataAccessor accessor : PROFILE_DATA_ACCESSORS) { |
| if (expectedTag == 0) { |
| assert accessor == null; |
| } else { |
| assert accessor.tag == expectedTag : expectedTag + " != " + accessor.tag + " " + accessor; |
| } |
| expectedTag++; |
| } |
| return true; |
| } |
| |
| static { |
| assert checkAccessorTags(); |
| } |
| // @formatter:on |
| } |