/******************************************************************************* * Copyright (c) 2009 Red Hat, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Elliott Baron <ebaron@redhat.com> - initial API and implementation *******************************************************************************/ package org.eclipse.linuxtools.internal.valgrind.cachegrind; import java.util.Arrays; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.ISourceRange; import org.eclipse.cdt.core.model.ISourceReference; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.TreeViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.linuxtools.internal.valgrind.cachegrind.model.CachegrindFile; import org.eclipse.linuxtools.internal.valgrind.cachegrind.model.CachegrindFunction; import org.eclipse.linuxtools.internal.valgrind.cachegrind.model.CachegrindLine; import org.eclipse.linuxtools.internal.valgrind.cachegrind.model.CachegrindOutput; import org.eclipse.linuxtools.internal.valgrind.cachegrind.model.ICachegrindElement; import org.eclipse.linuxtools.internal.valgrind.ui.ValgrindUIPlugin; import org.eclipse.linuxtools.profiling.ui.ProfileUIUtils; import org.eclipse.linuxtools.valgrind.ui.CollapseAction; import org.eclipse.linuxtools.valgrind.ui.ExpandAction; import org.eclipse.linuxtools.valgrind.ui.IValgrindToolView; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.ui.part.ViewPart; public class CachegrindViewPart extends ViewPart implements IValgrindToolView { private CachegrindOutput[] outputs; private TreeViewer viewer; private static final int COLUMN_SIZE = 75; private CachegrindLabelProvider labelProvider; private CachegrindTreeContentProvider contentProvider; private IDoubleClickListener doubleClickListener; private ExpandAction expandAction; private CollapseAction collapseAction; // Events - Cache private static final String IR = "Ir"; //$NON-NLS-1$ private static final String I1MR = "I1mr"; //$NON-NLS-1$ private static final String ILMR = "ILmr"; //$NON-NLS-1$ private static final String DR = "Dr"; //$NON-NLS-1$ private static final String D1MR = "D1mr"; //$NON-NLS-1$ private static final String DLMR = "DLmr"; //$NON-NLS-1$ private static final String DW = "Dw"; //$NON-NLS-1$ private static final String D1MW = "D1mw"; //$NON-NLS-1$ private static final String DLMW = "DLmw"; //$NON-NLS-1$ // Events - Branch private static final String BC = "Bc"; //$NON-NLS-1$ private static final String BCM = "Bcm"; //$NON-NLS-1$ private static final String BI = "Bi"; //$NON-NLS-1$ private static final String BIM = "Bim"; //$NON-NLS-1$ @Override public void createPartControl(Composite parent) { Composite top = new Composite(parent, SWT.NONE); GridLayout topLayout = new GridLayout(); topLayout.marginHeight = topLayout.marginWidth = 0; top.setLayout(topLayout); top.setLayoutData(new GridData(GridData.FILL_BOTH)); viewer = new TreeViewer(top, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION); labelProvider = new CachegrindLabelProvider(); ColumnViewerToolTipSupport.enableFor(viewer); Tree tree = viewer.getTree(); tree.setHeaderVisible(true); tree.setLinesVisible(true); tree.setLayoutData(new GridData(GridData.FILL_BOTH)); TreeViewerColumn column = new TreeViewerColumn(viewer, SWT.NONE); column.getColumn().setText(Messages.getString("CachegrindViewPart.Location")); //$NON-NLS-1$ column.getColumn().setWidth(COLUMN_SIZE * 4); column.getColumn().setResizable(true); column.getColumn().addSelectionListener(getHeaderListener()); column.setLabelProvider(labelProvider); contentProvider = new CachegrindTreeContentProvider(); viewer.setContentProvider(contentProvider); viewer.setLabelProvider(labelProvider); viewer.setAutoExpandLevel(2); doubleClickListener = event -> { Object selection = ((StructuredSelection) event.getSelection()).getFirstElement(); String path = null; int line = 0; if (selection instanceof CachegrindFile) { path = ((CachegrindFile) selection).getPath(); } else if (selection instanceof CachegrindLine) { CachegrindLine element = (CachegrindLine) selection; CachegrindFile file = (CachegrindFile) element.getParent().getParent(); path = file.getPath(); line = element.getLine(); } else if (selection instanceof CachegrindFunction) { CachegrindFunction function = (CachegrindFunction) selection; path = ((CachegrindFile) function.getParent()).getPath(); if (function.getModel() instanceof ISourceReference) { ISourceReference model = (ISourceReference) function.getModel(); try { ISourceRange sr = model.getSourceRange(); if (sr != null) { line = sr.getStartLine(); } } catch (CModelException e1) { e1.printStackTrace(); } } } if (path != null) { try { ProfileUIUtils.openEditorAndSelect(path, line, ValgrindUIPlugin.getDefault().getProfiledProject()); } catch (BadLocationException | CoreException e2) { e2.printStackTrace(); } } }; viewer.addDoubleClickListener(doubleClickListener); expandAction = new ExpandAction(viewer); collapseAction = new CollapseAction(viewer); MenuManager manager = new MenuManager(); manager.addMenuListener(manager1 -> { ITreeSelection selection = (ITreeSelection) viewer.getSelection(); ICachegrindElement element = (ICachegrindElement) selection.getFirstElement(); if (contentProvider.hasChildren(element)) { manager1.add(expandAction); manager1.add(collapseAction); } }); manager.setRemoveAllWhenShown(true); Menu contextMenu = manager.createContextMenu(viewer.getTree()); viewer.getControl().setMenu(contextMenu); } @Override public void setFocus() { viewer.getTree().setFocus(); } @Override public IAction[] getToolbarActions() { return null; } @Override public void refreshView() { if (outputs != null && outputs.length > 0) { String[] events = outputs[0].getEvents(); for (int i = 0; i < events.length; i++) { TreeViewerColumn column = new TreeViewerColumn(viewer, SWT.NONE); column.getColumn().setText(events[i]); column.getColumn().setWidth(COLUMN_SIZE); column.getColumn().setToolTipText(getFullEventName(events[i])); column.getColumn().setResizable(true); column.getColumn().addSelectionListener(getHeaderListener()); column.setLabelProvider(labelProvider); } viewer.setInput(outputs); viewer.getTree().layout(true); } } public void setOutputs(CachegrindOutput[] outputs) { this.outputs = outputs; } public CachegrindOutput[] getOutputs() { return outputs; } public TreeViewer getViewer() { return viewer; } public IDoubleClickListener getDoubleClickListener() { return doubleClickListener; } private SelectionListener getHeaderListener() { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { TreeColumn column = (TreeColumn) e.widget; Tree tree = viewer.getTree(); if (column.equals(tree.getSortColumn())) { int direction = tree.getSortDirection() == SWT.UP ? SWT.DOWN : SWT.UP; tree.setSortDirection(direction); } else { tree.setSortDirection(SWT.UP); } tree.setSortColumn(column); viewer.setComparator(new ViewerComparator() { @Override public int compare(Viewer viewer, Object e1, Object e2) { Tree tree = ((TreeViewer) viewer).getTree(); int direction = tree.getSortDirection(); ICachegrindElement o1 = (ICachegrindElement) e1; ICachegrindElement o2 = (ICachegrindElement) e2; long result = 0; int sortIndex = Arrays.asList(tree.getColumns()).indexOf(tree.getSortColumn()); if (sortIndex == 0) { // use compareTo result = o1.compareTo(o2); } else { long[] v1 = null; long[] v2 = null; if (o1 instanceof CachegrindFunction && o2 instanceof CachegrindFunction) { v1 = ((CachegrindFunction) o1).getTotals(); v2 = ((CachegrindFunction) o2).getTotals(); } else if (o1 instanceof CachegrindLine && o2 instanceof CachegrindLine) { v1 = ((CachegrindLine) o1).getValues(); v2 = ((CachegrindLine) o2).getValues(); } else if (o1 instanceof CachegrindOutput && o2 instanceof CachegrindOutput) { v1 = ((CachegrindOutput) o1).getSummary(); v2 = ((CachegrindOutput) o2).getSummary(); } if (v1 != null && v2 != null) { result = v1[sortIndex - 1] - v2[sortIndex - 1]; } } // ascending or descending result = direction == SWT.UP ? result : -result; // overflow check if (result > Integer.MAX_VALUE) { result = Integer.MAX_VALUE; } else if (result < Integer.MIN_VALUE) { result = Integer.MIN_VALUE; } return (int) result; } }); } }; } private String getFullEventName(String event) { String result = event; if (event.equals(IR)) { result = Messages.getString("CachegrindViewPart.Ir_long"); //$NON-NLS-1$ } else if (event.equals(I1MR)) { result = Messages.getString("CachegrindViewPart.I1mr_long"); //$NON-NLS-1$ } else if (event.equals(ILMR)) { result = Messages.getString("CachegrindViewPart.I2mr_long"); //$NON-NLS-1$ } else if (event.equals(DR)) { result = Messages.getString("CachegrindViewPart.Dr_long"); //$NON-NLS-1$ } else if (event.equals(D1MR)) { result = Messages.getString("CachegrindViewPart.D1mr_long"); //$NON-NLS-1$ } else if (event.equals(DLMR)) { result = Messages.getString("CachegrindViewPart.D2mr_long"); //$NON-NLS-1$ } else if (event.equals(DW)) { result = Messages.getString("CachegrindViewPart.Dw_long"); //$NON-NLS-1$ } else if (event.equals(D1MW)) { result = Messages.getString("CachegrindViewPart.D1mw_long"); //$NON-NLS-1$ } else if (event.equals(DLMW)) { result = Messages.getString("CachegrindViewPart.D2mw_long"); //$NON-NLS-1$ } else if (event.equals(BC)) { result = Messages.getString("CachegrindViewPart.Bc_long"); //$NON-NLS-1$ } else if (event.equals(BCM)) { result = Messages.getString("CachegrindViewPart.Bcm_long"); //$NON-NLS-1$ } else if (event.equals(BI)) { result = Messages.getString("CachegrindViewPart.Bi_long"); //$NON-NLS-1$ } else if (event.equals(BIM)) { result = Messages.getString("CachegrindViewPart.Bim_long"); //$NON-NLS-1$ } return result; } protected static class CachegrindTreeContentProvider implements ITreeContentProvider { @Override public Object[] getChildren(Object parentElement) { Object[] result = null; if (parentElement instanceof CachegrindOutput[]) { result = (CachegrindOutput[]) parentElement; } else if (parentElement instanceof ICachegrindElement) { result = ((ICachegrindElement) parentElement).getChildren(); } return result; } @Override public Object getParent(Object element) { return ((ICachegrindElement) element).getParent(); } @Override public boolean hasChildren(Object element) { ICachegrindElement[] children = (ICachegrindElement[]) getChildren(element); return children != null && children.length > 0; } @Override public Object[] getElements(Object inputElement) { return getChildren(inputElement); } @Override public void dispose() { } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } } }