1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.traceview; 18 19 import org.eclipse.swt.graphics.Color; 20 import org.eclipse.swt.graphics.Image; 21 22 import java.util.ArrayList; 23 import java.util.Arrays; 24 import java.util.Collection; 25 import java.util.Comparator; 26 import java.util.HashMap; 27 28 public class MethodData { 29 30 private int mId; 31 private int mRank = -1; 32 private String mClassName; 33 private String mMethodName; 34 private String mSignature; 35 private String mName; 36 private String mProfileName; 37 private String mPathname; 38 private int mLineNumber; 39 private long mElapsedExclusiveCpuTime; 40 private long mElapsedInclusiveCpuTime; 41 private long mTopExclusiveCpuTime; 42 private long mElapsedExclusiveRealTime; 43 private long mElapsedInclusiveRealTime; 44 private long mTopExclusiveRealTime; 45 private int[] mNumCalls = new int[2]; // index 0=normal, 1=recursive 46 private Color mColor; 47 private Color mFadedColor; 48 private Image mImage; 49 private Image mFadedImage; 50 private HashMap<Integer, ProfileData> mParents; 51 private HashMap<Integer, ProfileData> mChildren; 52 53 // The parents of this method when this method was in a recursive call 54 private HashMap<Integer, ProfileData> mRecursiveParents; 55 56 // The children of this method when this method was in a recursive call 57 private HashMap<Integer, ProfileData> mRecursiveChildren; 58 59 private ProfileNode[] mProfileNodes; 60 private int mX; 61 private int mY; 62 private double mWeight; 63 MethodData(int id, String className)64 public MethodData(int id, String className) { 65 mId = id; 66 mClassName = className; 67 mMethodName = null; 68 mSignature = null; 69 mPathname = null; 70 mLineNumber = -1; 71 computeName(); 72 computeProfileName(); 73 } 74 MethodData(int id, String className, String methodName, String signature, String pathname, int lineNumber)75 public MethodData(int id, String className, String methodName, 76 String signature, String pathname, int lineNumber) { 77 mId = id; 78 mClassName = className; 79 mMethodName = methodName; 80 mSignature = signature; 81 mPathname = pathname; 82 mLineNumber = lineNumber; 83 computeName(); 84 computeProfileName(); 85 } 86 addWeight(int x, int y, double weight)87 public double addWeight(int x, int y, double weight) { 88 if (mX == x && mY == y) 89 mWeight += weight; 90 else { 91 mX = x; 92 mY = y; 93 mWeight = weight; 94 } 95 return mWeight; 96 } 97 clearWeight()98 public void clearWeight() { 99 mWeight = 0; 100 } 101 getRank()102 public int getRank() { 103 return mRank; 104 } 105 setRank(int rank)106 public void setRank(int rank) { 107 mRank = rank; 108 computeProfileName(); 109 } 110 addElapsedExclusive(long cpuTime, long realTime)111 public void addElapsedExclusive(long cpuTime, long realTime) { 112 mElapsedExclusiveCpuTime += cpuTime; 113 mElapsedExclusiveRealTime += realTime; 114 } 115 addElapsedInclusive(long cpuTime, long realTime, boolean isRecursive, Call parent)116 public void addElapsedInclusive(long cpuTime, long realTime, 117 boolean isRecursive, Call parent) { 118 if (isRecursive == false) { 119 mElapsedInclusiveCpuTime += cpuTime; 120 mElapsedInclusiveRealTime += realTime; 121 mNumCalls[0] += 1; 122 } else { 123 mNumCalls[1] += 1; 124 } 125 126 if (parent == null) 127 return; 128 129 // Find the child method in the parent 130 MethodData parentMethod = parent.getMethodData(); 131 if (parent.isRecursive()) { 132 parentMethod.mRecursiveChildren = updateInclusive(cpuTime, realTime, 133 parentMethod, this, false, 134 parentMethod.mRecursiveChildren); 135 } else { 136 parentMethod.mChildren = updateInclusive(cpuTime, realTime, 137 parentMethod, this, false, parentMethod.mChildren); 138 } 139 140 // Find the parent method in the child 141 if (isRecursive) { 142 mRecursiveParents = updateInclusive(cpuTime, realTime, this, parentMethod, true, 143 mRecursiveParents); 144 } else { 145 mParents = updateInclusive(cpuTime, realTime, this, parentMethod, true, 146 mParents); 147 } 148 } 149 updateInclusive(long cpuTime, long realTime, MethodData contextMethod, MethodData elementMethod, boolean elementIsParent, HashMap<Integer, ProfileData> map)150 private HashMap<Integer, ProfileData> updateInclusive(long cpuTime, long realTime, 151 MethodData contextMethod, MethodData elementMethod, 152 boolean elementIsParent, HashMap<Integer, ProfileData> map) { 153 if (map == null) { 154 map = new HashMap<Integer, ProfileData>(4); 155 } else { 156 ProfileData profileData = map.get(elementMethod.mId); 157 if (profileData != null) { 158 profileData.addElapsedInclusive(cpuTime, realTime); 159 return map; 160 } 161 } 162 163 ProfileData elementData = new ProfileData(contextMethod, 164 elementMethod, elementIsParent); 165 elementData.setElapsedInclusive(cpuTime, realTime); 166 elementData.setNumCalls(1); 167 map.put(elementMethod.mId, elementData); 168 return map; 169 } 170 analyzeData(TimeBase timeBase)171 public void analyzeData(TimeBase timeBase) { 172 // Sort the parents and children into decreasing inclusive time 173 ProfileData[] sortedParents; 174 ProfileData[] sortedChildren; 175 ProfileData[] sortedRecursiveParents; 176 ProfileData[] sortedRecursiveChildren; 177 178 sortedParents = sortProfileData(mParents, timeBase); 179 sortedChildren = sortProfileData(mChildren, timeBase); 180 sortedRecursiveParents = sortProfileData(mRecursiveParents, timeBase); 181 sortedRecursiveChildren = sortProfileData(mRecursiveChildren, timeBase); 182 183 // Add "self" time to the top of the sorted children 184 sortedChildren = addSelf(sortedChildren); 185 186 // Create the ProfileNode objects that we need 187 ArrayList<ProfileNode> nodes = new ArrayList<ProfileNode>(); 188 ProfileNode profileNode; 189 if (mParents != null) { 190 profileNode = new ProfileNode("Parents", this, sortedParents, 191 true, false); 192 nodes.add(profileNode); 193 } 194 if (mChildren != null) { 195 profileNode = new ProfileNode("Children", this, sortedChildren, 196 false, false); 197 nodes.add(profileNode); 198 } 199 if (mRecursiveParents!= null) { 200 profileNode = new ProfileNode("Parents while recursive", this, 201 sortedRecursiveParents, true, true); 202 nodes.add(profileNode); 203 } 204 if (mRecursiveChildren != null) { 205 profileNode = new ProfileNode("Children while recursive", this, 206 sortedRecursiveChildren, false, true); 207 nodes.add(profileNode); 208 } 209 mProfileNodes = nodes.toArray(new ProfileNode[nodes.size()]); 210 } 211 212 // Create and return a ProfileData[] array that is a sorted copy 213 // of the given HashMap values. sortProfileData(HashMap<Integer, ProfileData> map, final TimeBase timeBase)214 private ProfileData[] sortProfileData(HashMap<Integer, ProfileData> map, 215 final TimeBase timeBase) { 216 if (map == null) 217 return null; 218 219 // Convert the hash values to an array of ProfileData 220 Collection<ProfileData> values = map.values(); 221 ProfileData[] sorted = values.toArray(new ProfileData[values.size()]); 222 223 // Sort the array by elapsed inclusive time 224 Arrays.sort(sorted, new Comparator<ProfileData>() { 225 @Override 226 public int compare(ProfileData pd1, ProfileData pd2) { 227 if (timeBase.getElapsedInclusiveTime(pd2) > timeBase.getElapsedInclusiveTime(pd1)) 228 return 1; 229 if (timeBase.getElapsedInclusiveTime(pd2) < timeBase.getElapsedInclusiveTime(pd1)) 230 return -1; 231 return 0; 232 } 233 }); 234 return sorted; 235 } 236 addSelf(ProfileData[] children)237 private ProfileData[] addSelf(ProfileData[] children) { 238 ProfileData[] pdata; 239 if (children == null) { 240 pdata = new ProfileData[1]; 241 } else { 242 pdata = new ProfileData[children.length + 1]; 243 System.arraycopy(children, 0, pdata, 1, children.length); 244 } 245 pdata[0] = new ProfileSelf(this); 246 return pdata; 247 } 248 addTopExclusive(long cpuTime, long realTime)249 public void addTopExclusive(long cpuTime, long realTime) { 250 mTopExclusiveCpuTime += cpuTime; 251 mTopExclusiveRealTime += realTime; 252 } 253 getTopExclusiveCpuTime()254 public long getTopExclusiveCpuTime() { 255 return mTopExclusiveCpuTime; 256 } 257 getTopExclusiveRealTime()258 public long getTopExclusiveRealTime() { 259 return mTopExclusiveRealTime; 260 } 261 getId()262 public int getId() { 263 return mId; 264 } 265 computeName()266 private void computeName() { 267 if (mMethodName == null) { 268 mName = mClassName; 269 return; 270 } 271 272 StringBuilder sb = new StringBuilder(); 273 sb.append(mClassName); 274 sb.append("."); //$NON-NLS-1$ 275 sb.append(mMethodName); 276 sb.append(" "); //$NON-NLS-1$ 277 sb.append(mSignature); 278 mName = sb.toString(); 279 } 280 getName()281 public String getName() { 282 return mName; 283 } 284 getClassName()285 public String getClassName() { 286 return mClassName; 287 } 288 getMethodName()289 public String getMethodName() { 290 return mMethodName; 291 } 292 getProfileName()293 public String getProfileName() { 294 return mProfileName; 295 } 296 getSignature()297 public String getSignature() { 298 return mSignature; 299 } 300 computeProfileName()301 public void computeProfileName() { 302 if (mRank == -1) { 303 mProfileName = mName; 304 return; 305 } 306 307 StringBuilder sb = new StringBuilder(); 308 sb.append(mRank); 309 sb.append(" "); //$NON-NLS-1$ 310 sb.append(getName()); 311 mProfileName = sb.toString(); 312 } 313 getCalls()314 public String getCalls() { 315 return String.format("%d+%d", mNumCalls[0], mNumCalls[1]); 316 } 317 getTotalCalls()318 public int getTotalCalls() { 319 return mNumCalls[0] + mNumCalls[1]; 320 } 321 getColor()322 public Color getColor() { 323 return mColor; 324 } 325 setColor(Color color)326 public void setColor(Color color) { 327 mColor = color; 328 } 329 setImage(Image image)330 public void setImage(Image image) { 331 mImage = image; 332 } 333 getImage()334 public Image getImage() { 335 return mImage; 336 } 337 338 @Override toString()339 public String toString() { 340 return getName(); 341 } 342 getElapsedExclusiveCpuTime()343 public long getElapsedExclusiveCpuTime() { 344 return mElapsedExclusiveCpuTime; 345 } 346 getElapsedExclusiveRealTime()347 public long getElapsedExclusiveRealTime() { 348 return mElapsedExclusiveRealTime; 349 } 350 getElapsedInclusiveCpuTime()351 public long getElapsedInclusiveCpuTime() { 352 return mElapsedInclusiveCpuTime; 353 } 354 getElapsedInclusiveRealTime()355 public long getElapsedInclusiveRealTime() { 356 return mElapsedInclusiveRealTime; 357 } 358 setFadedColor(Color fadedColor)359 public void setFadedColor(Color fadedColor) { 360 mFadedColor = fadedColor; 361 } 362 getFadedColor()363 public Color getFadedColor() { 364 return mFadedColor; 365 } 366 setFadedImage(Image fadedImage)367 public void setFadedImage(Image fadedImage) { 368 mFadedImage = fadedImage; 369 } 370 getFadedImage()371 public Image getFadedImage() { 372 return mFadedImage; 373 } 374 setPathname(String pathname)375 public void setPathname(String pathname) { 376 mPathname = pathname; 377 } 378 getPathname()379 public String getPathname() { 380 return mPathname; 381 } 382 setLineNumber(int lineNumber)383 public void setLineNumber(int lineNumber) { 384 mLineNumber = lineNumber; 385 } 386 getLineNumber()387 public int getLineNumber() { 388 return mLineNumber; 389 } 390 getProfileNodes()391 public ProfileNode[] getProfileNodes() { 392 return mProfileNodes; 393 } 394 395 public static class Sorter implements Comparator<MethodData> { 396 @Override compare(MethodData md1, MethodData md2)397 public int compare(MethodData md1, MethodData md2) { 398 if (mColumn == Column.BY_NAME) { 399 int result = md1.getName().compareTo(md2.getName()); 400 return (mDirection == Direction.INCREASING) ? result : -result; 401 } 402 if (mColumn == Column.BY_INCLUSIVE_CPU_TIME) { 403 if (md2.getElapsedInclusiveCpuTime() > md1.getElapsedInclusiveCpuTime()) 404 return (mDirection == Direction.INCREASING) ? -1 : 1; 405 if (md2.getElapsedInclusiveCpuTime() < md1.getElapsedInclusiveCpuTime()) 406 return (mDirection == Direction.INCREASING) ? 1 : -1; 407 return md1.getName().compareTo(md2.getName()); 408 } 409 if (mColumn == Column.BY_EXCLUSIVE_CPU_TIME) { 410 if (md2.getElapsedExclusiveCpuTime() > md1.getElapsedExclusiveCpuTime()) 411 return (mDirection == Direction.INCREASING) ? -1 : 1; 412 if (md2.getElapsedExclusiveCpuTime() < md1.getElapsedExclusiveCpuTime()) 413 return (mDirection == Direction.INCREASING) ? 1 : -1; 414 return md1.getName().compareTo(md2.getName()); 415 } 416 if (mColumn == Column.BY_INCLUSIVE_REAL_TIME) { 417 if (md2.getElapsedInclusiveRealTime() > md1.getElapsedInclusiveRealTime()) 418 return (mDirection == Direction.INCREASING) ? -1 : 1; 419 if (md2.getElapsedInclusiveRealTime() < md1.getElapsedInclusiveRealTime()) 420 return (mDirection == Direction.INCREASING) ? 1 : -1; 421 return md1.getName().compareTo(md2.getName()); 422 } 423 if (mColumn == Column.BY_EXCLUSIVE_REAL_TIME) { 424 if (md2.getElapsedExclusiveRealTime() > md1.getElapsedExclusiveRealTime()) 425 return (mDirection == Direction.INCREASING) ? -1 : 1; 426 if (md2.getElapsedExclusiveRealTime() < md1.getElapsedExclusiveRealTime()) 427 return (mDirection == Direction.INCREASING) ? 1 : -1; 428 return md1.getName().compareTo(md2.getName()); 429 } 430 if (mColumn == Column.BY_CALLS) { 431 int result = md1.getTotalCalls() - md2.getTotalCalls(); 432 if (result == 0) 433 return md1.getName().compareTo(md2.getName()); 434 return (mDirection == Direction.INCREASING) ? result : -result; 435 } 436 if (mColumn == Column.BY_CPU_TIME_PER_CALL) { 437 double time1 = md1.getElapsedInclusiveCpuTime(); 438 time1 = time1 / md1.getTotalCalls(); 439 double time2 = md2.getElapsedInclusiveCpuTime(); 440 time2 = time2 / md2.getTotalCalls(); 441 double diff = time1 - time2; 442 int result = 0; 443 if (diff < 0) 444 result = -1; 445 else if (diff > 0) 446 result = 1; 447 if (result == 0) 448 return md1.getName().compareTo(md2.getName()); 449 return (mDirection == Direction.INCREASING) ? result : -result; 450 } 451 if (mColumn == Column.BY_REAL_TIME_PER_CALL) { 452 double time1 = md1.getElapsedInclusiveRealTime(); 453 time1 = time1 / md1.getTotalCalls(); 454 double time2 = md2.getElapsedInclusiveRealTime(); 455 time2 = time2 / md2.getTotalCalls(); 456 double diff = time1 - time2; 457 int result = 0; 458 if (diff < 0) 459 result = -1; 460 else if (diff > 0) 461 result = 1; 462 if (result == 0) 463 return md1.getName().compareTo(md2.getName()); 464 return (mDirection == Direction.INCREASING) ? result : -result; 465 } 466 return 0; 467 } 468 setColumn(Column column)469 public void setColumn(Column column) { 470 // If the sort column specified is the same as last time, 471 // then reverse the sort order. 472 if (mColumn == column) { 473 // Reverse the sort order 474 if (mDirection == Direction.INCREASING) 475 mDirection = Direction.DECREASING; 476 else 477 mDirection = Direction.INCREASING; 478 } else { 479 // Sort names into increasing order, data into decreasing order. 480 if (column == Column.BY_NAME) 481 mDirection = Direction.INCREASING; 482 else 483 mDirection = Direction.DECREASING; 484 } 485 mColumn = column; 486 } 487 getColumn()488 public Column getColumn() { 489 return mColumn; 490 } 491 setDirection(Direction direction)492 public void setDirection(Direction direction) { 493 mDirection = direction; 494 } 495 getDirection()496 public Direction getDirection() { 497 return mDirection; 498 } 499 500 public static enum Column { 501 BY_NAME, BY_EXCLUSIVE_CPU_TIME, BY_EXCLUSIVE_REAL_TIME, 502 BY_INCLUSIVE_CPU_TIME, BY_INCLUSIVE_REAL_TIME, BY_CALLS, 503 BY_REAL_TIME_PER_CALL, BY_CPU_TIME_PER_CALL, 504 }; 505 506 public static enum Direction { 507 INCREASING, DECREASING 508 }; 509 510 private Column mColumn; 511 private Direction mDirection; 512 } 513 } 514