/* * 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 com.android.utils.SdkUtils; 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; import java.io.InputStream; import java.util.Arrays; 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 Cpu Time %", "Incl Cpu Time", "Excl Cpu Time %", "Excl Cpu Time", "Incl Real Time %", "Incl Real Time", "Excl Real Time %", "Excl Real Time", "Calls+Recur\nCalls/Total", "Cpu Time/Call", "Real Time/Call" }; private int mColumnWidths[] = { 370, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }; private int mColumnAlignments[] = { SWT.LEFT, SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, SWT.RIGHT, SWT.CENTER, SWT.RIGHT, SWT.RIGHT }; private static final int COL_NAME = 0; private static final int COL_INCLUSIVE_CPU_TIME_PER = 1; private static final int COL_INCLUSIVE_CPU_TIME = 2; private static final int COL_EXCLUSIVE_CPU_TIME_PER = 3; private static final int COL_EXCLUSIVE_CPU_TIME = 4; private static final int COL_INCLUSIVE_REAL_TIME_PER = 5; private static final int COL_INCLUSIVE_REAL_TIME = 6; private static final int COL_EXCLUSIVE_REAL_TIME_PER = 7; private static final int COL_EXCLUSIVE_REAL_TIME = 8; private static final int COL_CALLS = 9; private static final int COL_CPU_TIME_PER_CALL = 10; private static final int COL_REAL_TIME_PER_CALL = 11; private long mTotalCpuTime; private long mTotalRealTime; private int mPrevMatchIndex = -1; public ProfileProvider(TraceReader reader) { mRoots = reader.getMethods(); mReader = reader; mTotalCpuTime = reader.getTotalCpuTime(); mTotalRealTime = reader.getTotalRealTime(); 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); } private MethodData doMatchName(String name, int startIndex) { // Check if the given "name" has any uppercase letters boolean hasUpper = SdkUtils.hasUpperCaseCharacter(name); 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() { int[] widths = Arrays.copyOf(mColumnWidths, mColumnWidths.length); if (!mReader.haveCpuTime()) { widths[COL_EXCLUSIVE_CPU_TIME] = 0; widths[COL_EXCLUSIVE_CPU_TIME_PER] = 0; widths[COL_INCLUSIVE_CPU_TIME] = 0; widths[COL_INCLUSIVE_CPU_TIME_PER] = 0; widths[COL_CPU_TIME_PER_CALL] = 0; } if (!mReader.haveRealTime()) { widths[COL_EXCLUSIVE_REAL_TIME] = 0; widths[COL_EXCLUSIVE_REAL_TIME_PER] = 0; widths[COL_INCLUSIVE_REAL_TIME] = 0; widths[COL_INCLUSIVE_REAL_TIME_PER] = 0; widths[COL_REAL_TIME_PER_CALL] = 0; } return widths; } public int[] getColumnAlignments() { return mColumnAlignments; } @Override 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]; } @Override public Object getParent(Object element) { return null; } @Override public boolean hasChildren(Object element) { if (element instanceof MethodData) return true; if (element instanceof ProfileNode) return true; return false; } @Override public Object[] getElements(Object element) { return mRoots; } @Override public void dispose() { } @Override 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(); } @Override 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_CPU_TIME) { double val = md.getElapsedExclusiveCpuTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } if (col == COL_EXCLUSIVE_CPU_TIME_PER) { double val = md.getElapsedExclusiveCpuTime(); double per = val * 100.0 / mTotalCpuTime; return String.format("%.1f%%", per); } if (col == COL_INCLUSIVE_CPU_TIME) { double val = md.getElapsedInclusiveCpuTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } if (col == COL_INCLUSIVE_CPU_TIME_PER) { double val = md.getElapsedInclusiveCpuTime(); double per = val * 100.0 / mTotalCpuTime; return String.format("%.1f%%", per); } if (col == COL_EXCLUSIVE_REAL_TIME) { double val = md.getElapsedExclusiveRealTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } if (col == COL_EXCLUSIVE_REAL_TIME_PER) { double val = md.getElapsedExclusiveRealTime(); double per = val * 100.0 / mTotalRealTime; return String.format("%.1f%%", per); } if (col == COL_INCLUSIVE_REAL_TIME) { double val = md.getElapsedInclusiveRealTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } if (col == COL_INCLUSIVE_REAL_TIME_PER) { double val = md.getElapsedInclusiveRealTime(); double per = val * 100.0 / mTotalRealTime; return String.format("%.1f%%", per); } if (col == COL_CALLS) return md.getCalls(); if (col == COL_CPU_TIME_PER_CALL) { int numCalls = md.getTotalCalls(); double val = md.getElapsedInclusiveCpuTime(); val = val / numCalls; val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } if (col == COL_REAL_TIME_PER_CALL) { int numCalls = md.getTotalCalls(); double val = md.getElapsedInclusiveRealTime(); 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_CPU_TIME) { double val = ps.getElapsedInclusiveCpuTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } if (col == COL_INCLUSIVE_CPU_TIME_PER) { double total; double val = ps.getElapsedInclusiveCpuTime(); MethodData context = ps.getContext(); total = context.getElapsedInclusiveCpuTime(); double per = val * 100.0 / total; return String.format("%.1f%%", per); } if (col == COL_INCLUSIVE_REAL_TIME) { double val = ps.getElapsedInclusiveRealTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } if (col == COL_INCLUSIVE_REAL_TIME_PER) { double total; double val = ps.getElapsedInclusiveRealTime(); MethodData context = ps.getContext(); total = context.getElapsedInclusiveRealTime(); 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_CPU_TIME) { double val = pd.getElapsedInclusiveCpuTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } if (col == COL_INCLUSIVE_CPU_TIME_PER) { double total; double val = pd.getElapsedInclusiveCpuTime(); MethodData context = pd.getContext(); total = context.getElapsedInclusiveCpuTime(); double per = val * 100.0 / total; return String.format("%.1f%%", per); } if (col == COL_INCLUSIVE_REAL_TIME) { double val = pd.getElapsedInclusiveRealTime(); val = traceUnits.getScaledValue(val); return String.format("%.3f", val); } if (col == COL_INCLUSIVE_REAL_TIME_PER) { double total; double val = pd.getElapsedInclusiveRealTime(); MethodData context = pd.getContext(); total = context.getElapsedInclusiveRealTime(); 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; } @Override 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; } @Override public Color getForeground(Object element) { return null; } @Override 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_CPU_TIME]) { sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE_CPU_TIME); Arrays.sort(mRoots, sorter); } else if (name == mColumnNames[COL_EXCLUSIVE_CPU_TIME_PER]) { sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE_CPU_TIME); Arrays.sort(mRoots, sorter); } else if (name == mColumnNames[COL_INCLUSIVE_CPU_TIME]) { sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE_CPU_TIME); Arrays.sort(mRoots, sorter); } else if (name == mColumnNames[COL_INCLUSIVE_CPU_TIME_PER]) { sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE_CPU_TIME); Arrays.sort(mRoots, sorter); } else if (name == mColumnNames[COL_EXCLUSIVE_REAL_TIME]) { sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE_REAL_TIME); Arrays.sort(mRoots, sorter); } else if (name == mColumnNames[COL_EXCLUSIVE_REAL_TIME_PER]) { sorter.setColumn(MethodData.Sorter.Column.BY_EXCLUSIVE_REAL_TIME); Arrays.sort(mRoots, sorter); } else if (name == mColumnNames[COL_INCLUSIVE_REAL_TIME]) { sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE_REAL_TIME); Arrays.sort(mRoots, sorter); } else if (name == mColumnNames[COL_INCLUSIVE_REAL_TIME_PER]) { sorter.setColumn(MethodData.Sorter.Column.BY_INCLUSIVE_REAL_TIME); 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_CPU_TIME_PER_CALL]) { sorter.setColumn(MethodData.Sorter.Column.BY_CPU_TIME_PER_CALL); Arrays.sort(mRoots, sorter); } else if (name == mColumnNames[COL_REAL_TIME_PER_CALL]) { sorter.setColumn(MethodData.Sorter.Column.BY_REAL_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(); } } }