/******************************************************************************* * 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: * Red Hat - initial API and implementation *******************************************************************************/ package org.eclipse.linuxtools.internal.callgraph; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.draw2d.LightweightSystem; import org.eclipse.draw2d.parts.ScrollableThumbnail; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.linuxtools.internal.callgraph.core.PluginConstants; import org.eclipse.linuxtools.internal.callgraph.core.SystemTapParser; import org.eclipse.linuxtools.internal.callgraph.core.SystemTapUIErrorMessages; import org.eclipse.linuxtools.internal.callgraph.core.SystemTapView; import org.eclipse.linuxtools.internal.callgraph.graphlisteners.AutoScrollSelectionListener; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Spinner; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * The SystemTap View for displaying output of the 'stap' command, and acts * as a container for any graph to be rendered. Any buttons/controls/actions * necessary to the smooth running of SystemTap could be placed here. */ public class CallgraphView extends SystemTapView { private StapGraphParser parser; private Action viewTreeview; private Action viewRadialview; private Action viewAggregateview; private Action viewLevelview; private Action viewRefresh; private Action animationSlow; private Action animationFast; private Action modeCollapsedNodes; private Action markersNext; private Action markersPrevious; private Action limits; private Action gotoNext; private Action gotoPrevious; private Action gotoLast; private Action play; private Action saveDot; private Action saveColDot; private Action saveCurDot; private Action saveText; private ImageDescriptor playImage = getImageDescriptor("icons/perform.png"); //$NON-NLS-1$ private ImageDescriptor pauseImage = getImageDescriptor("icons/pause.gif"); //$NON-NLS-1$ private Composite graphComp; private Composite treeComp; private StapGraph g; private static final int TREE_SIZE = 200; /** * Initializes the view by creating composites (if necessary) and canvases * Calls loadData(), and calls finishLoad() if not in realTime mode (otherwise * it is up to the user-defined update methods to finish loading). * * @return status * */ @Override public IStatus initializeView(Display targetDisplay, IProgressMonitor monitor) { if (targetDisplay == null && Display.getCurrent() == null) { Display.getDefault(); } makeTreeComp(); makeGraphComp(); graphComp.setBackgroundMode(SWT.INHERIT_FORCE); //Create papa canvas Canvas papaCanvas = new Canvas(graphComp, SWT.BORDER); GridLayout papaLayout = new GridLayout(1, true); papaLayout.horizontalSpacing=0; papaLayout.verticalSpacing=0; papaLayout.marginHeight=0; papaLayout.marginWidth=0; papaCanvas.setLayout(papaLayout); GridData papaGD = new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false); papaGD.widthHint=160; papaCanvas.setLayoutData(papaGD); //Add first button Image image = getImageDescriptor("icons/up.gif").createImage(); //$NON-NLS-1$ Button up = new Button(papaCanvas, SWT.PUSH); GridData buttonData = new GridData(SWT.CENTER, SWT.CENTER, true, false); buttonData.widthHint = 150; buttonData.heightHint = 20; up.setData(buttonData); up.setImage(image); up.setToolTipText(Messages.getString("CallgraphView.ThumbNailUp")); //$NON-NLS-1$ //Add thumb canvas Canvas thumbCanvas = new Canvas(papaCanvas, SWT.NONE); //Add second button image = getImageDescriptor("icons/down.gif").createImage(); //$NON-NLS-1$ Button down = new Button(papaCanvas, SWT.PUSH); buttonData = new GridData(SWT.CENTER, SWT.CENTER, true, false); buttonData.widthHint = 150; buttonData.heightHint = 0; down.setData(buttonData); down.setImage(image); down.setToolTipText(Messages.getString("CallgraphView.ThumbNailDown")); //$NON-NLS-1$ //Initialize graph g = new StapGraph(graphComp, SWT.BORDER, treeComp, papaCanvas, this); g.setLayoutData(new GridData(masterComposite.getBounds().width,Display.getCurrent().getBounds().height - TREE_SIZE)); up.addSelectionListener(new AutoScrollSelectionListener( AutoScrollSelectionListener.AUTO_SCROLL_UP, g)); down.addSelectionListener(new AutoScrollSelectionListener( AutoScrollSelectionListener.AUTO_SCROLL_DOWN, g)); //Initialize thumbnail GridData thumbGD = new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false); thumbGD.widthHint=160; thumbCanvas.setLayoutData(thumbGD); LightweightSystem lws = new LightweightSystem(thumbCanvas); ScrollableThumbnail thumb = new ScrollableThumbnail(g.getViewport()); thumb.setSource(g.getContents()); lws.setContents(thumb); loadData(monitor); return finishLoad(monitor); } /** * Load data. * @param mon -- Progress monitor. * @return */ private IStatus loadData(IProgressMonitor mon) { IProgressMonitor monitor = mon; //Dummy node, set start time if (g.getNodeData(0) == null) { g.loadData(SWT.NONE, 0, StapGraph.CONSTANT_TOP_NODE_NAME, 1, 1, -1, false, ""); //$NON-NLS-1$ } g.setStartTime(parser.startTime); g.setEndTime(parser.endingTimeInNS); /* * Load graph data */ for (int id_parent : parser.serialMap.keySet()) { if (id_parent < 0) { continue; } boolean marked = false; String msg = ""; //$NON-NLS-1$ if (g.getNodeData(id_parent) == null) { if (parser.markedMap.get(id_parent) != null) { marked = true; msg = parser.markedMap.remove(id_parent); } g.loadData(SWT.NONE, id_parent, parser.serialMap.get(id_parent), parser.timeMap.get(id_parent), 1, 0, marked, msg); } for (int key :parser.neighbourMaps.keySet()) { HashMap<Integer, ArrayList<Integer>> outNeighbours = parser.neighbourMaps.get(key); if (outNeighbours == null || outNeighbours.get(id_parent) == null) { continue; } for (int id_child : outNeighbours.get(id_parent)) { if (g.getNodeData(id_child) != null && id_child < 0) { //Assume this is an additional call of the same node //Should only happen in dot-files!! g.addCalled(id_child); continue; } else if (g.getNodeData(id_child) != null) { continue; } if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } marked = false; msg = ""; //$NON-NLS-1$ if (parser.markedMap.get(id_child) != null) { marked = true; msg = parser.markedMap.remove(id_child); } if (id_child != -1) { if (parser.timeMap.get(id_child) == null){ g.loadData(SWT.NONE, id_child, parser.serialMap .get(id_child), parser.timeMap.get(0), 1, id_parent, marked,msg); }else{ g.loadData(SWT.NONE, id_child, parser.serialMap .get(id_child), parser.timeMap.get(id_child), 1, id_parent, marked,msg); } } } } if (parser.neighbourMaps.size() > 1) { g.setThreaded(); } } monitor.worked(1); if (parser.markedMap.size() > 0) { //Still some markers left for (int key : parser.markedMap.keySet()) { g.insertMessage(key, parser.markedMap.get(key)); } //Erase the remaining nodes, just in case parser.markedMap.clear(); } if (g.aggregateTime == null) { g.aggregateTime = new HashMap<>(); } if (g.aggregateCount == null) { g.aggregateCount = new HashMap<>(); } g.aggregateCount.putAll(parser.countMap); g.aggregateTime.putAll(parser.aggregateTimeMap); //TODO: Do not set to 0. g.setLastFunctionCalled(0); //Finish off by collapsing nodes, initializing the tree and setting options g.recursivelyCollapseAllChildrenOfNode(g.getTopNode()); monitor.worked(1); setGraphOptions(true); g.initializeTree(); g.setProject(parser.project); return Status.OK_STATUS; } /** * Completes the loading process by calculating aggregate data. * * @param monitor * @return */ private IStatus finishLoad(IProgressMonitor monitor) { if (g.aggregateCount == null) { g.aggregateCount = new HashMap<>(); } g.aggregateCount.putAll(parser.countMap); if (g.aggregateTime == null) { g.aggregateTime = new HashMap<>(); } g.aggregateTime.putAll(parser.aggregateTimeMap); //Set total time if (parser.totalTime != -1) { g.setTotalTime(parser.totalTime); } //-------------Finish initializations //Generate data for collapsed nodes if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } g.initializeTree(); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } g.setCallOrderList(parser.callOrderList); g.setProject(parser.project); this.initializePartControl(); return Status.OK_STATUS; } /** * Enable or Disable the graph options * @param visible */ private void setGraphOptions (boolean visible){ play.setEnabled(visible); saveFile.setEnabled(visible); saveDot.setEnabled(visible); saveColDot.setEnabled(visible); saveCurDot.setEnabled(visible); saveText.setEnabled(visible); viewTreeview.setEnabled(visible); viewRadialview.setEnabled(visible); viewAggregateview.setEnabled(visible); viewLevelview.setEnabled(visible); viewRefresh.setEnabled(visible); limits.setEnabled(visible); markersNext.setEnabled(visible); markersPrevious.setEnabled(visible); animationSlow.setEnabled(visible); animationFast.setEnabled(visible); modeCollapsedNodes.setEnabled(visible); gotoNext.setEnabled(visible); gotoPrevious.setEnabled(visible); gotoLast.setEnabled(visible); } private void makeTreeComp() { if (treeComp != null && !treeComp.isDisposed()) { treeComp.dispose(); } treeComp = new Composite(this.masterComposite, SWT.NONE); GridData treegd = new GridData(SWT.BEGINNING, SWT.FILL, false, true); treegd.widthHint = TREE_SIZE; treeComp.setLayout(new FillLayout()); treeComp.setLayoutData(treegd); } private void makeGraphComp() { if (graphComp != null && !graphComp.isDisposed()) { graphComp.dispose(); } graphComp = new Composite(this.masterComposite, SWT.NONE); GridData graphgd = new GridData(SWT.FILL, SWT.FILL, true, true); GridLayout gl = new GridLayout(2, false); gl.horizontalSpacing=0; gl.verticalSpacing=0; graphComp.setLayout(gl); graphComp.setLayoutData(graphgd); } /** * This must be executed before a Graph is displayed */ private void initializePartControl(){ setGraphOptions(true); if (graphComp == null) { return; } graphComp.setParent(masterComposite); if (treeComp != null) { treeComp.setParent(masterComposite); } graphComp.setSize(masterComposite.getSize().x ,masterComposite.getSize().y); } /** * The action performed by saveText. */ private void saveTextAction() { //Prints an 80 char table Shell sh = new Shell(); FileDialog dialog = new FileDialog(sh, SWT.SAVE); String filePath = dialog.open(); if (filePath == null) { return; } File f = new File(filePath); f.delete(); try (BufferedWriter out = new BufferedWriter(new FileWriter(f))) { f.createNewFile(); StringBuilder builder = new StringBuilder(); builder.append(" Function | Called | Time\n"); //$NON-NLS-1$ for (StapData k : g.nodeDataMap.values()) { if ( (!k.isCollapsed ) && !k.isOnlyChildWithThisName()) { continue; } if (k.isCollapsed) { StringBuilder name = new StringBuilder(k.name); name = fixString(name, 60); builder.append(" " + name + " | "); //$NON-NLS-1$ //$NON-NLS-2$ StringBuilder called = new StringBuilder("" + k.timesCalled); //$NON-NLS-1$ called = fixString(called, 6); StringBuilder time = new StringBuilder("" + //$NON-NLS-1$ StapNode.numberFormat.format((float) k.getTime()/g.getTotalTime() * 100) + "%"); //$NON-NLS-1$ time = fixString(time, 6); builder.append(called + " | " + time + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ } if (builder.length() > 0) { out.append(builder.toString()); } } } catch (IOException e) { e.printStackTrace(); } } /** * This is a callback that will allow us to create the viewer and * initialize it. */ @Override public void createPartControl(Composite parent) { if (masterComposite != null) { masterComposite.dispose(); } masterComposite = parent; GridLayout layout = new GridLayout(2, false); layout.horizontalSpacing=0; GridData gd = new GridData(100, 100); parent.setLayout(layout); parent.setLayoutData(gd); // LOAD ALL ACTIONS createActions(); //MENU FOR SYSTEMTAP BUTTONS IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager(); //MENU FOR SYSTEMTAP GRAPH OPTIONS IMenuManager menu = getViewSite().getActionBars().getMenuManager(); // ADD OPTIONS TO THE GRAPH MENU addFileMenu(); saveCurDot = new Action(Messages.getString("CallgraphView.SaveViewAsDot")) { //$NON-NLS-1$ @Override public void run(){ writeToDot(g.getCollapseMode(), g.nodeMap.keySet()); } }; saveDot = new Action(Messages.getString("CallgraphView.SaveAllUncollapsedAsDot")) { //$NON-NLS-1$ @Override public void run(){ writeToDot(false, g.nodeDataMap.keySet()); } }; saveColDot = new Action (Messages.getString("CallgraphView.SaveAllCollapsedAsDot")) { //$NON-NLS-1$ @Override public void run(){ writeToDot(true, g.nodeDataMap.keySet()); } }; saveText = new Action (Messages.getString("CallgraphView.SaveCollapsedAsASCII")) { //$NON-NLS-1$ @Override public void run() { saveTextAction(); } }; IMenuManager saveMenu = new MenuManager(Messages.getString("CallgraphView.SaveMenu")); //$NON-NLS-1$ file.add(saveMenu); saveMenu.add(saveCurDot); saveMenu.add(saveColDot); saveMenu.add(saveText); saveMenu.add(saveDot); IMenuManager view = new MenuManager(Messages.getString("CallgraphView.ViewMenu")); //$NON-NLS-1$ IMenuManager animation = new MenuManager(Messages.getString("CallgraphView.AnimationMenu")); //$NON-NLS-1$ IMenuManager markers = new MenuManager(Messages.getString("CallgraphView.Markers")); //$NON-NLS-1$ IMenuManager gotoMenu = new MenuManager(Messages.getString("CallgraphView.GoTo")); //$NON-NLS-1$ menu.add(view); menu.add(gotoMenu); addHelpMenu(); view.add(viewTreeview); view.add(viewRadialview); view.add(viewAggregateview); view.add(viewLevelview); view.add(getViewRefresh()); view.add(modeCollapsedNodes); view.add(limits); view.add(animation); gotoMenu.add(play); gotoMenu.add(gotoPrevious); gotoMenu.add(gotoNext); gotoMenu.add(gotoLast); gotoMenu.add(markers); addKillButton(); mgr.add(play); mgr.add(viewRadialview); mgr.add(viewTreeview); mgr.add(viewLevelview); mgr.add(viewAggregateview); mgr.add(modeCollapsedNodes); markers.add(markersNext); markers.add(markersPrevious); animation.add(animationSlow); animation.add(animationFast); setGraphOptions(false); } private static StringBuilder fixString(StringBuilder name, int length) { if (name.length() > length) { name = new StringBuilder(name.substring(0, length - 1)); } else { int diff = length - name.length(); boolean left = true; while (diff > 0) { if (left) { name.insert(0, " "); //$NON-NLS-1$ left = false; } else { name.append(" "); //$NON-NLS-1$ left = true; } diff--; } } return name; } private void createViewActions() { viewTreeview = new Action(Messages.getString("CallgraphView.TreeView")){ //$NON-NLS-1$ @Override public void run() { g.draw(StapGraph.CONSTANT_DRAWMODE_TREE, g.getAnimationMode(), g.getRootVisibleNodeNumber()); g.scrollTo(g.getNode(g.getRootVisibleNodeNumber()) .getLocation().x - g.getBounds().width / 2, g .getNode(g.getRootVisibleNodeNumber()) .getLocation().y); if (play != null) { play.setEnabled(true); } } }; ImageDescriptor treeImage = getImageDescriptor("icons/tree_view.gif"); //$NON-NLS-1$ viewTreeview.setImageDescriptor(treeImage); //Set drawmode to radial view viewRadialview = new Action(Messages.getString("CallgraphView.RadialView")){ //$NON-NLS-1$ @Override public void run(){ g.draw(StapGraph.CONSTANT_DRAWMODE_RADIAL, g.getAnimationMode(), g.getRootVisibleNodeNumber()); if (play != null) { play.setEnabled(true); } } }; ImageDescriptor d = getImageDescriptor("/icons/radial_view.gif"); //$NON-NLS-1$ viewRadialview.setImageDescriptor(d); //Set drawmode to aggregate view viewAggregateview = new Action(Messages.getString("CallgraphView.AggregateView")){ //$NON-NLS-1$ @Override public void run(){ g.draw(StapGraph.CONSTANT_DRAWMODE_AGGREGATE, g.getAnimationMode(), g.getRootVisibleNodeNumber()); if (play != null) { play.setEnabled(false); } } }; ImageDescriptor aggregateImage = getImageDescriptor("/icons/view_aggregateview.gif"); //$NON-NLS-1$ viewAggregateview.setImageDescriptor(aggregateImage); //Set drawmode to level view viewLevelview = new Action(Messages.getString("CallgraphView.LevelView")){ //$NON-NLS-1$ @Override public void run(){ g.draw(StapGraph.CONSTANT_DRAWMODE_LEVEL, g.getAnimationMode(), g.getRootVisibleNodeNumber()); if (play != null) { play.setEnabled(true); } } }; ImageDescriptor levelImage = getImageDescriptor("/icons/showchild_mode.gif"); //$NON-NLS-1$ viewLevelview.setImageDescriptor(levelImage); this.viewRefresh = new Action(Messages.getString("CallgraphView.Reset")){ //$NON-NLS-1$ @Override public void run(){ g.reset(); } }; ImageDescriptor refreshImage = getImageDescriptor("/icons/nav_refresh.gif"); //$NON-NLS-1$ getViewRefresh().setImageDescriptor(refreshImage); } /** * Populates Animate menu. */ private void createAnimateActions() { //Set animation mode to slow animationSlow = new Action(Messages.getString("CallgraphView.AnimationSlow"), IAction.AS_RADIO_BUTTON){ //$NON-NLS-1$ @Override public void run(){ g.setAnimationMode(StapGraph.CONSTANT_ANIMATION_SLOW); this.setChecked(true); animationSlow.setChecked(true); animationFast.setChecked(false); } }; animationSlow.setChecked(true); //Set animation mode to fast animationFast = new Action(Messages.getString("CallgraphView.AnimationFast"), IAction.AS_RADIO_BUTTON){ //$NON-NLS-1$ @Override public void run(){ g.setAnimationMode(StapGraph.CONSTANT_ANIMATION_FASTEST); animationSlow.setChecked(false); animationFast.setChecked(true); } }; //Toggle collapse mode modeCollapsedNodes = new Action(Messages.getString("CallgraphView.CollapsedMode"), IAction.AS_CHECK_BOX){ //$NON-NLS-1$ @Override public void run(){ if (g.isCollapseMode()) { g.setCollapseMode(false); g.draw(g.getRootVisibleNodeNumber()); } else { g.setCollapseMode(true); g.draw(g.getRootVisibleNodeNumber()); } } }; ImageDescriptor newImage = getImageDescriptor("icons/mode_collapsednodes.gif"); //$NON-NLS-1$ modeCollapsedNodes.setImageDescriptor(newImage); limits = new Action(Messages.getString("CallgraphView.SetLimits"), IAction.AS_PUSH_BUTTON) { //$NON-NLS-1$ private Spinner limit; private Spinner buffer; private Shell sh; @Override public void run() { sh = new Shell(); sh.setLayout(new GridLayout()); sh.setSize(150, 200); Label limitLabel = new Label(sh, SWT.NONE); limitLabel.setLayoutData(new GridData(SWT.CENTER, SWT.DEFAULT, true, false)); limitLabel.setText(Messages.getString("CallgraphView.MaxNodes")); //$NON-NLS-1$ limit = new Spinner(sh, SWT.BORDER); limit.setMaximum(5000); limit.setSelection(g.getMaxNodes()); limit.setLayoutData(new GridData(SWT.CENTER, SWT.DEFAULT, true, false)); Label bufferLabel = new Label(sh, SWT.NONE); bufferLabel.setLayoutData(new GridData(SWT.CENTER, SWT.DEFAULT, true, false)); bufferLabel.setText(Messages.getString("CallgraphView.MaxDepth")); //$NON-NLS-1$ buffer = new Spinner(sh, SWT.BORDER); buffer.setMaximum(5000); buffer.setSelection(g.getLevelBuffer()); buffer.setLayoutData(new GridData(SWT.CENTER, SWT.DEFAULT, true, false)); Button setLimit = new Button(sh, SWT.PUSH); setLimit.setText(Messages.getString("CallgraphView.SetValues")); //$NON-NLS-1$ setLimit.setLayoutData(new GridData(SWT.CENTER, SWT.DEFAULT, true, false)); setLimit.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { boolean redraw = false; if (limit.getSelection() >= 0 && buffer.getSelection() >= 0) { g.setMaxNodes(limit.getSelection()); g.setLevelBuffer(buffer.getSelection()); if (g.changeLevelLimits(g.getLevelOfNode(g.getRootVisibleNodeNumber()))) { SystemTapUIErrorMessages mess = new SystemTapUIErrorMessages( Messages.getString("CallgraphView.BufferTooHigh"), Messages.getString("CallgraphView.BufferTooHigh"), //$NON-NLS-1$ //$NON-NLS-2$ Messages.getString("CallgraphView.BufferMessage1") + //$NON-NLS-1$ Messages.getString("CallgraphView.BufferMessage2") + //$NON-NLS-1$ Messages.getString("CallgraphView.BufferMessage3") + //$NON-NLS-1$ Messages.getString("CallgraphView.BufferMessage4") + g.getLevelBuffer() + //$NON-NLS-1$ Messages.getString("CallgraphView.BufferMessage5") + PluginConstants.NEW_LINE + PluginConstants.NEW_LINE + //$NON-NLS-1$ Messages.getString("CallgraphView.BufferMessage6") + //$NON-NLS-1$ Messages.getString("CallgraphView.BufferMessage7")); //$NON-NLS-1$ mess.schedule(); } redraw = true; } sh.dispose(); if (redraw) { g.draw(); } } }); sh.open(); } }; } /** * Convenience method for creating all the various actions */ private void createActions() { createViewActions(); createAnimateActions(); createMarkerActions(); createMovementActions(); modeCollapsedNodes.setChecked(true); } private void createMovementActions() { gotoNext = new Action(Messages.getString("CallgraphView.Next")) { //$NON-NLS-1$ @Override public void run() { g.drawNextNode(); } }; gotoPrevious = new Action(Messages.getString("CallgraphView.Previous")) { //$NON-NLS-1$ @Override public void run() { if (g.isCollapseMode()) { g.setCollapseMode(false); } int toDraw = g.getPreviousCalledNode(g.getRootVisibleNodeNumber()); if (toDraw != -1) g.draw(toDraw); } }; gotoLast = new Action(Messages.getString("CallgraphView.Last")) { //$NON-NLS-1$ @Override public void run() { if (g.isCollapseMode()) g.setCollapseMode(false); g.draw(g.getLastFunctionCalled()); } }; play = new Action(Messages.getString("CallgraphView.Play")) { //$NON-NLS-1$ @Override public void run() { if (g.getDrawMode() != StapGraph.CONSTANT_DRAWMODE_AGGREGATE) { g.play(); togglePlayImage(); } } }; play.setImageDescriptor(playImage); } /** * Toggles the play/pause image * @param play */ private void togglePlayImage() { if (play.getToolTipText() == Messages.getString("CallgraphView.Pause")) { //$NON-NLS-1$ play.setImageDescriptor(playImage); play.setToolTipText(Messages.getString("CallgraphView.Play")); //$NON-NLS-1$ } else { play.setImageDescriptor(pauseImage); play.setToolTipText(""); //$NON-NLS-1$ } } private void createMarkerActions() { markersNext = new Action(Messages.getString("CallgraphView.nextMarker")) { //$NON-NLS-1$ @Override public void run() { g.draw(g.getNextMarkedNode()); } }; markersPrevious = new Action(Messages.getString("CallgraphView.previousMarker")) { //$NON-NLS-1$ @Override public void run() { g.draw(g.getPreviousMarkedNode()); } }; } @Override protected boolean createOpenAction() { //Opens from specified location openFile = new Action(Messages.getString("CallgraphView.Open")){ //$NON-NLS-1$ @Override public void run(){ FileDialog dialog = new FileDialog(new Shell(), SWT.DEFAULT); String filePath = dialog.open(); if (filePath != null){ StapGraphParser new_parser = new StapGraphParser(); new_parser.setSourcePath(filePath); new_parser.setViewID(CallGraphConstants.VIEW_ID); new_parser.schedule(); } } }; return true; } @Override protected boolean createOpenDefaultAction() { //Opens from the default location openDefault = new Action(Messages.getString("CallgraphView.OpenLastRun")){ //$NON-NLS-1$ @Override public void run(){ StapGraphParser new_parser = new StapGraphParser(); new_parser.setViewID(CallGraphConstants.VIEW_ID); new_parser.schedule(); } }; return true; } @Override public boolean setParser(SystemTapParser newParser) { if (newParser instanceof StapGraphParser) { parser = (StapGraphParser) newParser; return true; } return false; } @Override public void setViewID() { viewID = "org.eclipse.linuxtools.callgraph.callgraphview"; //$NON-NLS-1$ } public Action getAnimationSlow() { return animationSlow; } public Action getAnimationFast() { return animationFast; } public Action getModeCollapsednodes() { return modeCollapsedNodes; } public Action getViewRefresh() { return viewRefresh; } public Action getGotoNext() { return gotoNext; } public Action getGotoPrevious() { return gotoPrevious; } public Action getGotoLast() { return gotoLast; } public Action getViewTreeview() { return viewTreeview; } public Action getViewRadialview() { return viewRadialview; } public Action getViewAggregateview() { return viewAggregateview; } public Action getViewLevelview() { return viewLevelview; } public Action getPlay() { return play; } public StapGraph getGraph() { return g; } @Override public void setFocus() { if(masterComposite != null){ masterComposite.setFocus(); } } @Override public void updateMethod() { IProgressMonitor m = new NullProgressMonitor(); m.beginTask("Updating callgraph", 4); //$NON-NLS-1$ loadData(m); m.worked(1); if (parser.totalTime > 0) { finishLoad(m); } m.worked(1); g.draw(StapGraph.CONSTANT_DRAWMODE_RADIAL, StapGraph.CONSTANT_ANIMATION_SLOW, g.getFirstUsefulNode()); } @Override public SystemTapParser getParser() { return parser; } private void writeToDot(boolean mode, Set<Integer> keySet) { Shell sh = new Shell(); FileDialog dialog = new FileDialog(sh, SWT.SAVE); String filePath = dialog.open(); if (filePath != null) { File f = new File(filePath); f.delete(); try { f.createNewFile(); } catch (IOException e) { return; } try (BufferedWriter out = new BufferedWriter(new FileWriter(f))) { StringBuilder build = new StringBuilder(""); //$NON-NLS-1$ out.write("digraph stapgraph {\n"); //$NON-NLS-1$ for (int i : keySet) { if (i == 0) { continue; } StapData d = g.getNodeData(i); if ( (d.isCollapsed != mode) && !d.isOnlyChildWithThisName()) { continue; } build.append(i + " [label=\"" + d.name + " " ); //$NON-NLS-1$ //$NON-NLS-2$ build.append(StapNode.numberFormat.format((float) d.getTime()/g.getTotalTime() * 100) + "%\"]\n"); //$NON-NLS-1$ int j = d.parent; if (mode) { j = d.collapsedParent; } if (!keySet.contains(j) || j == 0) { continue; } String called = mode ? " [label=\"" + g.getNodeData(i).timesCalled + "\"]\n" : "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ build.append( j + "->" + i ); //$NON-NLS-1$ build.append( called ); out.write(build.toString()); build.setLength(0); } out.write("}"); //$NON-NLS-1$ out.flush(); } catch (IOException e) { e.printStackTrace(); } } } private static ImageDescriptor getImageDescriptor(String path) { return AbstractUIPlugin.imageDescriptorFromPlugin(CallGraphConstants.PLUGIN_ID, path); } }