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