blob: 69c5247929ccc333dcc1fed4da3324034c7a7535 [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.traceview;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
public class MethodData {
private int mId;
private int mRank = -1;
private String mClassName;
private String mMethodName;
private String mSignature;
private String mName;
private String mProfileName;
private String mPathname;
private int mLineNumber;
private long mElapsedExclusiveCpuTime;
private long mElapsedInclusiveCpuTime;
private long mTopExclusiveCpuTime;
private long mElapsedExclusiveRealTime;
private long mElapsedInclusiveRealTime;
private long mTopExclusiveRealTime;
private int[] mNumCalls = new int[2]; // index 0=normal, 1=recursive
private Color mColor;
private Color mFadedColor;
private Image mImage;
private Image mFadedImage;
private HashMap<Integer, ProfileData> mParents;
private HashMap<Integer, ProfileData> mChildren;
// The parents of this method when this method was in a recursive call
private HashMap<Integer, ProfileData> mRecursiveParents;
// The children of this method when this method was in a recursive call
private HashMap<Integer, ProfileData> mRecursiveChildren;
private ProfileNode[] mProfileNodes;
private int mX;
private int mY;
private double mWeight;
public MethodData(int id, String className) {
mId = id;
mClassName = className;
mMethodName = null;
mSignature = null;
mPathname = null;
mLineNumber = -1;
computeName();
computeProfileName();
}
public MethodData(int id, String className, String methodName,
String signature, String pathname, int lineNumber) {
mId = id;
mClassName = className;
mMethodName = methodName;
mSignature = signature;
mPathname = pathname;
mLineNumber = lineNumber;
computeName();
computeProfileName();
}
public double addWeight(int x, int y, double weight) {
if (mX == x && mY == y)
mWeight += weight;
else {
mX = x;
mY = y;
mWeight = weight;
}
return mWeight;
}
public void clearWeight() {
mWeight = 0;
}
public int getRank() {
return mRank;
}
public void setRank(int rank) {
mRank = rank;
computeProfileName();
}
public void addElapsedExclusive(long cpuTime, long realTime) {
mElapsedExclusiveCpuTime += cpuTime;
mElapsedExclusiveRealTime += realTime;
}
public void addElapsedInclusive(long cpuTime, long realTime,
boolean isRecursive, Call parent) {
if (isRecursive == false) {
mElapsedInclusiveCpuTime += cpuTime;
mElapsedInclusiveRealTime += realTime;
mNumCalls[0] += 1;
} else {
mNumCalls[1] += 1;
}
if (parent == null)
return;
// Find the child method in the parent
MethodData parentMethod = parent.getMethodData();
if (parent.isRecursive()) {
parentMethod.mRecursiveChildren = updateInclusive(cpuTime, realTime,
parentMethod, this, false,
parentMethod.mRecursiveChildren);
} else {
parentMethod.mChildren = updateInclusive(cpuTime, realTime,
parentMethod, this, false, parentMethod.mChildren);
}
// Find the parent method in the child
if (isRecursive) {
mRecursiveParents = updateInclusive(cpuTime, realTime, this, parentMethod, true,
mRecursiveParents);
} else {
mParents = updateInclusive(cpuTime, realTime, this, parentMethod, true,
mParents);
}
}
private HashMap<Integer, ProfileData> updateInclusive(long cpuTime, long realTime,
MethodData contextMethod, MethodData elementMethod,
boolean elementIsParent, HashMap<Integer, ProfileData> map) {
if (map == null) {
map = new HashMap<Integer, ProfileData>(4);
} else {
ProfileData profileData = map.get(elementMethod.mId);
if (profileData != null) {
profileData.addElapsedInclusive(cpuTime, realTime);
return map;
}
}
ProfileData elementData = new ProfileData(contextMethod,
elementMethod, elementIsParent);
elementData.setElapsedInclusive(cpuTime, realTime);
elementData.setNumCalls(1);
map.put(elementMethod.mId, elementData);
return map;
}
public void analyzeData(TimeBase timeBase) {
// Sort the parents and children into decreasing inclusive time
ProfileData[] sortedParents;
ProfileData[] sortedChildren;
ProfileData[] sortedRecursiveParents;
ProfileData[] sortedRecursiveChildren;
sortedParents = sortProfileData(mParents, timeBase);
sortedChildren = sortProfileData(mChildren, timeBase);
sortedRecursiveParents = sortProfileData(mRecursiveParents, timeBase);
sortedRecursiveChildren = sortProfileData(mRecursiveChildren, timeBase);
// Add "self" time to the top of the sorted children
sortedChildren = addSelf(sortedChildren);
// Create the ProfileNode objects that we need
ArrayList<ProfileNode> nodes = new ArrayList<ProfileNode>();
ProfileNode profileNode;
if (mParents != null) {
profileNode = new ProfileNode("Parents", this, sortedParents,
true, false);
nodes.add(profileNode);
}
if (mChildren != null) {
profileNode = new ProfileNode("Children", this, sortedChildren,
false, false);
nodes.add(profileNode);
}
if (mRecursiveParents!= null) {
profileNode = new ProfileNode("Parents while recursive", this,
sortedRecursiveParents, true, true);
nodes.add(profileNode);
}
if (mRecursiveChildren != null) {
profileNode = new ProfileNode("Children while recursive", this,
sortedRecursiveChildren, false, true);
nodes.add(profileNode);
}
mProfileNodes = nodes.toArray(new ProfileNode[nodes.size()]);
}
// Create and return a ProfileData[] array that is a sorted copy
// of the given HashMap values.
private ProfileData[] sortProfileData(HashMap<Integer, ProfileData> map,
final TimeBase timeBase) {
if (map == null)
return null;
// Convert the hash values to an array of ProfileData
Collection<ProfileData> values = map.values();
ProfileData[] sorted = values.toArray(new ProfileData[values.size()]);
// Sort the array by elapsed inclusive time
Arrays.sort(sorted, new Comparator<ProfileData>() {
@Override
public int compare(ProfileData pd1, ProfileData pd2) {
if (timeBase.getElapsedInclusiveTime(pd2) > timeBase.getElapsedInclusiveTime(pd1))
return 1;
if (timeBase.getElapsedInclusiveTime(pd2) < timeBase.getElapsedInclusiveTime(pd1))
return -1;
return 0;
}
});
return sorted;
}
private ProfileData[] addSelf(ProfileData[] children) {
ProfileData[] pdata;
if (children == null) {
pdata = new ProfileData[1];
} else {
pdata = new ProfileData[children.length + 1];
System.arraycopy(children, 0, pdata, 1, children.length);
}
pdata[0] = new ProfileSelf(this);
return pdata;
}
public void addTopExclusive(long cpuTime, long realTime) {
mTopExclusiveCpuTime += cpuTime;
mTopExclusiveRealTime += realTime;
}
public long getTopExclusiveCpuTime() {
return mTopExclusiveCpuTime;
}
public long getTopExclusiveRealTime() {
return mTopExclusiveRealTime;
}
public int getId() {
return mId;
}
private void computeName() {
if (mMethodName == null) {
mName = mClassName;
return;
}
StringBuilder sb = new StringBuilder();
sb.append(mClassName);
sb.append("."); //$NON-NLS-1$
sb.append(mMethodName);
sb.append(" "); //$NON-NLS-1$
sb.append(mSignature);
mName = sb.toString();
}
public String getName() {
return mName;
}
public String getClassName() {
return mClassName;
}
public String getMethodName() {
return mMethodName;
}
public String getProfileName() {
return mProfileName;
}
public String getSignature() {
return mSignature;
}
public void computeProfileName() {
if (mRank == -1) {
mProfileName = mName;
return;
}
StringBuilder sb = new StringBuilder();
sb.append(mRank);
sb.append(" "); //$NON-NLS-1$
sb.append(getName());
mProfileName = sb.toString();
}
public String getCalls() {
return String.format("%d+%d", mNumCalls[0], mNumCalls[1]);
}
public int getTotalCalls() {
return mNumCalls[0] + mNumCalls[1];
}
public Color getColor() {
return mColor;
}
public void setColor(Color color) {
mColor = color;
}
public void setImage(Image image) {
mImage = image;
}
public Image getImage() {
return mImage;
}
@Override
public String toString() {
return getName();
}
public long getElapsedExclusiveCpuTime() {
return mElapsedExclusiveCpuTime;
}
public long getElapsedExclusiveRealTime() {
return mElapsedExclusiveRealTime;
}
public long getElapsedInclusiveCpuTime() {
return mElapsedInclusiveCpuTime;
}
public long getElapsedInclusiveRealTime() {
return mElapsedInclusiveRealTime;
}
public void setFadedColor(Color fadedColor) {
mFadedColor = fadedColor;
}
public Color getFadedColor() {
return mFadedColor;
}
public void setFadedImage(Image fadedImage) {
mFadedImage = fadedImage;
}
public Image getFadedImage() {
return mFadedImage;
}
public void setPathname(String pathname) {
mPathname = pathname;
}
public String getPathname() {
return mPathname;
}
public void setLineNumber(int lineNumber) {
mLineNumber = lineNumber;
}
public int getLineNumber() {
return mLineNumber;
}
public ProfileNode[] getProfileNodes() {
return mProfileNodes;
}
public static class Sorter implements Comparator<MethodData> {
@Override
public int compare(MethodData md1, MethodData md2) {
if (mColumn == Column.BY_NAME) {
int result = md1.getName().compareTo(md2.getName());
return (mDirection == Direction.INCREASING) ? result : -result;
}
if (mColumn == Column.BY_INCLUSIVE_CPU_TIME) {
if (md2.getElapsedInclusiveCpuTime() > md1.getElapsedInclusiveCpuTime())
return (mDirection == Direction.INCREASING) ? -1 : 1;
if (md2.getElapsedInclusiveCpuTime() < md1.getElapsedInclusiveCpuTime())
return (mDirection == Direction.INCREASING) ? 1 : -1;
return md1.getName().compareTo(md2.getName());
}
if (mColumn == Column.BY_EXCLUSIVE_CPU_TIME) {
if (md2.getElapsedExclusiveCpuTime() > md1.getElapsedExclusiveCpuTime())
return (mDirection == Direction.INCREASING) ? -1 : 1;
if (md2.getElapsedExclusiveCpuTime() < md1.getElapsedExclusiveCpuTime())
return (mDirection == Direction.INCREASING) ? 1 : -1;
return md1.getName().compareTo(md2.getName());
}
if (mColumn == Column.BY_INCLUSIVE_REAL_TIME) {
if (md2.getElapsedInclusiveRealTime() > md1.getElapsedInclusiveRealTime())
return (mDirection == Direction.INCREASING) ? -1 : 1;
if (md2.getElapsedInclusiveRealTime() < md1.getElapsedInclusiveRealTime())
return (mDirection == Direction.INCREASING) ? 1 : -1;
return md1.getName().compareTo(md2.getName());
}
if (mColumn == Column.BY_EXCLUSIVE_REAL_TIME) {
if (md2.getElapsedExclusiveRealTime() > md1.getElapsedExclusiveRealTime())
return (mDirection == Direction.INCREASING) ? -1 : 1;
if (md2.getElapsedExclusiveRealTime() < md1.getElapsedExclusiveRealTime())
return (mDirection == Direction.INCREASING) ? 1 : -1;
return md1.getName().compareTo(md2.getName());
}
if (mColumn == Column.BY_CALLS) {
int result = md1.getTotalCalls() - md2.getTotalCalls();
if (result == 0)
return md1.getName().compareTo(md2.getName());
return (mDirection == Direction.INCREASING) ? result : -result;
}
if (mColumn == Column.BY_CPU_TIME_PER_CALL) {
double time1 = md1.getElapsedInclusiveCpuTime();
time1 = time1 / md1.getTotalCalls();
double time2 = md2.getElapsedInclusiveCpuTime();
time2 = time2 / md2.getTotalCalls();
double diff = time1 - time2;
int result = 0;
if (diff < 0)
result = -1;
else if (diff > 0)
result = 1;
if (result == 0)
return md1.getName().compareTo(md2.getName());
return (mDirection == Direction.INCREASING) ? result : -result;
}
if (mColumn == Column.BY_REAL_TIME_PER_CALL) {
double time1 = md1.getElapsedInclusiveRealTime();
time1 = time1 / md1.getTotalCalls();
double time2 = md2.getElapsedInclusiveRealTime();
time2 = time2 / md2.getTotalCalls();
double diff = time1 - time2;
int result = 0;
if (diff < 0)
result = -1;
else if (diff > 0)
result = 1;
if (result == 0)
return md1.getName().compareTo(md2.getName());
return (mDirection == Direction.INCREASING) ? result : -result;
}
return 0;
}
public void setColumn(Column column) {
// If the sort column specified is the same as last time,
// then reverse the sort order.
if (mColumn == column) {
// Reverse the sort order
if (mDirection == Direction.INCREASING)
mDirection = Direction.DECREASING;
else
mDirection = Direction.INCREASING;
} else {
// Sort names into increasing order, data into decreasing order.
if (column == Column.BY_NAME)
mDirection = Direction.INCREASING;
else
mDirection = Direction.DECREASING;
}
mColumn = column;
}
public Column getColumn() {
return mColumn;
}
public void setDirection(Direction direction) {
mDirection = direction;
}
public Direction getDirection() {
return mDirection;
}
public static enum Column {
BY_NAME, BY_EXCLUSIVE_CPU_TIME, BY_EXCLUSIVE_REAL_TIME,
BY_INCLUSIVE_CPU_TIME, BY_INCLUSIVE_REAL_TIME, BY_CALLS,
BY_REAL_TIME_PER_CALL, BY_CPU_TIME_PER_CALL,
};
public static enum Direction {
INCREASING, DECREASING
};
private Column mColumn;
private Direction mDirection;
}
}