tmf.ui: Introduce TmfSymbolMapUpdatedSignal
[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 ImageDescriptor SORT_BY_NAME_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha.gif"); //$NON-NLS-1$
80 private static final ImageDescriptor SORT_BY_NAME_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha_rev.gif"); //$NON-NLS-1$
81 private static final ImageDescriptor SORT_BY_ID_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_num.gif"); //$NON-NLS-1$
82 private static final ImageDescriptor SORT_BY_ID_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_num_rev.gif"); //$NON-NLS-1$
83
84 private TimeGraphViewer fTimeGraphViewer;
85
86 private FlameGraphContentProvider fTimeGraphContentProvider;
87
88 private TimeGraphPresentationProvider fPresentationProvider;
89
90 private ITmfTrace fTrace;
91
92 private final @NonNull MenuManager fEventMenuManager = new MenuManager();
93 private Action fSortByNameAction;
94 private Action fSortByIdAction;
95 /**
96 * A plain old semaphore is used since different threads will be competing
97 * for the same resource.
98 */
99 private final Semaphore fLock = new Semaphore(1);
100
101 /**
102 * Constructor
103 */
104 public FlameGraphView() {
105 super(ID);
106 }
107
108 @Override
109 public void createPartControl(Composite parent) {
110 super.createPartControl(parent);
111 fTimeGraphViewer = new TimeGraphViewer(parent, SWT.NONE);
112 fTimeGraphContentProvider = new FlameGraphContentProvider();
113 fPresentationProvider = new FlameGraphPresentationProvider();
114 fTimeGraphViewer.setTimeGraphContentProvider(fTimeGraphContentProvider);
115 fTimeGraphViewer.setTimeGraphProvider(fPresentationProvider);
116 IEditorPart editor = getSite().getPage().getActiveEditor();
117 if (editor instanceof ITmfTraceEditor) {
118 ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace();
119 if (trace != null) {
120 traceSelected(new TmfTraceSelectedSignal(this, trace));
121 }
122 }
123 contributeToActionBars();
124 loadSortOption();
125 TmfSignalManager.register(this);
126 getSite().setSelectionProvider(fTimeGraphViewer.getSelectionProvider());
127 createTimeEventContextMenu();
128 fTimeGraphViewer.getTimeGraphControl().addMouseListener(new MouseAdapter() {
129 @Override
130 public void mouseDoubleClick(MouseEvent e) {
131 TimeGraphControl timeGraphControl = getTimeGraphViewer().getTimeGraphControl();
132 ISelection selection = timeGraphControl.getSelection();
133 if (selection instanceof IStructuredSelection) {
134 for (Object object : ((IStructuredSelection) selection).toList()) {
135 if (object instanceof FlamegraphEvent) {
136 FlamegraphEvent event = (FlamegraphEvent) object;
137 long startTime = event.getTime();
138 long endTime = startTime + event.getDuration();
139 getTimeGraphViewer().setStartFinishTime(startTime, endTime);
140 break;
141 }
142 }
143 }
144 }
145 });
146 }
147
148 /**
149 * Get the time graph viewer
150 *
151 * @return the time graph viewer
152 */
153 @VisibleForTesting
154 public TimeGraphViewer getTimeGraphViewer() {
155 return fTimeGraphViewer;
156 }
157
158 /**
159 * Handler for the trace selected signal
160 *
161 * @param signal
162 * The incoming signal
163 */
164 @TmfSignalHandler
165 public void traceSelected(final TmfTraceSelectedSignal signal) {
166 fTrace = signal.getTrace();
167 if (fTrace != null) {
168 CallGraphAnalysis flamegraphModule = TmfTraceUtils.getAnalysisModuleOfClass(fTrace, CallGraphAnalysis.class, CallGraphAnalysisUI.ID);
169 buildFlameGraph(flamegraphModule);
170 }
171 }
172
173 /**
174 * Get the necessary data for the flame graph and display it
175 *
176 * @param callGraphAnalysis
177 * the callGraphAnalysis
178 */
179 @VisibleForTesting
180 public void buildFlameGraph(CallGraphAnalysis callGraphAnalysis) {
181 /*
182 * Note for synchronization:
183 *
184 * Acquire the lock at entry. then we have 4 places to release it
185 *
186 * 1- if the lock failed
187 *
188 * 2- if the data is null and we have no UI to update
189 *
190 * 3- if the request is cancelled before it gets to the display
191 *
192 * 4- on a clean execution
193 */
194 try {
195 fLock.acquire();
196 } catch (InterruptedException e) {
197 Activator.getDefault().logError(e.getMessage(), e);
198 fLock.release();
199 }
200 if (callGraphAnalysis == null) {
201 fTimeGraphViewer.setInput(null);
202 fLock.release();
203 return;
204 }
205 fTimeGraphViewer.setInput(callGraphAnalysis.getSegmentStore());
206 callGraphAnalysis.schedule();
207 Job j = new Job(Messages.CallGraphAnalysis_Execution) {
208
209 @Override
210 protected IStatus run(IProgressMonitor monitor) {
211 if (monitor.isCanceled()) {
212 fLock.release();
213 return Status.CANCEL_STATUS;
214 }
215 callGraphAnalysis.waitForCompletion(monitor);
216 Display.getDefault().asyncExec(() -> {
217 fTimeGraphViewer.setInput(callGraphAnalysis.getThreadNodes());
218 fTimeGraphViewer.resetStartFinishTime();
219 fLock.release();
220 });
221 return Status.OK_STATUS;
222 }
223 };
224 j.schedule();
225 }
226
227 /**
228 * Await the next refresh
229 *
230 * @throws InterruptedException
231 * something took too long
232 */
233 @VisibleForTesting
234 public void waitForUpdate() throws InterruptedException {
235 /*
236 * wait for the semaphore to be available, then release it immediately
237 */
238 fLock.acquire();
239 fLock.release();
240 }
241
242 /**
243 * Trace is closed: clear the data structures and the view
244 *
245 * @param signal
246 * the signal received
247 */
248 @TmfSignalHandler
249 public void traceClosed(final TmfTraceClosedSignal signal) {
250 if (signal.getTrace() == fTrace) {
251 fTimeGraphViewer.setInput(null);
252 }
253 }
254
255 @Override
256 public void setFocus() {
257 fTimeGraphViewer.setFocus();
258 }
259
260 // ------------------------------------------------------------------------
261 // Helper methods
262 // ------------------------------------------------------------------------
263
264 private void createTimeEventContextMenu() {
265 fEventMenuManager.setRemoveAllWhenShown(true);
266 TimeGraphControl timeGraphControl = fTimeGraphViewer.getTimeGraphControl();
267 final Menu timeEventMenu = fEventMenuManager.createContextMenu(timeGraphControl);
268
269 timeGraphControl.addTimeGraphEntryMenuListener(new MenuDetectListener() {
270 @Override
271 public void menuDetected(MenuDetectEvent event) {
272 /*
273 * The TimeGraphControl will call the TimeGraphEntryMenuListener
274 * before the TimeEventMenuListener. We need to clear the menu
275 * for the case the selection was done on the namespace where
276 * the time event listener below won't be called afterwards.
277 */
278 timeGraphControl.setMenu(null);
279 event.doit = false;
280 }
281 });
282 timeGraphControl.addTimeEventMenuListener(new MenuDetectListener() {
283 @Override
284 public void menuDetected(MenuDetectEvent event) {
285 Menu menu = timeEventMenu;
286 if (event.data instanceof FlamegraphEvent) {
287 timeGraphControl.setMenu(menu);
288 return;
289 }
290 timeGraphControl.setMenu(null);
291 event.doit = false;
292 }
293 });
294
295 fEventMenuManager.addMenuListener(new IMenuListener() {
296 @Override
297 public void menuAboutToShow(IMenuManager manager) {
298 fillTimeEventContextMenu(fEventMenuManager);
299 fEventMenuManager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
300 }
301 });
302 getSite().registerContextMenu(fEventMenuManager, fTimeGraphViewer.getSelectionProvider());
303 }
304
305 /**
306 * Fill context menu
307 *
308 * @param menuManager
309 * a menuManager to fill
310 */
311 protected void fillTimeEventContextMenu(@NonNull IMenuManager menuManager) {
312 ISelection selection = getSite().getSelectionProvider().getSelection();
313 if (selection instanceof IStructuredSelection) {
314 for (Object object : ((IStructuredSelection) selection).toList()) {
315 if (object instanceof FlamegraphEvent) {
316 final FlamegraphEvent flamegraphEvent = (FlamegraphEvent) object;
317 menuManager.add(new Action(Messages.FlameGraphView_GotoMaxDuration) {
318 @Override
319 public void run() {
320 ISegment maxSeg = flamegraphEvent.getStatistics().getMaxSegment();
321 TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(maxSeg.getStart()), TmfTimestamp.fromNanos(maxSeg.getEnd()));
322 broadcast(sig);
323 }
324 });
325
326 menuManager.add(new Action(Messages.FlameGraphView_GotoMinDuration) {
327 @Override
328 public void run() {
329 ISegment minSeg = flamegraphEvent.getStatistics().getMinSegment();
330 TmfSelectionRangeUpdatedSignal sig = new TmfSelectionRangeUpdatedSignal(this, TmfTimestamp.fromNanos(minSeg.getStart()), TmfTimestamp.fromNanos(minSeg.getEnd()));
331 broadcast(sig);
332 }
333 });
334 }
335 }
336 }
337 }
338
339 private void contributeToActionBars() {
340 IActionBars bars = getViewSite().getActionBars();
341 fillLocalToolBar(bars.getToolBarManager());
342 }
343
344 private void fillLocalToolBar(IToolBarManager manager) {
345 manager.add(getSortByNameAction());
346 manager.add(getSortByIdAction());
347 manager.add(new Separator());
348 }
349
350 private Action getSortByNameAction() {
351 if (fSortByNameAction == null) {
352 fSortByNameAction = new Action(Messages.FlameGraph_SortByThreadName, IAction.AS_CHECK_BOX) {
353 @Override
354 public void run() {
355 SortOption sortOption = fTimeGraphContentProvider.getSortOption();
356 if (sortOption == SortOption.BY_NAME) {
357 setSortOption(SortOption.BY_NAME_REV);
358 } else {
359 setSortOption(SortOption.BY_NAME);
360 }
361 }
362 };
363 fSortByNameAction.setToolTipText(Messages.FlameGraph_SortByThreadName);
364 fSortByNameAction.setImageDescriptor(SORT_BY_NAME_ICON);
365 }
366 return fSortByNameAction;
367 }
368
369 private Action getSortByIdAction() {
370 if (fSortByIdAction == null) {
371 fSortByIdAction = new Action(Messages.FlameGraph_SortByThreadId, IAction.AS_CHECK_BOX) {
372 @Override
373 public void run() {
374 SortOption sortOption = fTimeGraphContentProvider.getSortOption();
375 if (sortOption == SortOption.BY_ID) {
376 setSortOption(SortOption.BY_ID_REV);
377 } else {
378 setSortOption(SortOption.BY_ID);
379 }
380 }
381 };
382 fSortByIdAction.setToolTipText(Messages.FlameGraph_SortByThreadId);
383 fSortByIdAction.setImageDescriptor(SORT_BY_ID_ICON);
384 }
385 return fSortByIdAction;
386 }
387
388 private void setSortOption(SortOption sortOption) {
389 // reset defaults
390 getSortByNameAction().setChecked(false);
391 getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON);
392 getSortByIdAction().setChecked(false);
393 getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON);
394
395 if (sortOption.equals(SortOption.BY_NAME)) {
396 fTimeGraphContentProvider.setSortOption(SortOption.BY_NAME);
397 getSortByNameAction().setChecked(true);
398 } else if (sortOption.equals(SortOption.BY_NAME_REV)) {
399 fTimeGraphContentProvider.setSortOption(SortOption.BY_NAME_REV);
400 getSortByNameAction().setChecked(true);
401 getSortByNameAction().setImageDescriptor(SORT_BY_NAME_REV_ICON);
402 } else if (sortOption.equals(SortOption.BY_ID)) {
403 fTimeGraphContentProvider.setSortOption(SortOption.BY_ID);
404 getSortByIdAction().setChecked(true);
405 } else if (sortOption.equals(SortOption.BY_ID_REV)) {
406 fTimeGraphContentProvider.setSortOption(SortOption.BY_ID_REV);
407 getSortByIdAction().setChecked(true);
408 getSortByIdAction().setImageDescriptor(SORT_BY_ID_REV_ICON);
409 }
410 saveSortOption();
411 fTimeGraphViewer.refresh();
412 }
413
414 private void saveSortOption() {
415 SortOption sortOption = fTimeGraphContentProvider.getSortOption();
416 IDialogSettings settings = Activator.getDefault().getDialogSettings();
417 IDialogSettings section = settings.getSection(getClass().getName());
418 if (section == null) {
419 section = settings.addNewSection(getClass().getName());
420 }
421 section.put(SORT_OPTION_KEY, sortOption.name());
422 }
423
424 private void loadSortOption() {
425 IDialogSettings settings = Activator.getDefault().getDialogSettings();
426 IDialogSettings section = settings.getSection(getClass().getName());
427 if (section == null) {
428 return;
429 }
430 String sortOption = section.get(SORT_OPTION_KEY);
431 if (sortOption == null) {
432 return;
433 }
434 setSortOption(SortOption.fromName(sortOption));
435 }
436
437 /**
438 * Symbol map provider updated
439 *
440 * @param signal
441 * the signal
442 */
443 @TmfSignalHandler
444 public void symbolMapUpdated(TmfSymbolProviderUpdatedSignal signal) {
445 if (signal.getSource() != this) {
446 fTimeGraphViewer.refresh();
447 }
448 }
449
450 }
This page took 0.040126 seconds and 5 git commands to generate.