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 public int compare(ProfileData pd1, ProfileData pd2) { 226 if (timeBase.getElapsedInclusiveTime(pd2) > timeBase.getElapsedInclusiveTime(pd1)) 227 return 1; 228 if (timeBase.getElapsedInclusiveTime(pd2) < timeBase.getElapsedInclusiveTime(pd1)) 229 return -1; 230 return 0; 231 } 232 }); 233 return sorted; 234 } 235 addSelf(ProfileData[] children)236 private ProfileData[] addSelf(ProfileData[] children) { 237 ProfileData[] pdata; 238 if (children == null) { 239 pdata = new ProfileData[1]; 240 } else { 241 pdata = new ProfileData[children.length + 1]; 242 System.arraycopy(children, 0, pdata, 1, children.length); 243 } 244 pdata[0] = new ProfileSelf(this); 245 return pdata; 246 } 247 addTopExclusive(long cpuTime, long realTime)248 public void addTopExclusive(long cpuTime, long realTime) { 249 mTopExclusiveCpuTime += cpuTime; 250 mTopExclusiveRealTime += realTime; 251 } 252 getTopExclusiveCpuTime()253 public long getTopExclusiveCpuTime() { 254 return mTopExclusiveCpuTime; 255 } 256 getTopExclusiveRealTime()257 public long getTopExclusiveRealTime() { 258 return mTopExclusiveRealTime; 259 } 260 getId()261 public int getId() { 262 return mId; 263 } 264 computeName()265 private void computeName() { 266 if (mMethodName == null) { 267 mName = mClassName; 268 return; 269 } 270 271 StringBuilder sb = new StringBuilder(); 272 sb.append(mClassName); 273 sb.append("."); //$NON-NLS-1$ 274 sb.append(mMethodName); 275 sb.append(" "); //$NON-NLS-1$ 276 sb.append(mSignature); 277 mName = sb.toString(); 278 } 279 getName()280 public String getName() { 281 return mName; 282 } 283 getClassName()284 public String getClassName() { 285 return mClassName; 286 } 287 getMethodName()288 public String getMethodName() { 289 return mMethodName; 290 } 291 getProfileName()292 public String getProfileName() { 293 return mProfileName; 294 } 295 getSignature()296 public String getSignature() { 297 return mSignature; 298 } 299 computeProfileName()300 public void computeProfileName() { 301 if (mRank == -1) { 302 mProfileName = mName; 303 return; 304 } 305 306 StringBuilder sb = new StringBuilder(); 307 sb.append(mRank); 308 sb.append(" "); //$NON-NLS-1$ 309 sb.append(getName()); 310 mProfileName = sb.toString(); 311 } 312 getCalls()313 public String getCalls() { 314 return String.format("%d+%d", mNumCalls[0], mNumCalls[1]); 315 } 316 getTotalCalls()317 public int getTotalCalls() { 318 return mNumCalls[0] + mNumCalls[1]; 319 } 320 getColor()321 public Color getColor() { 322 return mColor; 323 } 324 setColor(Color color)325 public void setColor(Color color) { 326 mColor = color; 327 } 328 setImage(Image image)329 public void setImage(Image image) { 330 mImage = image; 331 } 332 getImage()333 public Image getImage() { 334 return mImage; 335 } 336 337 @Override toString()338 public String toString() { 339 return getName(); 340 } 341 getElapsedExclusiveCpuTime()342 public long getElapsedExclusiveCpuTime() { 343 return mElapsedExclusiveCpuTime; 344 } 345 getElapsedExclusiveRealTime()346 public long getElapsedExclusiveRealTime() { 347 return mElapsedExclusiveRealTime; 348 } 349 getElapsedInclusiveCpuTime()350 public long getElapsedInclusiveCpuTime() { 351 return mElapsedInclusiveCpuTime; 352 } 353 getElapsedInclusiveRealTime()354 public long getElapsedInclusiveRealTime() { 355 return mElapsedInclusiveRealTime; 356 } 357 setFadedColor(Color fadedColor)358 public void setFadedColor(Color fadedColor) { 359 mFadedColor = fadedColor; 360 } 361 getFadedColor()362 public Color getFadedColor() { 363 return mFadedColor; 364 } 365 setFadedImage(Image fadedImage)366 public void setFadedImage(Image fadedImage) { 367 mFadedImage = fadedImage; 368 } 369 getFadedImage()370 public Image getFadedImage() { 371 return mFadedImage; 372 } 373 setPathname(String pathname)374 public void setPathname(String pathname) { 375 mPathname = pathname; 376 } 377 getPathname()378 public String getPathname() { 379 return mPathname; 380 } 381 setLineNumber(int lineNumber)382 public void setLineNumber(int lineNumber) { 383 mLineNumber = lineNumber; 384 } 385 getLineNumber()386 public int getLineNumber() { 387 return mLineNumber; 388 } 389 getProfileNodes()390 public ProfileNode[] getProfileNodes() { 391 return mProfileNodes; 392 } 393 394 public static class Sorter implements Comparator<MethodData> { compare(MethodData md1, MethodData md2)395 public int compare(MethodData md1, MethodData md2) { 396 if (mColumn == Column.BY_NAME) { 397 int result = md1.getName().compareTo(md2.getName()); 398 return (mDirection == Direction.INCREASING) ? result : -result; 399 } 400 if (mColumn == Column.BY_INCLUSIVE_CPU_TIME) { 401 if (md2.getElapsedInclusiveCpuTime() > md1.getElapsedInclusiveCpuTime()) 402 return (mDirection == Direction.INCREASING) ? -1 : 1; 403 if (md2.getElapsedInclusiveCpuTime() < md1.getElapsedInclusiveCpuTime()) 404 return (mDirection == Direction.INCREASING) ? 1 : -1; 405 return md1.getName().compareTo(md2.getName()); 406 } 407 if (mColumn == Column.BY_EXCLUSIVE_CPU_TIME) { 408 if (md2.getElapsedExclusiveCpuTime() > md1.getElapsedExclusiveCpuTime()) 409 return (mDirection == Direction.INCREASING) ? -1 : 1; 410 if (md2.getElapsedExclusiveCpuTime() < md1.getElapsedExclusiveCpuTime()) 411 return (mDirection == Direction.INCREASING) ? 1 : -1; 412 return md1.getName().compareTo(md2.getName()); 413 } 414 if (mColumn == Column.BY_INCLUSIVE_REAL_TIME) { 415 if (md2.getElapsedInclusiveRealTime() > md1.getElapsedInclusiveRealTime()) 416 return (mDirection == Direction.INCREASING) ? -1 : 1; 417 if (md2.getElapsedInclusiveRealTime() < md1.getElapsedInclusiveRealTime()) 418 return (mDirection == Direction.INCREASING) ? 1 : -1; 419 return md1.getName().compareTo(md2.getName()); 420 } 421 if (mColumn == Column.BY_EXCLUSIVE_REAL_TIME) { 422 if (md2.getElapsedExclusiveRealTime() > md1.getElapsedExclusiveRealTime()) 423 return (mDirection == Direction.INCREASING) ? -1 : 1; 424 if (md2.getElapsedExclusiveRealTime() < md1.getElapsedExclusiveRealTime()) 425 return (mDirection == Direction.INCREASING) ? 1 : -1; 426 return md1.getName().compareTo(md2.getName()); 427 } 428 if (mColumn == Column.BY_CALLS) { 429 int result = md1.getTotalCalls() - md2.getTotalCalls(); 430 if (result == 0) 431 return md1.getName().compareTo(md2.getName()); 432 return (mDirection == Direction.INCREASING) ? result : -result; 433 } 434 if (mColumn == Column.BY_CPU_TIME_PER_CALL) { 435 double time1 = md1.getElapsedInclusiveCpuTime(); 436 time1 = time1 / md1.getTotalCalls(); 437 double time2 = md2.getElapsedInclusiveCpuTime(); 438 time2 = time2 / md2.getTotalCalls(); 439 double diff = time1 - time2; 440 int result = 0; 441 if (diff < 0) 442 result = -1; 443 else if (diff > 0) 444 result = 1; 445 if (result == 0) 446 return md1.getName().compareTo(md2.getName()); 447 return (mDirection == Direction.INCREASING) ? result : -result; 448 } 449 if (mColumn == Column.BY_REAL_TIME_PER_CALL) { 450 double time1 = md1.getElapsedInclusiveRealTime(); 451 time1 = time1 / md1.getTotalCalls(); 452 double time2 = md2.getElapsedInclusiveRealTime(); 453 time2 = time2 / md2.getTotalCalls(); 454 double diff = time1 - time2; 455 int result = 0; 456 if (diff < 0) 457 result = -1; 458 else if (diff > 0) 459 result = 1; 460 if (result == 0) 461 return md1.getName().compareTo(md2.getName()); 462 return (mDirection == Direction.INCREASING) ? result : -result; 463 } 464 return 0; 465 } 466 setColumn(Column column)467 public void setColumn(Column column) { 468 // If the sort column specified is the same as last time, 469 // then reverse the sort order. 470 if (mColumn == column) { 471 // Reverse the sort order 472 if (mDirection == Direction.INCREASING) 473 mDirection = Direction.DECREASING; 474 else 475 mDirection = Direction.INCREASING; 476 } else { 477 // Sort names into increasing order, data into decreasing order. 478 if (column == Column.BY_NAME) 479 mDirection = Direction.INCREASING; 480 else 481 mDirection = Direction.DECREASING; 482 } 483 mColumn = column; 484 } 485 getColumn()486 public Column getColumn() { 487 return mColumn; 488 } 489 setDirection(Direction direction)490 public void setDirection(Direction direction) { 491 mDirection = direction; 492 } 493 getDirection()494 public Direction getDirection() { 495 return mDirection; 496 } 497 498 public static enum Column { 499 BY_NAME, BY_EXCLUSIVE_CPU_TIME, BY_EXCLUSIVE_REAL_TIME, 500 BY_INCLUSIVE_CPU_TIME, BY_INCLUSIVE_REAL_TIME, BY_CALLS, 501 BY_REAL_TIME_PER_CALL, BY_CPU_TIME_PER_CALL, 502 }; 503 504 public static enum Direction { 505 INCREASING, DECREASING 506 }; 507 508 private Column mColumn; 509 private Direction mDirection; 510 } 511 } 512