/*
* 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 mElapsedExclusive;
private long mElapsedInclusive;
private long mTopExclusive;
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();
}
private Comparator<ProfileData> mByElapsedInclusive = new Comparator<ProfileData>() {
public int compare(ProfileData pd1, ProfileData pd2) {
if (pd2.getElapsedInclusive() > pd1.getElapsedInclusive())
return 1;
if (pd2.getElapsedInclusive() < pd1.getElapsedInclusive())
return -1;
return 0;
}
};
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 time) {
mElapsedExclusive += time;
}
public void addElapsedInclusive(long time, boolean isRecursive, Call parent) {
if (isRecursive == false) {
mElapsedInclusive += time;
mNumCalls[0] += 1;
} else {
mNumCalls[1] += 1;
}
if (parent == null)
return;
// Find the child method in the parent
MethodData parentMethod = parent.mMethodData;
if (parent.isRecursive()) {
parentMethod.mRecursiveChildren = updateInclusive(time,
parentMethod, this, false,
parentMethod.mRecursiveChildren);
} else {
parentMethod.mChildren = updateInclusive(time,
parentMethod, this, false, parentMethod.mChildren);
}
// Find the parent method in the child
if (isRecursive) {
mRecursiveParents = updateInclusive(time, this, parentMethod, true,
mRecursiveParents);
} else {
mParents = updateInclusive(time, this, parentMethod, true,
mParents);
}
}
private HashMap<Integer, ProfileData> updateInclusive(long time,
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(time);
return map;
}
}
ProfileData elementData = new ProfileData(contextMethod,
elementMethod, elementIsParent);
elementData.setElapsedInclusive(time);
elementData.setNumCalls(1);
map.put(elementMethod.mId, elementData);
return map;
}
public void analyzeData() {
// Sort the parents and children into decreasing inclusive time
ProfileData[] sortedParents;
ProfileData[] sortedChildren;
ProfileData[] sortedRecursiveParents;
ProfileData[] sortedRecursiveChildren;
sortedParents = sortProfileData(mParents);
sortedChildren = sortProfileData(mChildren);
sortedRecursiveParents = sortProfileData(mRecursiveParents);
sortedRecursiveChildren = sortProfileData(mRecursiveChildren);
// 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) {
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, mByElapsedInclusive);
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 time) {
mTopExclusive += time;
}
public long getTopExclusive() {
return mTopExclusive;
}
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 getElapsedExclusive() {
return mElapsedExclusive;
}
public long getElapsedInclusive() {
return mElapsedInclusive;
}
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> {
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) {
if (md2.getElapsedInclusive() > md1.getElapsedInclusive())
return (mDirection == Direction.INCREASING) ? -1 : 1;
if (md2.getElapsedInclusive() < md1.getElapsedInclusive())
return (mDirection == Direction.INCREASING) ? 1 : -1;
return md1.getName().compareTo(md2.getName());
}
if (mColumn == Column.BY_EXCLUSIVE) {
if (md2.getElapsedExclusive() > md1.getElapsedExclusive())
return (mDirection == Direction.INCREASING) ? -1 : 1;
if (md2.getElapsedExclusive() < md1.getElapsedExclusive())
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_TIME_PER_CALL) {
double time1 = md1.getElapsedInclusive();
time1 = time1 / md1.getTotalCalls();
double time2 = md2.getElapsedInclusive();
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, BY_INCLUSIVE, BY_CALLS, BY_TIME_PER_CALL
};
public static enum Direction {
INCREASING, DECREASING
};
private Column mColumn;
private Direction mDirection;
}
}