/* * 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 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; } }