/***************************************************************************** * Copyright (c) 2010 g-Eclipse Consortium * 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 * * Initial development of the original code was made for the * g-Eclipse project founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributors: * Christof Klausecker MNM-Team, LMU Munich - initial API and implementation *****************************************************************************/ package eu.geclipse.callgraph.views; import java.util.Iterator; import java.util.Stack; import org.eclipse.cdt.core.dom.ast.IASTForStatement; import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTIfStatement; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTWhileStatement; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.core.runtime.CoreException; import org.eclipse.draw2d.Label; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.part.ViewPart; import org.eclipse.zest.core.viewers.AbstractZoomableViewer; import org.eclipse.zest.core.viewers.GraphViewer; import org.eclipse.zest.core.viewers.IZoomableWorkbenchPart; import org.eclipse.zest.core.widgets.Graph; import org.eclipse.zest.core.widgets.GraphConnection; import org.eclipse.zest.core.widgets.GraphNode; import org.eclipse.zest.core.widgets.ZestStyles; import org.eclipse.zest.layouts.LayoutAlgorithm; import org.eclipse.zest.layouts.LayoutStyles; import org.eclipse.zest.layouts.algorithms.CompositeLayoutAlgorithm; import org.eclipse.zest.layouts.algorithms.HorizontalShift; import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; import eu.geclipse.eventgraph.tracereader.otf.OTFReader; import eu.geclipse.eventgraph.tracereader.otf.Process; import eu.geclipse.eventgraph.tracereader.otf.util.Node; import eu.geclipse.traceview.IProcess; /** * Uses Zest to draw a Call Graph */ public class CallGraphView extends ViewPart implements IZoomableWorkbenchPart { private Graph graph; private GraphViewer viewer; private long max; private int depth; private int x; private Stack<GraphNode> stack; private ISelectionListener selectionListener; /** * Constructs a new CallGraphView */ public CallGraphView() { this.max = 0; this.x = 100; this.depth = 0; } /* * (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ @Override public void createPartControl( final Composite parent ) { this.graph = new Graph( parent, SWT.NONE ); this.graph.addSelectionListener( new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent e ) { super.widgetSelected( e ); } } ); this.graph.addPaintListener( new PaintListener() { @SuppressWarnings("synthetic-access") public void paintControl( final PaintEvent e ) { CallGraphView.this.graph.layout(); } } ); this.selectionListener = new ISelectionListener() { @SuppressWarnings("synthetic-access") public void selectionChanged( final IWorkbenchPart part, final ISelection selection ) { if( graph.isDisposed() ) { getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener( selectionListener ); return; } if( selection instanceof IStructuredSelection ) { IStructuredSelection structuredSelection = ( IStructuredSelection )selection; // OTF based call graph if( structuredSelection.getFirstElement() instanceof IProcess ) { Iterator iterator = structuredSelection.iterator(); Object[] objects = CallGraphView.this.graph.getConnections().toArray(); for( int i = 0; i < objects.length; i++ ) { ( ( GraphConnection )objects[ i ] ).dispose(); } objects = CallGraphView.this.graph.getNodes().toArray(); for( int i = 0; i < objects.length; i++ ) { ( ( GraphNode )objects[ i ] ).dispose(); } while( iterator.hasNext() ) { Object next = iterator.next(); if( next instanceof Process ) { Process process = ( Process )next; OTFReader otfReader = ( OTFReader )process.getTrace(); Node root = otfReader.getRootNode( process.getProcessId() ); CallGraphView.this.max = 0; getMax( root ); drawNode( root, null ); CompositeLayoutAlgorithm treeLayoutAlgorithm = new CompositeLayoutAlgorithm( LayoutStyles.NO_LAYOUT_NODE_RESIZING, new LayoutAlgorithm[]{ new TreeLayoutAlgorithm( LayoutStyles.NO_LAYOUT_NODE_RESIZING ), new HorizontalShift( LayoutStyles.NO_LAYOUT_NODE_RESIZING ) } ); CallGraphView.this.graph.setLayoutAlgorithm( treeLayoutAlgorithm, true ); } } } else // experimental C/C++ file call graph if( structuredSelection.getFirstElement() instanceof ITranslationUnit ) { ITranslationUnit translationUnit = ( ITranslationUnit )structuredSelection.getFirstElement(); Object[] objects = CallGraphView.this.graph.getConnections().toArray(); for( int i = 0; i < objects.length; i++ ) { ( ( GraphConnection )objects[ i ] ).dispose(); } objects = CallGraphView.this.graph.getNodes().toArray(); for( int i = 0; i < objects.length; i++ ) { ( ( GraphNode )objects[ i ] ).dispose(); // TODO find fix to dispose selected nodes } try { IASTTranslationUnit astTranslationUnit = translationUnit.getAST(); // IASTDeclaration[] declarations = astTranslationUnit.getDeclarations(); // declarations[ 0 ].getFileLocation(); // Visitor visitor = new Visitor(); // astTranslationUnit.accept( visitor); IASTNode[] nodes = astTranslationUnit.getChildren(); for( IASTNode node : nodes ) { if( node instanceof IASTFunctionDefinition ) { IASTFunctionDefinition functionDefinition = ( IASTFunctionDefinition )node; if( "main".equals( functionDefinition.getDeclarator().getName().toString() ) ) { //$NON-NLS-1$ CallGraphView.this.depth = 0; CallGraphView.this.x = 0; GraphNode main = new GraphNode( CallGraphView.this.graph, SWT.NONE, "main" ); //$NON-NLS-1$ CallGraphView.this.stack = new Stack<GraphNode>(); CallGraphView.this.stack.push( main ); node( functionDefinition ); } } } } catch( CoreException e ) { e.printStackTrace(); } } } } }; getSite().getWorkbenchWindow().getSelectionService().addSelectionListener( this.selectionListener ); } private void nodeIf( final IASTIfStatement ifStatement ) { this.depth += 50; int elsepos = this.depth + 50; // IF GraphNode ifNode = new GraphNode( this.graph, SWT.NONE, "if " + ifStatement.getConditionExpression().getRawSignature() ); //$NON-NLS-1$ ifNode.setLocation( this.x - ifNode.getSize().width / 2, this.depth ); this.x -= ifNode.getSize().width + 50; this.depth += 50; GraphNode thenNode = new GraphNode( this.graph, SWT.NONE, "then" ); //$NON-NLS-1$ thenNode.setLocation( this.x - thenNode.getSize().width / 2, this.depth ); new GraphConnection( this.graph, ZestStyles.CONNECTIONS_DIRECTED, this.stack.lastElement(), ifNode ); new GraphConnection( this.graph, ZestStyles.CONNECTIONS_DIRECTED, ifNode, thenNode ); this.stack.push( thenNode ); node( ifStatement.getThenClause() ); this.stack.pop(); this.x += ifNode.getSize().width + 50; int backup = this.depth; this.depth = elsepos; if( ifStatement.getElseClause() != null ) { this.x += ifNode.getSize().width + 50; GraphNode elseNode = new GraphNode( this.graph, SWT.NONE, "else" ); //$NON-NLS-1$ new GraphConnection( this.graph, ZestStyles.CONNECTIONS_DIRECTED, ifNode, elseNode ); elseNode.setLocation( this.x - elseNode.getSize().width / 2, this.depth ); this.stack.push( elseNode ); node( ifStatement.getElseClause() ); this.stack.pop(); this.x -= ifNode.getSize().width + 50; } this.depth = backup; } private void nodeFor( final IASTForStatement forStatement ) { this.depth += 50; GraphNode graphNode = new GraphNode( this.graph, SWT.NONE, "for " + forStatement.getInitializerStatement().getRawSignature()); //$NON-NLS-1$ graphNode.setLocation( this.x - graphNode.getSize().width / 2, this.depth ); this.x -= graphNode.getSize().width + 50; new GraphConnection( this.graph, ZestStyles.CONNECTIONS_DIRECTED, this.stack.lastElement(), graphNode ); this.stack.push( graphNode ); this.x += graphNode.getSize().width + 50; node( forStatement.getBody() ); new GraphConnection( this.graph, ZestStyles.CONNECTIONS_DIRECTED, this.stack.pop(), graphNode ); } private void node( final IASTNode node ) { IASTNode[] children = node.getChildren(); for( IASTNode child : children ) { // Function Call if( child instanceof IASTFunctionCallExpression ) { this.depth += 50; IASTFunctionCallExpression functionCallExpression = ( IASTFunctionCallExpression )child; for( IASTNode n : functionCallExpression.getFunctionNameExpression().getChildren() ) { GraphNode graphNode = new GraphNode( this.graph, SWT.NONE, n.getRawSignature() ); graphNode.setLocation( this.x - graphNode.getSize().width / 2, this.depth ); new GraphConnection( this.graph, ZestStyles.CONNECTIONS_DIRECTED, this.stack.pop(), graphNode ); this.stack.push( graphNode ); } } // IF and ELSE else if( child instanceof IASTIfStatement ) { IASTIfStatement ifStatement = ( IASTIfStatement )child; nodeIf( ifStatement ); // WHILE } else if( child instanceof IASTWhileStatement ) { this.depth += 50; IASTWhileStatement whileStatement = ( IASTWhileStatement )child; this.depth++; GraphNode graphNode = new GraphNode( this.graph, SWT.NONE, "while" ); //$NON-NLS-1$ graphNode.setLocation( x - graphNode.getSize().width / 2, this.depth ); new GraphConnection( this.graph, ZestStyles.CONNECTIONS_DIRECTED, this.stack.lastElement(), graphNode ); this.stack.push( graphNode ); node( child ); this.stack.pop(); } else // FOR if( child instanceof IASTForStatement ) { IASTForStatement forStatement = ( IASTForStatement )child; nodeFor( forStatement ); } else { node( child ); } } } private void getMax( final Node node ) { if( this.max < node.getTime() ) { this.max = node.getTime(); } for( Node child : node.getChildren() ) { getMax( child ); } } private void drawNode( final Node node, final GraphNode parent ) { GraphNode graphNode = new GraphNode( this.graph, SWT.NONE, node.getFunctionName() ); int percentage = ( ( int )( ( node.getTime() * 255 ) / this.max ) ); Label label = new Label(); label.setText( "time: " + node.getTime() ); graphNode.setTooltip( label ); Color color = new Color( Display.getDefault(), new RGB( 255, 255 - percentage, 255 - percentage ) ); graphNode.setBackgroundColor( color ); if( parent != null ) { GraphConnection graphConnection = new GraphConnection( this.graph, SWT.None, parent, graphNode ); graphConnection.setText( Integer.toString( node.getCount() ) ); } for( Node child : node.getChildren() ) drawNode( child, graphNode ); } /* * (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ @Override public void setFocus() { // TODO Auto-generated method stub } /* * (non-Javadoc) * @see org.eclipse.zest.core.viewers.IZoomableWorkbenchPart#getZoomableViewer() */ public AbstractZoomableViewer getZoomableViewer() { return this.viewer; } }