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 java.io.InputStream; 20 import java.util.Arrays; 21 import java.util.regex.Pattern; 22 23 import org.eclipse.jface.viewers.IColorProvider; 24 import org.eclipse.jface.viewers.ITableLabelProvider; 25 import org.eclipse.jface.viewers.ITreeContentProvider; 26 import org.eclipse.jface.viewers.LabelProvider; 27 import org.eclipse.jface.viewers.TreeViewer; 28 import org.eclipse.jface.viewers.Viewer; 29 import org.eclipse.swt.SWT; 30 import org.eclipse.swt.events.SelectionAdapter; 31 import org.eclipse.swt.events.SelectionEvent; 32 import org.eclipse.swt.graphics.Color; 33 import org.eclipse.swt.graphics.Image; 34 import org.eclipse.swt.widgets.Display; 35 import org.eclipse.swt.widgets.Tree; 36 import org.eclipse.swt.widgets.TreeColumn; 37 import org.eclipse.swt.widgets.TreeItem; 38 39 class ProfileProvider implements ITreeContentProvider { 40 41 private MethodData[] mRoots; 42 private SelectionAdapter mListener; 43 private TreeViewer mTreeViewer; 44 private TraceReader mReader; 45 private Image mSortUp; 46 private Image mSortDown; 47 private String mColumnNames[] = { "Name", "Incl %", "Inclusive", "Excl %", 48 "Exclusive", "Calls+Recur\nCalls/Total", "Time/Call" }; 49 private int mColumnWidths[] = { 370, 70, 70, 70, 70, 90, 70 }; 50 private int mColumnAlignments[] = { SWT.LEFT, SWT.RIGHT, SWT.RIGHT, 51 SWT.RIGHT, SWT.RIGHT, SWT.CENTER, SWT.RIGHT }; 52 private static final int COL_NAME = 0; 53 private static final int COL_INCLUSIVE_PER = 1; 54 private static final int COL_INCLUSIVE = 2; 55 private static final int COL_EXCLUSIVE_PER = 3; 56 private static final int COL_EXCLUSIVE = 4; 57 private static final int COL_CALLS = 5; 58 private static final int COL_TIME_PER_CALL = 6; 59 private long mTotalTime; 60 private Pattern mUppercase; 61 private int mPrevMatchIndex = -1; 62 ProfileProvider(TraceReader reader)63 public ProfileProvider(TraceReader reader) { 64 mRoots = reader.getMethods(); 65 mReader = reader; 66 mTotalTime = reader.getEndTime(); 67 Display display = Display.getCurrent(); 68 InputStream in = getClass().getClassLoader().getResourceAsStream( 69 "icons/sort_up.png"); 70 mSortUp = new Image(display, in); 71 in = getClass().getClassLoader().getResourceAsStream( 72 "icons/sort_down.png"); 73 mSortDown = new Image(display, in); 74 mUppercase = Pattern.compile("[A-Z]"); 75 } 76 doMatchName(String name, int startIndex)77 private MethodData doMatchName(String name, int startIndex) { 78 // Check if the given "name" has any uppercase letters 79 boolean hasUpper = mUppercase.matcher(name).matches(); 80 for (int ii = startIndex; ii < mRoots.length; ++ii) { 81 MethodData md = mRoots[ii]; 82 String fullName = md.getName(); 83 // If there were no upper case letters in the given name, 84 // then ignore case when matching. 85 if (!hasUpper) 86 fullName = fullName.toLowerCase(); 87 if (fullName.indexOf(name) != -1) { 88 mPrevMatchIndex = ii; 89 return md; 90 } 91 } 92 mPrevMatchIndex = -1; 93 return null; 94 } 95 findMatchingName(String name)96 public MethodData findMatchingName(String name) { 97 return doMatchName(name, 0); 98 } 99 findNextMatchingName(String name)100 public MethodData findNextMatchingName(String name) { 101 return doMatchName(name, mPrevMatchIndex + 1); 102 } 103 findMatchingTreeItem(TreeItem item)104 public MethodData findMatchingTreeItem(TreeItem item) { 105 if (item == null) 106 return null; 107 String text = item.getText(); 108 if (Character.isDigit(text.charAt(0)) == false) 109 return null; 110 int spaceIndex = text.indexOf(' '); 111 String numstr = text.substring(0, spaceIndex); 112 int rank = Integer.valueOf(numstr); 113 for (MethodData md : mRoots) { 114 if (md.getRank() == rank) 115 return md; 116 } 117 return null; 118 } 119 setTreeViewer(TreeViewer treeViewer)120 public void setTreeViewer(TreeViewer treeViewer) { 121 mTreeViewer = treeViewer; 122 } 123 getColumnNames()124 public String[] getColumnNames() { 125 return mColumnNames; 126 } 127 getColumnWidths()128 public int[] getColumnWidths() { 129 return mColumnWidths; 130 } 131 getColumnAlignments()132 public int[] getColumnAlignments() { 133 return mColumnAlignments; 134 } 135 getChildren(Object element)136 public Object[] getChildren(Object element) { 137 if (element instanceof MethodData) { 138 MethodData md = (MethodData) element; 139 return md.getProfileNodes(); 140 } 141 if (element instanceof ProfileNode) { 142 ProfileNode pn = (ProfileNode) element; 143 return pn.getChildren(); 144 } 145 return new Object[0]; 146 } 147 getParent(Object element)148 public Object getParent(Object element) { 149 return null; 150 } 151 hasChildren(Object element)152 public boolean hasChildren(Object element) { 153 if (element instanceof MethodData) 154 return true; 155 if (element instanceof ProfileNode) 156 return true; 157 return false; 158 } 159 getElements(Object element)160 public Object[] getElements(Object element) { 161 return mRoots; 162 } 163 dispose()164 public void dispose() { 165 } 166 inputChanged(Viewer arg0, Object arg1, Object arg2)167 public void inputChanged(Viewer arg0, Object arg1, Object arg2) { 168 } 169 getRoot()170 public Object getRoot() { 171 return "root"; 172 } 173 getColumnListener()174 public SelectionAdapter getColumnListener() { 175 if (mListener == null) 176 mListener = new ColumnListener(); 177 return mListener; 178 } 179 getLabelProvider()180 public LabelProvider getLabelProvider() { 181 return new ProfileLabelProvider(); 182 } 183 184 class ProfileLabelProvider extends LabelProvider implements 185 ITableLabelProvider, IColorProvider { 186 Color colorRed; 187 Color colorParentsBack; 188 Color colorChildrenBack; 189 TraceUnits traceUnits; 190 ProfileLabelProvider()191 public ProfileLabelProvider() { 192 Display display = Display.getCurrent(); 193 colorRed = display.getSystemColor(SWT.COLOR_RED); 194 colorParentsBack = new Color(display, 230, 230, 255); // blue 195 colorChildrenBack = new Color(display, 255, 255, 210); // yellow 196 traceUnits = mReader.getTraceUnits(); 197 } 198 getColumnText(Object element, int col)199 public String getColumnText(Object element, int col) { 200 if (element instanceof MethodData) { 201 MethodData md = (MethodData) element; 202 if (col == COL_NAME) 203 return md.getProfileName(); 204 if (col == COL_EXCLUSIVE) { 205 double val = md.getElapsedExclusive(); 206 val = traceUnits.getScaledValue(val); 207 return String.format("%.3f", val); 208 } 209 if (col == COL_EXCLUSIVE_PER) { 210 double val = md.getElapsedExclusive(); 211 double per = val * 100.0 / mTotalTime; 212 return String.format("%.1f%%", per); 213 } 214 if (col == COL_INCLUSIVE) { 215 double val = md.getElapsedInclusive(); 216 val = traceUnits.getScaledValue(val); 217 return String.format("%.3f", val); 218 } 219 if (col == COL_INCLUSIVE_PER) { 220 double val = md.getElapsedInclusive(); 221 double per = val * 100.0 / mTotalTime; 222 return String.format("%.1f%%", per); 223 } 224 if (col == COL_CALLS) 225 return md.getCalls(); 226 if (col == COL_TIME_PER_CALL) { 227 int numCalls = md.getTotalCalls(); 228 double val = md.getElapsedInclusive(); 229 val = val / numCalls; 230 val = traceUnits.getScaledValue(val); 231 return String.format("%.3f", val); 232 } 233 } else if (element instanceof ProfileSelf) { 234 ProfileSelf ps = (ProfileSelf) element; 235 if (col == COL_NAME) 236 return ps.getProfileName(); 237 if (col == COL_INCLUSIVE) { 238 double val = ps.getElapsedInclusive(); 239 val = traceUnits.getScaledValue(val); 240 return String.format("%.3f", val); 241 } 242 if (col == COL_INCLUSIVE_PER) { 243 double total; 244 double val = ps.getElapsedInclusive(); 245 MethodData context = ps.getContext(); 246 total = context.getElapsedInclusive(); 247 double per = val * 100.0 / total; 248 return String.format("%.1f%%", per); 249 } 250 return ""; 251 } else if (element instanceof ProfileData) { 252 ProfileData pd = (ProfileData) element; 253 if (col == COL_NAME) 254 return pd.getProfileName(); 255 if (col == COL_INCLUSIVE) { 256 double val = pd.getElapsedInclusive(); 257 val = traceUnits.getScaledValue(val); 258 return String.format("%.3f", val); 259 } 260 if (col == COL_INCLUSIVE_PER) { 261 double total; 262 double val = pd.getElapsedInclusive(); 263 MethodData context = pd.getContext(); 264 total = context.getElapsedInclusive(); 265 double per = val * 100.0 / total; 266 return String.format("%.1f%%", per); 267 } 268 if (col == COL_CALLS) 269 return pd.getNumCalls(); 270 return ""; 271 } else if (element instanceof ProfileNode) { 272 ProfileNode pn = (ProfileNode) element; 273 if (col == COL_NAME) 274 return pn.getLabel(); 275 return ""; 276 } 277 return "col" + col; 278 } 279 getColumnImage(Object element, int col)280 public Image getColumnImage(Object element, int col) { 281 if (col != COL_NAME) 282 return null; 283 if (element instanceof MethodData) { 284 MethodData md = (MethodData) element; 285 return md.getImage(); 286 } 287 if (element instanceof ProfileData) { 288 ProfileData pd = (ProfileData) element; 289 MethodData md = pd.getMethodData(); 290 return md.getImage(); 291 } 292 return null; 293 } 294 getForeground(Object element)295 public Color getForeground(Object element) { 296 return null; 297 } 298 getBackground(Object element)299 public Color getBackground(Object element) { 300 if (element instanceof ProfileData) { 301 ProfileData pd = (ProfileData) element; 302 if (pd.isParent()) 303 return colorParentsBack; 304 return colorChildrenBack; 305 } 306 if (element instanceof ProfileNode) { 307 ProfileNode pn = (ProfileNode) element; 308 if (pn.isParent()) 309 return colorParentsBack; 310 return colorChildrenBack; 311 } 312 return null; 313 } 314 } 315 316 class ColumnListener extends SelectionAdapter { 317 MethodData.Sorter sorter = new MethodData.Sorter(); 318 319 @Override widgetSelected(SelectionEvent event)320 public void widgetSelected(SelectionEvent event) { 321 TreeColumn column = (TreeColumn) event.widget; 322 String name = column.getText(); 323 Tree tree = column.getParent(); 324 tree.setRedraw(false); 325 TreeColumn[] columns = tree.getColumns(); 326 for (TreeColumn col : columns) { 327 col.setImage(null); 328 } 329 if (name == mColumnNames[COL_NAME]) { 330 // Sort names alphabetically 331 sorter.setColumn(MethodData.Sorter.Column.BY_NAME); 332 Arrays.sort(mRoots, sorter); 333 } else if (name == mColumnNames[COL_EXCLUSIVE]) { 334 sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE); 335 Arrays.sort(mRoots, sorter); 336 } else if (name == mColumnNames[COL_EXCLUSIVE_PER]) { 337 sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE); 338 Arrays.sort(mRoots, sorter); 339 } else if (name == mColumnNames[COL_INCLUSIVE]) { 340 sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE); 341 Arrays.sort(mRoots, sorter); 342 } else if (name == mColumnNames[COL_INCLUSIVE_PER]) { 343 sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE); 344 Arrays.sort(mRoots, sorter); 345 } else if (name == mColumnNames[COL_CALLS]) { 346 sorter.setColumn(MethodData.Sorter.Column.BY_CALLS); 347 Arrays.sort(mRoots, sorter); 348 } else if (name == mColumnNames[COL_TIME_PER_CALL]) { 349 sorter.setColumn(MethodData.Sorter.Column.BY_TIME_PER_CALL); 350 Arrays.sort(mRoots, sorter); 351 } 352 MethodData.Sorter.Direction direction = sorter.getDirection(); 353 if (direction == MethodData.Sorter.Direction.INCREASING) 354 column.setImage(mSortDown); 355 else 356 column.setImage(mSortUp); 357 tree.setRedraw(true); 358 mTreeViewer.refresh(); 359 } 360 } 361 } 362