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