2da1dba663a7a80f7e7f0a13e3798a1c5d749599
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.timing.ui / src / org / eclipse / tracecompass / internal / analysis / timing / ui / flamegraph / FlameGraphView.java
1 /*******************************************************************************
2 * Copyright (c) 2016 Ericsson
3 *
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
8 *
9 * Author:
10 * Sonia Farrah
11 *******************************************************************************/
12 package org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph;
13
14 import java.util.concurrent.Semaphore;
15
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;
62
63 import com.google.common.annotations.VisibleForTesting;
64
65 /**
66 * View to display the flame graph .This uses the flameGraphNode tree generated
67 * by CallGraphAnalysisUI.
68 *
69 * @author Sonia Farrah
70 */
71 public class FlameGraphView extends TmfView {
72
73 /**
74 *
75 */
76 public static final String ID = FlameGraphView.class.getPackage().getName() + ".flamegraphView"; //$NON-NLS-1$
77
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$
84
85 private final Action VIEW_BY_THREAD = new Action(Messages.FlameGraph_ShowPerThreads, IAction.AS_RADIO_BUTTON) {
86 @Override
87 public void run() {
88 if (fContentPresentation != ContentPresentation.BY_THREAD) {
89 buildFlameGraph(fFlamegraphModule);
90 fContentPresentation = ContentPresentation.BY_THREAD;
91 saveContentPresentationOption(fContentPresentation);
92 }
93 }
94 };
95
96 private final Action VIEW_AGGREGATE = new Action(Messages.FlameGraph_AggregateByThread, IAction.AS_RADIO_BUTTON) {
97 @Override
98 public void run() {
99 if (fContentPresentation != ContentPresentation.AGGREGATE_THREADS) {
100 buildFlameGraph(fFlamegraphModule);
101 fContentPresentation = ContentPresentation.AGGREGATE_THREADS;
102 saveContentPresentationOption(fContentPresentation);
103 }
104 }
105 };
106
107 private volatile ContentPresentation fContentPresentation = ContentPresentation.AGGREGATE_THREADS;
108
109 private TimeGraphViewer fTimeGraphViewer;
110
111 private FlameGraphContentProvider fTimeGraphContentProvider;
112
113 private TimeGraphPresentationProvider fPresentationProvider;
114
115 private ITmfTrace fTrace;
116
117 private final @NonNull MenuManager fEventMenuManager = new MenuManager();
118 /**
119 * A plain old semaphore is used since different threads will be competing
120 * for the same resource.
121 */
122
123 private final Semaphore fLock = new Semaphore(1);
124
125 private Action fSortByNameAction;
126 private Action fSortByIdAction;
127 private Job fJob;
128
129 private CallGraphAnalysis fFlamegraphModule = null;
130
131 /**
132 * Constructor
133 */
134 public FlameGraphView() {
135 super(ID);
136 }
137
138 @Override
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();
149 if (trace != null) {
150 traceSelected(new TmfTraceSelectedSignal(this, trace));
151 }
152 }
153 contributeToActionBars();
154 loadSortOption();
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() {
162
163 @Override
164 public void menuAboutToShow(IMenuManager manager) {
165 item.add(VIEW_BY_THREAD);
166 item.add(VIEW_AGGREGATE);
167 }
168 });
169 menuManager.add(item);
170 createTimeEventContextMenu();
171 fTimeGraphViewer.getTimeGraphControl().addMouseListener(new MouseAdapter() {
172 @Override
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);
183 break;
184 }
185 }
186 }
187 }
188 });
189 }
190
191 /**
192 * Get the time graph viewer
193 *
194 * @return the time graph viewer
195 */
196 @VisibleForTesting
197 public TimeGraphViewer getTimeGraphViewer() {
198 return fTimeGraphViewer;
199 }
200
201 /**
202 * Handler for the trace selected signal
203 *
204 * @param signal
205 * The incoming signal
206 */
207 @TmfSignalHandler
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);
213 }
214 }
215
216 /**
217 * Get the necessary data for the flame graph and display it
218 *
219 * @param callGraphAnalysis
220 * the callGraphAnalysis
221 */
222 @VisibleForTesting
223 public void buildFlameGraph(CallGraphAnalysis callGraphAnalysis) {
224 /*
225 * Note for synchronization:
226 *
227 * Acquire the lock at entry. then we have 4 places to release it
228 *
229 * 1- if the lock failed
230 *
231 * 2- if the data is null and we have no UI to update
232 *
233 * 3- if the request is cancelled before it gets to the display
234 *
235 * 4- on a clean execution
236 */
237 Job job = fJob;
238 if (job != null) {
239 job.cancel();
240 }
241 try {
242 fLock.acquire();
243 } catch (InterruptedException e) {
244 Activator.getDefault().logError(e.getMessage(), e);
245 fLock.release();
246 }
247 if (callGraphAnalysis == null) {
248 fTimeGraphViewer.setInput(null);
249 fLock.release();
250 return;
251 }
252 fTimeGraphViewer.setInput(fContentPresentation == ContentPresentation.BY_THREAD ? callGraphAnalysis.getThreadNodes() : callGraphAnalysis.getFlameGraph());
253 callGraphAnalysis.schedule();
254 job = new Job(Messages.CallGraphAnalysis_Execution) {
255
256 @Override
257 protected IStatus run(IProgressMonitor monitor) {
258 try {
259 if (monitor.isCanceled()) {
260 return Status.CANCEL_STATUS;
261 }
262 callGraphAnalysis.waitForCompletion(monitor);
263 Display.getDefault().asyncExec(() -> {
264 fTimeGraphViewer.setInput(fContentPresentation == ContentPresentation.BY_THREAD ? callGraphAnalysis.getThreadNodes() : callGraphAnalysis.getFlameGraph());
265 fTimeGraphViewer.resetStartFinishTime();
266 });
267 return Status.OK_STATUS;
268 } finally {
269 fJob = null;
270 fLock.release();
271 }
272 }
273 };
274 fJob = job;
275 job.schedule();
276 }
277
278 /**
279 * Await the next refresh
280 *
281 * @throws InterruptedException
282 * something took too long
283 */
284 @VisibleForTesting
285 public void waitForUpdate() throws InterruptedException {
286 /*
287 * wait for the semaphore to be available, then release it immediately
288 */
289 fLock.acquire();
290 fLock.release();
291 }
292
293 /**
294 * Trace is closed: clear the data structures and the view
295 *
296 * @param signal
297 * the signal received
298 */
299 @TmfSignalHandler
300 public void traceClosed(final TmfTraceClosedSignal signal) {
301 if (signal.getTrace() == fTrace) {
302 fTimeGraphViewer.setInput(null);
303 }
304 }
305
306 @Override
307 public void setFocus() {
308 fTimeGraphViewer.setFocus();
309 }
310
311 // ------------------------------------------------------------------------
312 // Helper methods
313 // ------------------------------------------------------------------------
314
315 private void createTimeEventContextMenu() {
316 fEventMenuManager.setRemoveAllWhenShown(true);
317 TimeGraphControl timeGraphControl = fTimeGraphViewer.getTimeGraphControl();
318 final Menu timeEventMenu = fEventMenuManager.createContextMenu(timeGraphControl);
319
320 timeGraphControl.addTimeGraphEntryMenuListener(new MenuDetectListener() {
321 @Override
322 public void menuDetected(MenuDetectEvent event) {
323 /*
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.
328 */
329 timeGraphControl.setMenu(null);
330 event.doit = false;
331 }
332 });
333 timeGraphControl.addTimeEventMenuListener(new MenuDetectListener() {
334 @Override
335 public void menuDetected(MenuDetectEvent event) {
336 Menu menu = timeEventMenu;
337 if (event.data instanceof FlamegraphEvent) {
338 timeGraphControl.setMenu(menu);
339 return;
340 }
341 timeGraphControl.setMenu(null);
342 event.doit = false;
343 }
344 });
345
346 fEventMenuManager.addMenuListener(new IMenuListener() {
347 @Override
348 public void menuAboutToShow(IMenuManager manager) {
349 fillTimeEventContextMenu(fEventMenuManager);
350 fEventMenuManager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
351 }
352 });
353 getSite().registerContextMenu(fEventMenuManager, fTimeGraphViewer.getSelectionProvider());
354 }
355
356 /**
357 * Fill context menu
358 *
359 * @param menuManager
360 * a menuManager to fill
361 */
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) {
369 @Override
370 public void run() {
371 ISegment maxSeg = flamegraphEvent.getStatistics().getDurationStatistics().getMaxObject();
372 if (maxSeg == null) {
373 return;
374 }
375 TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(maxSeg.getStart()), TmfTimestamp.fromNanos(maxSeg.getEnd()));
376 broadcast(sig);
377 }
378 });
379
380 menuManager.add(new Action(Messages.FlameGraphView_GotoMinDuration) {
381 @Override
382 public void run() {
383 ISegment minSeg = flamegraphEvent.getStatistics().getDurationStatistics().getMinObject();
384 if (minSeg == null) {
385 return;
386 }
387 TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(minSeg.getStart()), TmfTimestamp.fromNanos(minSeg.getEnd()));
388 broadcast(sig);
389 }
390 });
391 }
392 }
393 }
394 }
395
396 private void contributeToActionBars() {
397 IActionBars bars = getViewSite().getActionBars();
398 fillLocalToolBar(bars.getToolBarManager());
399 }
400
401 private void fillLocalToolBar(IToolBarManager manager) {
402 manager.add(getSortByNameAction());
403 manager.add(getSortByIdAction());
404 manager.add(new Separator());
405 }
406
407 private Action getSortByNameAction() {
408 if (fSortByNameAction == null) {
409 fSortByNameAction = new Action(Messages.FlameGraph_SortByThreadName, IAction.AS_CHECK_BOX) {
410 @Override
411 public void run() {
412 SortOption sortOption = fTimeGraphContentProvider.getSortOption();
413 if (sortOption == SortOption.BY_NAME) {
414 setSortOption(SortOption.BY_NAME_REV);
415 } else {
416 setSortOption(SortOption.BY_NAME);
417 }
418 }
419 };
420 fSortByNameAction.setToolTipText(Messages.FlameGraph_SortByThreadName);
421 fSortByNameAction.setImageDescriptor(SORT_BY_NAME_ICON);
422 }
423 return fSortByNameAction;
424 }
425
426 private Action getSortByIdAction() {
427 if (fSortByIdAction == null) {
428 fSortByIdAction = new Action(Messages.FlameGraph_SortByThreadId, IAction.AS_CHECK_BOX) {
429 @Override
430 public void run() {
431 SortOption sortOption = fTimeGraphContentProvider.getSortOption();
432 if (sortOption == SortOption.BY_ID) {
433 setSortOption(SortOption.BY_ID_REV);
434 } else {
435 setSortOption(SortOption.BY_ID);
436 }
437 }
438 };
439 fSortByIdAction.setToolTipText(Messages.FlameGraph_SortByThreadId);
440 fSortByIdAction.setImageDescriptor(SORT_BY_ID_ICON);
441 }
442 return fSortByIdAction;
443 }
444
445 private void setSortOption(SortOption sortOption) {
446 // reset defaults
447 getSortByNameAction().setChecked(false);
448 getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON);
449 getSortByIdAction().setChecked(false);
450 getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON);
451
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);
466 }
467 saveSortOption();
468 fTimeGraphViewer.refresh();
469 }
470
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());
477 }
478 section.put(SORT_OPTION_KEY, sortOption.name());
479 }
480
481 private void loadSortOption() {
482 IDialogSettings settings = Activator.getDefault().getDialogSettings();
483 IDialogSettings section = settings.getSection(getClass().getName());
484 if (section == null) {
485 return;
486 }
487 String sortOption = section.get(SORT_OPTION_KEY);
488 if (sortOption == null) {
489 return;
490 }
491 setSortOption(SortOption.fromName(sortOption));
492 }
493
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());
499 }
500 section.put(CONTENT_PRESENTATION_OPTION_KEY, contentPresentation.name());
501 }
502
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);
511 }
512 }
513 VIEW_BY_THREAD.setChecked(contentPresentation == ContentPresentation.BY_THREAD);
514 VIEW_AGGREGATE.setChecked(contentPresentation == ContentPresentation.AGGREGATE_THREADS);
515 fContentPresentation = contentPresentation;
516 }
517
518 /**
519 * Symbol map provider updated
520 *
521 * @param signal
522 * the signal
523 */
524 @TmfSignalHandler
525 public void symbolMapUpdated(TmfSymbolProviderUpdatedSignal signal) {
526 if (signal.getSource() != this) {
527 fTimeGraphViewer.refresh();
528 }
529 }
530
531 }
This page took 0.042196 seconds and 4 git commands to generate.