--- /dev/null
+/*
+ * 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 java.io.InputStream;
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.TreeItem;
+
+class ProfileProvider implements ITreeContentProvider {
+
+ private MethodData[] mRoots;
+ private SelectionAdapter mListener;
+ private TreeViewer mTreeViewer;
+ private TraceReader mReader;
+ private Image mSortUp;
+ private Image mSortDown;
+ private String mColumnNames[] = { "Name", "Incl %", "Inclusive", "Excl %",
+ "Exclusive", "Calls+Recur\nCalls/Total", "Time/Call" };
+ private int mColumnWidths[] = { 370, 70, 70, 70, 70, 90, 70 };
+ private int mColumnAlignments[] = { SWT.LEFT, SWT.RIGHT, SWT.RIGHT,
+ SWT.RIGHT, SWT.RIGHT, SWT.CENTER, SWT.RIGHT };
+ private static final int COL_NAME = 0;
+ private static final int COL_INCLUSIVE_PER = 1;
+ private static final int COL_INCLUSIVE = 2;
+ private static final int COL_EXCLUSIVE_PER = 3;
+ private static final int COL_EXCLUSIVE = 4;
+ private static final int COL_CALLS = 5;
+ private static final int COL_TIME_PER_CALL = 6;
+ private long mTotalTime;
+ private Pattern mUppercase;
+ private int mPrevMatchIndex = -1;
+
+ public ProfileProvider(TraceReader reader) {
+ mRoots = reader.getMethods();
+ mReader = reader;
+ mTotalTime = reader.getEndTime();
+ Display display = Display.getCurrent();
+ InputStream in = getClass().getClassLoader().getResourceAsStream(
+ "icons/sort_up.png");
+ mSortUp = new Image(display, in);
+ in = getClass().getClassLoader().getResourceAsStream(
+ "icons/sort_down.png");
+ mSortDown = new Image(display, in);
+ mUppercase = Pattern.compile("[A-Z]");
+ }
+
+ private MethodData doMatchName(String name, int startIndex) {
+ // Check if the given "name" has any uppercase letters
+ boolean hasUpper = mUppercase.matcher(name).matches();
+ for (int ii = startIndex; ii < mRoots.length; ++ii) {
+ MethodData md = mRoots[ii];
+ String fullName = md.getName();
+ // If there were no upper case letters in the given name,
+ // then ignore case when matching.
+ if (!hasUpper)
+ fullName = fullName.toLowerCase();
+ if (fullName.indexOf(name) != -1) {
+ mPrevMatchIndex = ii;
+ return md;
+ }
+ }
+ mPrevMatchIndex = -1;
+ return null;
+ }
+
+ public MethodData findMatchingName(String name) {
+ return doMatchName(name, 0);
+ }
+
+ public MethodData findNextMatchingName(String name) {
+ return doMatchName(name, mPrevMatchIndex + 1);
+ }
+
+ public MethodData findMatchingTreeItem(TreeItem item) {
+ if (item == null)
+ return null;
+ String text = item.getText();
+ if (Character.isDigit(text.charAt(0)) == false)
+ return null;
+ int spaceIndex = text.indexOf(' ');
+ String numstr = text.substring(0, spaceIndex);
+ int rank = Integer.valueOf(numstr);
+ for (MethodData md : mRoots) {
+ if (md.getRank() == rank)
+ return md;
+ }
+ return null;
+ }
+
+ public void setTreeViewer(TreeViewer treeViewer) {
+ mTreeViewer = treeViewer;
+ }
+
+ public String[] getColumnNames() {
+ return mColumnNames;
+ }
+
+ public int[] getColumnWidths() {
+ return mColumnWidths;
+ }
+
+ public int[] getColumnAlignments() {
+ return mColumnAlignments;
+ }
+
+ public Object[] getChildren(Object element) {
+ if (element instanceof MethodData) {
+ MethodData md = (MethodData) element;
+ return md.getProfileNodes();
+ }
+ if (element instanceof ProfileNode) {
+ ProfileNode pn = (ProfileNode) element;
+ return pn.getChildren();
+ }
+ return new Object[0];
+ }
+
+ public Object getParent(Object element) {
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ if (element instanceof MethodData)
+ return true;
+ if (element instanceof ProfileNode)
+ return true;
+ return false;
+ }
+
+ public Object[] getElements(Object element) {
+ return mRoots;
+ }
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
+ }
+
+ public Object getRoot() {
+ return "root";
+ }
+
+ public SelectionAdapter getColumnListener() {
+ if (mListener == null)
+ mListener = new ColumnListener();
+ return mListener;
+ }
+
+ public LabelProvider getLabelProvider() {
+ return new ProfileLabelProvider();
+ }
+
+ class ProfileLabelProvider extends LabelProvider implements
+ ITableLabelProvider, IColorProvider {
+ Color colorRed;
+ Color colorParentsBack;
+ Color colorChildrenBack;
+ TraceUnits traceUnits;
+
+ public ProfileLabelProvider() {
+ Display display = Display.getCurrent();
+ colorRed = display.getSystemColor(SWT.COLOR_RED);
+ colorParentsBack = new Color(display, 230, 230, 255); // blue
+ colorChildrenBack = new Color(display, 255, 255, 210); // yellow
+ traceUnits = mReader.getTraceUnits();
+ }
+
+ public String getColumnText(Object element, int col) {
+ if (element instanceof MethodData) {
+ MethodData md = (MethodData) element;
+ if (col == COL_NAME)
+ return md.getProfileName();
+ if (col == COL_EXCLUSIVE) {
+ double val = md.getElapsedExclusive();
+ val = traceUnits.getScaledValue(val);
+ return String.format("%.3f", val);
+ }
+ if (col == COL_EXCLUSIVE_PER) {
+ double val = md.getElapsedExclusive();
+ double per = val * 100.0 / mTotalTime;
+ return String.format("%.1f%%", per);
+ }
+ if (col == COL_INCLUSIVE) {
+ double val = md.getElapsedInclusive();
+ val = traceUnits.getScaledValue(val);
+ return String.format("%.3f", val);
+ }
+ if (col == COL_INCLUSIVE_PER) {
+ double val = md.getElapsedInclusive();
+ double per = val * 100.0 / mTotalTime;
+ return String.format("%.1f%%", per);
+ }
+ if (col == COL_CALLS)
+ return md.getCalls();
+ if (col == COL_TIME_PER_CALL) {
+ int numCalls = md.getTotalCalls();
+ double val = md.getElapsedInclusive();
+ val = val / numCalls;
+ val = traceUnits.getScaledValue(val);
+ return String.format("%.3f", val);
+ }
+ } else if (element instanceof ProfileSelf) {
+ ProfileSelf ps = (ProfileSelf) element;
+ if (col == COL_NAME)
+ return ps.getProfileName();
+ if (col == COL_INCLUSIVE) {
+ double val = ps.getElapsedInclusive();
+ val = traceUnits.getScaledValue(val);
+ return String.format("%.3f", val);
+ }
+ if (col == COL_INCLUSIVE_PER) {
+ double total;
+ double val = ps.getElapsedInclusive();
+ MethodData context = ps.getContext();
+ total = context.getElapsedInclusive();
+ double per = val * 100.0 / total;
+ return String.format("%.1f%%", per);
+ }
+ return "";
+ } else if (element instanceof ProfileData) {
+ ProfileData pd = (ProfileData) element;
+ if (col == COL_NAME)
+ return pd.getProfileName();
+ if (col == COL_INCLUSIVE) {
+ double val = pd.getElapsedInclusive();
+ val = traceUnits.getScaledValue(val);
+ return String.format("%.3f", val);
+ }
+ if (col == COL_INCLUSIVE_PER) {
+ double total;
+ double val = pd.getElapsedInclusive();
+ MethodData context = pd.getContext();
+ total = context.getElapsedInclusive();
+ double per = val * 100.0 / total;
+ return String.format("%.1f%%", per);
+ }
+ if (col == COL_CALLS)
+ return pd.getNumCalls();
+ return "";
+ } else if (element instanceof ProfileNode) {
+ ProfileNode pn = (ProfileNode) element;
+ if (col == COL_NAME)
+ return pn.getLabel();
+ return "";
+ }
+ return "col" + col;
+ }
+
+ public Image getColumnImage(Object element, int col) {
+ if (col != COL_NAME)
+ return null;
+ if (element instanceof MethodData) {
+ MethodData md = (MethodData) element;
+ return md.getImage();
+ }
+ if (element instanceof ProfileData) {
+ ProfileData pd = (ProfileData) element;
+ MethodData md = pd.getMethodData();
+ return md.getImage();
+ }
+ return null;
+ }
+
+ public Color getForeground(Object element) {
+ return null;
+ }
+
+ public Color getBackground(Object element) {
+ if (element instanceof ProfileData) {
+ ProfileData pd = (ProfileData) element;
+ if (pd.isParent())
+ return colorParentsBack;
+ return colorChildrenBack;
+ }
+ if (element instanceof ProfileNode) {
+ ProfileNode pn = (ProfileNode) element;
+ if (pn.isParent())
+ return colorParentsBack;
+ return colorChildrenBack;
+ }
+ return null;
+ }
+ }
+
+ class ColumnListener extends SelectionAdapter {
+ MethodData.Sorter sorter = new MethodData.Sorter();
+
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ TreeColumn column = (TreeColumn) event.widget;
+ String name = column.getText();
+ Tree tree = column.getParent();
+ tree.setRedraw(false);
+ TreeColumn[] columns = tree.getColumns();
+ for (TreeColumn col : columns) {
+ col.setImage(null);
+ }
+ if (name == mColumnNames[COL_NAME]) {
+ // Sort names alphabetically
+ sorter.setColumn(MethodData.Sorter.Column.BY_NAME);
+ Arrays.sort(mRoots, sorter);
+ } else if (name == mColumnNames[COL_EXCLUSIVE]) {
+ sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE);
+ Arrays.sort(mRoots, sorter);
+ } else if (name == mColumnNames[COL_EXCLUSIVE_PER]) {
+ sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE);
+ Arrays.sort(mRoots, sorter);
+ } else if (name == mColumnNames[COL_INCLUSIVE]) {
+ sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE);
+ Arrays.sort(mRoots, sorter);
+ } else if (name == mColumnNames[COL_INCLUSIVE_PER]) {
+ sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE);
+ Arrays.sort(mRoots, sorter);
+ } else if (name == mColumnNames[COL_CALLS]) {
+ sorter.setColumn(MethodData.Sorter.Column.BY_CALLS);
+ Arrays.sort(mRoots, sorter);
+ } else if (name == mColumnNames[COL_TIME_PER_CALL]) {
+ sorter.setColumn(MethodData.Sorter.Column.BY_TIME_PER_CALL);
+ Arrays.sort(mRoots, sorter);
+ }
+ MethodData.Sorter.Direction direction = sorter.getDirection();
+ if (direction == MethodData.Sorter.Direction.INCREASING)
+ column.setImage(mSortDown);
+ else
+ column.setImage(mSortUp);
+ tree.setRedraw(true);
+ mTreeViewer.refresh();
+ }
+ }
+}