1 /*******************************************************************************
2 * Copyright (c) 2016 Ericsson
4 * All rights reserved. This program and the accompanying materials are made
5 * available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
11 *******************************************************************************/
12 package org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.flamegraph
;
14 import java
.util
.concurrent
.Semaphore
;
16 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
17 import org
.eclipse
.core
.runtime
.IStatus
;
18 import org
.eclipse
.core
.runtime
.Status
;
19 import org
.eclipse
.core
.runtime
.jobs
.Job
;
20 import org
.eclipse
.jdt
.annotation
.NonNull
;
21 import org
.eclipse
.jface
.action
.Action
;
22 import org
.eclipse
.jface
.action
.GroupMarker
;
23 import org
.eclipse
.jface
.action
.IAction
;
24 import org
.eclipse
.jface
.action
.IMenuListener
;
25 import org
.eclipse
.jface
.action
.IMenuManager
;
26 import org
.eclipse
.jface
.action
.IToolBarManager
;
27 import org
.eclipse
.jface
.action
.MenuManager
;
28 import org
.eclipse
.jface
.action
.Separator
;
29 import org
.eclipse
.jface
.dialogs
.IDialogSettings
;
30 import org
.eclipse
.jface
.resource
.ImageDescriptor
;
31 import org
.eclipse
.jface
.viewers
.ISelection
;
32 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
33 import org
.eclipse
.swt
.SWT
;
34 import org
.eclipse
.swt
.events
.MenuDetectEvent
;
35 import org
.eclipse
.swt
.events
.MenuDetectListener
;
36 import org
.eclipse
.swt
.events
.MouseAdapter
;
37 import org
.eclipse
.swt
.events
.MouseEvent
;
38 import org
.eclipse
.swt
.widgets
.Composite
;
39 import org
.eclipse
.swt
.widgets
.Display
;
40 import org
.eclipse
.swt
.widgets
.Menu
;
41 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.core
.callgraph
.CallGraphAnalysis
;
42 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.Activator
;
43 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.callgraph
.CallGraphAnalysisUI
;
44 import org
.eclipse
.tracecompass
.segmentstore
.core
.ISegment
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSelectionRangeUpdatedSignal
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
47 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalManager
;
48 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceClosedSignal
;
49 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceSelectedSignal
;
50 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestamp
;
51 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
52 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
53 import org
.eclipse
.tracecompass
.tmf
.ui
.editors
.ITmfTraceEditor
;
54 import org
.eclipse
.tracecompass
.tmf
.ui
.symbols
.TmfSymbolProviderUpdatedSignal
;
55 import org
.eclipse
.tracecompass
.tmf
.ui
.views
.TmfView
;
56 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.TimeGraphPresentationProvider
;
57 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.TimeGraphViewer
;
58 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.widgets
.TimeGraphControl
;
59 import org
.eclipse
.ui
.IActionBars
;
60 import org
.eclipse
.ui
.IEditorPart
;
61 import org
.eclipse
.ui
.IWorkbenchActionConstants
;
63 import com
.google
.common
.annotations
.VisibleForTesting
;
66 * View to display the flame graph .This uses the flameGraphNode tree generated
67 * by CallGraphAnalysisUI.
69 * @author Sonia Farrah
71 public class FlameGraphView
extends TmfView
{
76 public static final String ID
= FlameGraphView
.class.getPackage().getName() + ".flamegraphView"; //$NON-NLS-1$
78 private static final String SORT_OPTION_KEY
= "sort.option"; //$NON-NLS-1$
79 private static final String CONTENT_PRESENTATION_OPTION_KEY
= "presentation.option"; //$NON-NLS-1$
80 private static final ImageDescriptor SORT_BY_NAME_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha.gif"); //$NON-NLS-1$
81 private static final ImageDescriptor SORT_BY_NAME_REV_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha_rev.gif"); //$NON-NLS-1$
82 private static final ImageDescriptor SORT_BY_ID_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_num.gif"); //$NON-NLS-1$
83 private static final ImageDescriptor SORT_BY_ID_REV_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_num_rev.gif"); //$NON-NLS-1$
85 private final Action VIEW_BY_THREAD
= new Action(Messages
.FlameGraph_ShowPerThreads
, IAction
.AS_RADIO_BUTTON
) {
88 if (fContentPresentation
!= ContentPresentation
.BY_THREAD
) {
89 buildFlameGraph(fFlamegraphModule
);
90 fContentPresentation
= ContentPresentation
.BY_THREAD
;
91 saveContentPresentationOption(fContentPresentation
);
96 private final Action VIEW_AGGREGATE
= new Action(Messages
.FlameGraph_AggregateByThread
, IAction
.AS_RADIO_BUTTON
) {
99 if (fContentPresentation
!= ContentPresentation
.AGGREGATE_THREADS
) {
100 buildFlameGraph(fFlamegraphModule
);
101 fContentPresentation
= ContentPresentation
.AGGREGATE_THREADS
;
102 saveContentPresentationOption(fContentPresentation
);
107 private volatile ContentPresentation fContentPresentation
= ContentPresentation
.AGGREGATE_THREADS
;
109 private TimeGraphViewer fTimeGraphViewer
;
111 private FlameGraphContentProvider fTimeGraphContentProvider
;
113 private TimeGraphPresentationProvider fPresentationProvider
;
115 private ITmfTrace fTrace
;
117 private final @NonNull MenuManager fEventMenuManager
= new MenuManager();
119 * A plain old semaphore is used since different threads will be competing
120 * for the same resource.
123 private final Semaphore fLock
= new Semaphore(1);
125 private Action fSortByNameAction
;
126 private Action fSortByIdAction
;
129 private CallGraphAnalysis fFlamegraphModule
= null;
134 public FlameGraphView() {
139 public void createPartControl(Composite parent
) {
140 super.createPartControl(parent
);
141 fTimeGraphViewer
= new TimeGraphViewer(parent
, SWT
.NONE
);
142 fTimeGraphContentProvider
= new FlameGraphContentProvider();
143 fPresentationProvider
= new FlameGraphPresentationProvider();
144 fTimeGraphViewer
.setTimeGraphContentProvider(fTimeGraphContentProvider
);
145 fTimeGraphViewer
.setTimeGraphProvider(fPresentationProvider
);
146 IEditorPart editor
= getSite().getPage().getActiveEditor();
147 if (editor
instanceof ITmfTraceEditor
) {
148 ITmfTrace trace
= ((ITmfTraceEditor
) editor
).getTrace();
150 traceSelected(new TmfTraceSelectedSignal(this, trace
));
153 contributeToActionBars();
155 loadContentPresentationOption();
156 TmfSignalManager
.register(this);
157 getSite().setSelectionProvider(fTimeGraphViewer
.getSelectionProvider());
158 IMenuManager menuManager
= getViewSite().getActionBars().getMenuManager();
159 MenuManager item
= new MenuManager(Messages
.FlameGraphView_ContentPresentation
);
160 item
.setRemoveAllWhenShown(true);
161 item
.addMenuListener(new IMenuListener() {
164 public void menuAboutToShow(IMenuManager manager
) {
165 item
.add(VIEW_BY_THREAD
);
166 item
.add(VIEW_AGGREGATE
);
169 menuManager
.add(item
);
170 createTimeEventContextMenu();
171 fTimeGraphViewer
.getTimeGraphControl().addMouseListener(new MouseAdapter() {
173 public void mouseDoubleClick(MouseEvent e
) {
174 TimeGraphControl timeGraphControl
= getTimeGraphViewer().getTimeGraphControl();
175 ISelection selection
= timeGraphControl
.getSelection();
176 if (selection
instanceof IStructuredSelection
) {
177 for (Object object
: ((IStructuredSelection
) selection
).toList()) {
178 if (object
instanceof FlamegraphEvent
) {
179 FlamegraphEvent event
= (FlamegraphEvent
) object
;
180 long startTime
= event
.getTime();
181 long endTime
= startTime
+ event
.getDuration();
182 getTimeGraphViewer().setStartFinishTimeNotify(startTime
, endTime
);
192 * Get the time graph viewer
194 * @return the time graph viewer
197 public TimeGraphViewer
getTimeGraphViewer() {
198 return fTimeGraphViewer
;
202 * Handler for the trace selected signal
205 * The incoming signal
208 public void traceSelected(final TmfTraceSelectedSignal signal
) {
209 fTrace
= signal
.getTrace();
210 if (fTrace
!= null) {
211 fFlamegraphModule
= TmfTraceUtils
.getAnalysisModuleOfClass(fTrace
, CallGraphAnalysis
.class, CallGraphAnalysisUI
.ID
);
212 buildFlameGraph(fFlamegraphModule
);
217 * Get the necessary data for the flame graph and display it
219 * @param callGraphAnalysis
220 * the callGraphAnalysis
223 public void buildFlameGraph(CallGraphAnalysis callGraphAnalysis
) {
225 * Note for synchronization:
227 * Acquire the lock at entry. then we have 4 places to release it
229 * 1- if the lock failed
231 * 2- if the data is null and we have no UI to update
233 * 3- if the request is cancelled before it gets to the display
235 * 4- on a clean execution
243 } catch (InterruptedException e
) {
244 Activator
.getDefault().logError(e
.getMessage(), e
);
247 if (callGraphAnalysis
== null) {
248 fTimeGraphViewer
.setInput(null);
252 fTimeGraphViewer
.setInput(fContentPresentation
== ContentPresentation
.BY_THREAD ? callGraphAnalysis
.getThreadNodes() : callGraphAnalysis
.getFlameGraph());
253 callGraphAnalysis
.schedule();
254 job
= new Job(Messages
.CallGraphAnalysis_Execution
) {
257 protected IStatus
run(IProgressMonitor monitor
) {
259 if (monitor
.isCanceled()) {
260 return Status
.CANCEL_STATUS
;
262 callGraphAnalysis
.waitForCompletion(monitor
);
263 Display
.getDefault().asyncExec(() -> {
264 fTimeGraphViewer
.setInput(fContentPresentation
== ContentPresentation
.BY_THREAD ? callGraphAnalysis
.getThreadNodes() : callGraphAnalysis
.getFlameGraph());
265 fTimeGraphViewer
.resetStartFinishTime();
267 return Status
.OK_STATUS
;
279 * Await the next refresh
281 * @throws InterruptedException
282 * something took too long
285 public void waitForUpdate() throws InterruptedException
{
287 * wait for the semaphore to be available, then release it immediately
294 * Trace is closed: clear the data structures and the view
297 * the signal received
300 public void traceClosed(final TmfTraceClosedSignal signal
) {
301 if (signal
.getTrace() == fTrace
) {
302 fTimeGraphViewer
.setInput(null);
307 public void setFocus() {
308 fTimeGraphViewer
.setFocus();
311 // ------------------------------------------------------------------------
313 // ------------------------------------------------------------------------
315 private void createTimeEventContextMenu() {
316 fEventMenuManager
.setRemoveAllWhenShown(true);
317 TimeGraphControl timeGraphControl
= fTimeGraphViewer
.getTimeGraphControl();
318 final Menu timeEventMenu
= fEventMenuManager
.createContextMenu(timeGraphControl
);
320 timeGraphControl
.addTimeGraphEntryMenuListener(new MenuDetectListener() {
322 public void menuDetected(MenuDetectEvent event
) {
324 * The TimeGraphControl will call the TimeGraphEntryMenuListener
325 * before the TimeEventMenuListener. We need to clear the menu
326 * for the case the selection was done on the namespace where
327 * the time event listener below won't be called afterwards.
329 timeGraphControl
.setMenu(null);
333 timeGraphControl
.addTimeEventMenuListener(new MenuDetectListener() {
335 public void menuDetected(MenuDetectEvent event
) {
336 Menu menu
= timeEventMenu
;
337 if (event
.data
instanceof FlamegraphEvent
) {
338 timeGraphControl
.setMenu(menu
);
341 timeGraphControl
.setMenu(null);
346 fEventMenuManager
.addMenuListener(new IMenuListener() {
348 public void menuAboutToShow(IMenuManager manager
) {
349 fillTimeEventContextMenu(fEventMenuManager
);
350 fEventMenuManager
.add(new GroupMarker(IWorkbenchActionConstants
.MB_ADDITIONS
));
353 getSite().registerContextMenu(fEventMenuManager
, fTimeGraphViewer
.getSelectionProvider());
360 * a menuManager to fill
362 protected void fillTimeEventContextMenu(@NonNull IMenuManager menuManager
) {
363 ISelection selection
= getSite().getSelectionProvider().getSelection();
364 if (selection
instanceof IStructuredSelection
) {
365 for (Object object
: ((IStructuredSelection
) selection
).toList()) {
366 if (object
instanceof FlamegraphEvent
) {
367 final FlamegraphEvent flamegraphEvent
= (FlamegraphEvent
) object
;
368 menuManager
.add(new Action(Messages
.FlameGraphView_GotoMaxDuration
) {
371 ISegment maxSeg
= flamegraphEvent
.getStatistics().getDurationStatistics().getMaxObject();
372 if (maxSeg
== null) {
375 TmfSelectionRangeUpdatedSignal sig
= new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp
.fromNanos(maxSeg
.getStart()), TmfTimestamp
.fromNanos(maxSeg
.getEnd()));
380 menuManager
.add(new Action(Messages
.FlameGraphView_GotoMinDuration
) {
383 ISegment minSeg
= flamegraphEvent
.getStatistics().getDurationStatistics().getMinObject();
384 if (minSeg
== null) {
387 TmfSelectionRangeUpdatedSignal sig
= new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp
.fromNanos(minSeg
.getStart()), TmfTimestamp
.fromNanos(minSeg
.getEnd()));
396 private void contributeToActionBars() {
397 IActionBars bars
= getViewSite().getActionBars();
398 fillLocalToolBar(bars
.getToolBarManager());
401 private void fillLocalToolBar(IToolBarManager manager
) {
402 manager
.add(getSortByNameAction());
403 manager
.add(getSortByIdAction());
404 manager
.add(new Separator());
407 private Action
getSortByNameAction() {
408 if (fSortByNameAction
== null) {
409 fSortByNameAction
= new Action(Messages
.FlameGraph_SortByThreadName
, IAction
.AS_CHECK_BOX
) {
412 SortOption sortOption
= fTimeGraphContentProvider
.getSortOption();
413 if (sortOption
== SortOption
.BY_NAME
) {
414 setSortOption(SortOption
.BY_NAME_REV
);
416 setSortOption(SortOption
.BY_NAME
);
420 fSortByNameAction
.setToolTipText(Messages
.FlameGraph_SortByThreadName
);
421 fSortByNameAction
.setImageDescriptor(SORT_BY_NAME_ICON
);
423 return fSortByNameAction
;
426 private Action
getSortByIdAction() {
427 if (fSortByIdAction
== null) {
428 fSortByIdAction
= new Action(Messages
.FlameGraph_SortByThreadId
, IAction
.AS_CHECK_BOX
) {
431 SortOption sortOption
= fTimeGraphContentProvider
.getSortOption();
432 if (sortOption
== SortOption
.BY_ID
) {
433 setSortOption(SortOption
.BY_ID_REV
);
435 setSortOption(SortOption
.BY_ID
);
439 fSortByIdAction
.setToolTipText(Messages
.FlameGraph_SortByThreadId
);
440 fSortByIdAction
.setImageDescriptor(SORT_BY_ID_ICON
);
442 return fSortByIdAction
;
445 private void setSortOption(SortOption sortOption
) {
447 getSortByNameAction().setChecked(false);
448 getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON
);
449 getSortByIdAction().setChecked(false);
450 getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON
);
452 if (sortOption
.equals(SortOption
.BY_NAME
)) {
453 fTimeGraphContentProvider
.setSortOption(SortOption
.BY_NAME
);
454 getSortByNameAction().setChecked(true);
455 } else if (sortOption
.equals(SortOption
.BY_NAME_REV
)) {
456 fTimeGraphContentProvider
.setSortOption(SortOption
.BY_NAME_REV
);
457 getSortByNameAction().setChecked(true);
458 getSortByNameAction().setImageDescriptor(SORT_BY_NAME_REV_ICON
);
459 } else if (sortOption
.equals(SortOption
.BY_ID
)) {
460 fTimeGraphContentProvider
.setSortOption(SortOption
.BY_ID
);
461 getSortByIdAction().setChecked(true);
462 } else if (sortOption
.equals(SortOption
.BY_ID_REV
)) {
463 fTimeGraphContentProvider
.setSortOption(SortOption
.BY_ID_REV
);
464 getSortByIdAction().setChecked(true);
465 getSortByIdAction().setImageDescriptor(SORT_BY_ID_REV_ICON
);
468 fTimeGraphViewer
.refresh();
471 private void saveSortOption() {
472 SortOption sortOption
= fTimeGraphContentProvider
.getSortOption();
473 IDialogSettings settings
= Activator
.getDefault().getDialogSettings();
474 IDialogSettings section
= settings
.getSection(getClass().getName());
475 if (section
== null) {
476 section
= settings
.addNewSection(getClass().getName());
478 section
.put(SORT_OPTION_KEY
, sortOption
.name());
481 private void loadSortOption() {
482 IDialogSettings settings
= Activator
.getDefault().getDialogSettings();
483 IDialogSettings section
= settings
.getSection(getClass().getName());
484 if (section
== null) {
487 String sortOption
= section
.get(SORT_OPTION_KEY
);
488 if (sortOption
== null) {
491 setSortOption(SortOption
.fromName(sortOption
));
494 private void saveContentPresentationOption(ContentPresentation contentPresentation
) {
495 IDialogSettings settings
= Activator
.getDefault().getDialogSettings();
496 IDialogSettings section
= settings
.getSection(getClass().getName());
497 if (section
== null) {
498 section
= settings
.addNewSection(getClass().getName());
500 section
.put(CONTENT_PRESENTATION_OPTION_KEY
, contentPresentation
.name());
503 private void loadContentPresentationOption() {
504 IDialogSettings settings
= Activator
.getDefault().getDialogSettings();
505 IDialogSettings section
= settings
.getSection(getClass().getName());
506 ContentPresentation contentPresentation
= fContentPresentation
;
507 if (section
!= null) {
508 String contentPresentationOption
= section
.get(CONTENT_PRESENTATION_OPTION_KEY
);
509 if (contentPresentationOption
!= null) {
510 contentPresentation
= ContentPresentation
.fromName(contentPresentationOption
);
513 VIEW_BY_THREAD
.setChecked(contentPresentation
== ContentPresentation
.BY_THREAD
);
514 VIEW_AGGREGATE
.setChecked(contentPresentation
== ContentPresentation
.AGGREGATE_THREADS
);
515 fContentPresentation
= contentPresentation
;
519 * Symbol map provider updated
525 public void symbolMapUpdated(TmfSymbolProviderUpdatedSignal signal
) {
526 if (signal
.getSource() != this) {
527 fTimeGraphViewer
.refresh();