/*
* Copyright (c) 2009, 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 com.sun.max.vm.profile;
import java.util.*;
import com.sun.max.annotate.*;
import com.sun.max.program.*;
import com.sun.max.vm.compiler.target.*;
/**
* This class collects profiling information of various kinds into a single place.
* It implements a dense encoding for space-efficient recording of various types
* of profiling information, including method entry count, location count, taken/not taken
* branch counts, and receiver type and method profiles.
*
* Typically the instrumentation code that updates the underlying data is not synchronized,
* so users of this information should be prepared for the occasional oddity (e.g.
* branch taken + branch not taken != count of entry of block).
*
* This class only stores primitive profiling information in the form of ints--i.e. no Objects.
* Therefore profiling receiver method types and method implementations can use approximations
* (e.g. the low order bits of the code entrypoint address) or the ID of a type instead
* of the type itself.
*
* This class maintains the data sorted by bytecode index and therefore most operations to
* receive information for a particular BCI take logarithmic time.
*/
public class MethodProfile {
private static final byte METHOD_ENTRY = 0;
private static final byte BC_LOCATION = 1;
private static final byte BR_TAKEN = 2;
private static final byte BR_NOT_TAKEN = 3;
private static final byte RECVR_TYPE = 4;
private static final byte RECVR_METHOD = 5;
private static final byte RECVR_COUNT = 6;
private static final byte RECVR_NOT_FOUND = 7;
/**
* The method that contains the instrumentation to increase counters in this profile.
*/
public TargetMethod method;
/**
* The method invocation and backward branch counter. Decremented by profiling code.
* This is a separate counter since even methods without heavy profiling need a simple
* invocation counter to trigger recompilation.
*/
public int entryCount;
/**
* Records actual counts of a count entry
* TODO since we don't emit actual profiling yet, this is unused.
*/
private int[] data;
/**
* Records bci and type for each count entry.
* TODO since we don't emit actual profiling yet, this is unused.
*/
private int[] info;
/**
* When {@code true} re-compilation is disabled.
* This is used by JVMTI to prevent methods with JVMTI instrumentation from
* being recompiled with the optimizing compiler (and so removing the instrumentation).
*/
public boolean compilationDisabled;
protected MethodProfile() {
}
/**
* Gets the count at the method entrypoint, if it is available.
* @return the count of the method entrypoint if available;
* {@code null} if this profile info does not have such an entry
*/
public Integer getEntryCount() {
return get(search(0, METHOD_ENTRY));
}
/**
* Gets the type profile of the specified bytecode index, if it is available.
* The data is formatted as an array of integers, in pairs. The first integer in
* a pair represents the ID of a type, and the second integer represents the number
* of times that type was seen.
* @param bci the bytecode index for which to get the information
* @return an array of type id / count pairs;
* {@code null} if this profile info does not have such an entry
*/
public Integer[] getTypeProfile(int bci) {
return extractPairs(bci, RECVR_TYPE);
}
/**
* Gets the receiver method profile of the specified bytecode index, if it is available.
* The data is formatted as an array of integers, in pairs. The first integer in
* a pair represents the low 32 bits of the method entrypoint, and the second integer
* represents the number of times that entrypoint was seen.
* @param bci the bytecode index for which to get the information
* @return an array of entrypoint / count pairs;
* {@code null} if this profile info does not have such an entry
*/
public Integer[] getReceiverProfile(int bci) {
return extractPairs(bci, RECVR_METHOD);
}
/**
* Gets the taken and not taken counts for a branch at the specified index, if they
* are available.
* @param bci the bytecode index for which to get the information
* @return an array of length 2, with the first element representing the taken count,
* if it is available ({@code null} otherwise), the second element representing the
* not taken count, if it is available ({@code null} otherwise); {@code null} if there
* is no information for this branch
*/
public Integer[] getBranchCounts(int bci) {
Integer taken = get(search(bci, BR_TAKEN));
Integer notTaken = get(search(bci, BR_NOT_TAKEN));
if (taken != null || notTaken != null) {
return new Integer[] {taken, notTaken};
}
return null;
}
/**
* Gets the count for a particular bytecode location, if it exists.
* @param bci the bytecode index for which to get the information
* @return the value of the counter at the specified index, if it is available;
* {@code null} if there is no information for this bytecode location
*/
public Integer getLocationCount(int bci) {
return get(search(bci, BC_LOCATION));
}
/**
* Provides access to the raw data of this method profile.
*
* @return the int array that stores the data of this profile
*/
@INLINE
public final int[] rawData() {
return data;
}
/**
* Provides access to the raw type info of this method profile.
*
* @return the int array that stores the type info of this method profile
*/
@INLINE
public final int[] rawInfo() {
return info;
}
private Integer[] extractPairs(int bci, byte type) {
int index = search(bci, type);
int tinfo = encodeInfo(bci, type);
int cinfo = encodeInfo(bci, RECVR_COUNT);
int einfo = encodeInfo(bci, RECVR_NOT_FOUND);
if (index >= 0) {
List<Integer> list = new ArrayList<Integer>();
for (; index < dataLength() - 1; index += 2) {
if (infoAt(index + 1) == cinfo) {
if (infoAt(index) == tinfo) {
list.add(dataAt(index));
list.add(dataAt(index + 1));
} else if (infoAt(index) == einfo) {
list.add(0);
list.add(dataAt(index + 1));
}
}
}
return list.toArray(new Integer[list.size()]);
}
return null;
}
private Integer get(int index) {
if (index >= 0 && index < dataLength()) {
return dataAt(index);
}
return null;
}
private int search(int bci, byte type) {
// search for a specific type of data at a particular bci
int index = search(bci);
if (index >= 0) {
int info = encodeInfo(bci, type);
while (index < dataLength() && bciAt(index) == bci) {
if (infoAt(index) == info) {
return index;
}
index++;
}
}
return -1;
}
private int search(int bci) {
// perform binary search to find the lowest entry with data for the bci
int low = 0;
int high = dataLength() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = bciAt(mid);
if (midVal < bci) {
low = mid + 1;
} else if (midVal > bci) {
high = mid - 1;
} else {
while (mid > 0) {
// rewind to the first entry for this bci
if (bciAt(mid - 1) != bci) {
return mid;
}
mid--;
}
return mid; // key found
}
}
return -(low + 1); // key not found.
}
private int dataLength() {
return data.length;
}
private int infoAt(int index) {
return info[index];
}
private int dataAt(int index) {
return data[index];
}
private static int encodeInfo(int bci, byte type) {
return type | (bci << 16);
}
private byte typeAt(int index) {
return (byte) infoAt(index);
}
private int bciAt(int index) {
return infoAt(index) >>> 16;
}
public boolean protectedEntryCount() {
return entryCount <= MethodInstrumentation.protectionThreshold;
}
/**
* This class implements a builder that collects the instrumentation created for a particular
* method and then packs the information into a dense, sorted representation in the form of
* a {@link MethodProfile}.
*/
public static class Builder {
private List<Integer> infoList = new ArrayList<Integer>();
private List<Integer> dataList = new ArrayList<Integer>();
private final MethodProfile mpo = new MethodProfile();
private int lastBci = 0;
public void addEntryCounter(int initialValue) {
mpo.entryCount = initialValue;
}
public int addGotoCounter(int bci) {
return add(bci, BR_TAKEN, 0);
}
public int addLocationCounter(int initialValue) {
return add(0, BC_LOCATION, initialValue);
}
public int addBranchCounters(int bci) {
add(bci, BR_TAKEN, 0);
return add(bci, BR_NOT_TAKEN, 0) - 1;
}
public int addTypeProfile(int bci, int entries) {
int index = infoList.size();
for (int i = 0; i < entries; i++) {
add(bci, RECVR_TYPE, 0);
add(bci, RECVR_COUNT, 0);
}
add(bci, RECVR_NOT_FOUND, 0);
add(bci, RECVR_COUNT, 0);
return index;
}
public int addMethodProfile(int bci, int entries) {
int index = infoList.size();
for (int i = 0; i < entries; i++) {
add(bci, RECVR_METHOD, 0);
add(bci, RECVR_COUNT, 0);
}
add(bci, RECVR_NOT_FOUND, 0);
add(bci, RECVR_COUNT, 0);
return index;
}
@INLINE
public final MethodProfile methodProfileObject() {
return mpo;
}
public MethodProfile finish(TargetMethod method) {
mpo.method = method;
int size = infoList.size();
if (size > 0) {
int[] data = new int[size];
int[] info = new int[size];
for (int i = 0; i < size; i++) {
info[i] = infoList.get(i);
}
for (int i = 0; i < size; i++) {
data[i] = dataList.get(i);
}
mpo.info = info;
mpo.data = data;
}
return mpo;
}
private int add(int bci, byte type, int value) {
setLastBci(bci);
infoList.add(encodeInfo(0, type));
dataList.add(value);
return infoList.size() - 1;
}
private void setLastBci(int bci) {
if (bci < lastBci) {
throw ProgramError.unexpected("Profiling information not added in increasing BCI order: " + bci);
}
lastBci = bci;
}
}
}