analysis: Move plugins to their own sub-directory
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / views / callstack / CallStackView.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2015 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made 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 * Contributors:
10 * Patrick Tasse - Initial API and implementation
11 * Bernd Hufmann - Updated signal handling
12 * Marc-Andre Laperle - Map from binary file
13 *******************************************************************************/
14
15 package org.eclipse.tracecompass.tmf.ui.views.callstack;
16
17 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
18
19 import java.io.File;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.CopyOnWriteArrayList;
28
29 import org.eclipse.core.runtime.IProgressMonitor;
30 import org.eclipse.core.runtime.IStatus;
31 import org.eclipse.core.runtime.NullProgressMonitor;
32 import org.eclipse.core.runtime.Status;
33 import org.eclipse.core.runtime.jobs.Job;
34 import org.eclipse.jdt.annotation.NonNull;
35 import org.eclipse.jdt.annotation.Nullable;
36 import org.eclipse.jface.action.Action;
37 import org.eclipse.jface.action.IAction;
38 import org.eclipse.jface.action.IStatusLineManager;
39 import org.eclipse.jface.action.IToolBarManager;
40 import org.eclipse.jface.action.MenuManager;
41 import org.eclipse.jface.action.Separator;
42 import org.eclipse.jface.dialogs.IDialogSettings;
43 import org.eclipse.jface.resource.ImageDescriptor;
44 import org.eclipse.jface.util.IPropertyChangeListener;
45 import org.eclipse.jface.util.PropertyChangeEvent;
46 import org.eclipse.jface.viewers.DoubleClickEvent;
47 import org.eclipse.jface.viewers.IDoubleClickListener;
48 import org.eclipse.jface.viewers.ILabelProviderListener;
49 import org.eclipse.jface.viewers.ISelection;
50 import org.eclipse.jface.viewers.IStructuredSelection;
51 import org.eclipse.jface.viewers.ITableLabelProvider;
52 import org.eclipse.swt.SWT;
53 import org.eclipse.swt.events.ControlAdapter;
54 import org.eclipse.swt.events.ControlEvent;
55 import org.eclipse.swt.events.MouseAdapter;
56 import org.eclipse.swt.events.MouseEvent;
57 import org.eclipse.swt.graphics.Image;
58 import org.eclipse.swt.widgets.Composite;
59 import org.eclipse.swt.widgets.Display;
60 import org.eclipse.swt.widgets.FileDialog;
61 import org.eclipse.swt.widgets.Menu;
62 import org.eclipse.swt.widgets.Tree;
63 import org.eclipse.tracecompass.internal.tmf.core.callstack.FunctionNameMapper;
64 import org.eclipse.tracecompass.internal.tmf.ui.Activator;
65 import org.eclipse.tracecompass.internal.tmf.ui.ITmfImageConstants;
66 import org.eclipse.tracecompass.internal.tmf.ui.Messages;
67 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
68 import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
69 import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
70 import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
71 import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
72 import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
73 import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
74 import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
75 import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue.Type;
76 import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
77 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
78 import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
79 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
80 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
81 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
82 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
83 import org.eclipse.tracecompass.tmf.core.timestamp.TmfNanoTimestamp;
84 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
85 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
86 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampDelta;
87 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
88 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext;
89 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
90 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
91 import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor;
92 import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
93 import org.eclipse.tracecompass.tmf.ui.views.ITmfTimeAligned;
94 import org.eclipse.tracecompass.tmf.ui.views.TmfView;
95 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphRangeListener;
96 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTimeListener;
97 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphCombo;
98 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphContentProvider;
99 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphRangeUpdateEvent;
100 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent;
101 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer;
102 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
103 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
104 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
105 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
106 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
107 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
108 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphSelection;
109 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
110 import org.eclipse.ui.IActionBars;
111 import org.eclipse.ui.IEditorPart;
112
113 /**
114 * Main implementation for the Call Stack view
115 *
116 * @author Patrick Tasse
117 */
118 public class CallStackView extends TmfView implements ITmfTimeAligned {
119
120 // ------------------------------------------------------------------------
121 // Constants
122 // ------------------------------------------------------------------------
123
124 /** View ID. */
125 public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.callstack"; //$NON-NLS-1$
126
127 /**
128 * Redraw state enum
129 */
130 private enum State {
131 IDLE, BUSY, PENDING
132 }
133
134 private static final String[] COLUMN_TIMES = new String[] {
135 Messages.CallStackView_FunctionColumn,
136 Messages.CallStackView_DepthColumn,
137 Messages.CallStackView_EntryTimeColumn,
138 Messages.CallStackView_ExitTimeColumn,
139 Messages.CallStackView_DurationColumn
140 };
141
142 private static final int[] COLUMN_WIDTHS = new int[] {
143 200,
144 50,
145 120,
146 120,
147 120
148 };
149
150 /** Timeout between updates in the build thread in ms */
151 private static final long BUILD_UPDATE_TIMEOUT = 500;
152
153 // Fraction of a function duration to be added as spacing
154 private static final double SPACING_RATIO = 0.01;
155
156 private static final Image THREAD_IMAGE = Activator.getDefault().getImageFromPath("icons/obj16/thread_obj.gif"); //$NON-NLS-1$
157 private static final Image STACKFRAME_IMAGE = Activator.getDefault().getImageFromPath("icons/obj16/stckframe_obj.gif"); //$NON-NLS-1$
158
159 private static final String IMPORT_MAPPING_ICON_PATH = "icons/etool16/import.gif"; //$NON-NLS-1$
160 private static final String IMPORT_BINARY_ICON_PATH = "icons/obj16/binaries_obj.gif"; //$NON-NLS-1$
161
162 private static final ImageDescriptor SORT_BY_NAME_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha.gif"); //$NON-NLS-1$
163 private static final ImageDescriptor SORT_BY_NAME_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha_rev.gif"); //$NON-NLS-1$
164 private static final ImageDescriptor SORT_BY_ID_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_num.gif"); //$NON-NLS-1$
165 private static final ImageDescriptor SORT_BY_ID_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_num_rev.gif"); //$NON-NLS-1$
166 private static final ImageDescriptor SORT_BY_TIME_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_time.gif"); //$NON-NLS-1$
167 private static final ImageDescriptor SORT_BY_TIME_REV_ICON = Activator.getDefault().getImageDescripterFromPath("icons/etool16/sort_time_rev.gif"); //$NON-NLS-1$
168 private static final String SORT_OPTION_KEY = "sort.option"; //$NON-NLS-1$
169
170 private enum SortOption {
171 BY_NAME, BY_NAME_REV, BY_ID, BY_ID_REV, BY_TIME, BY_TIME_REV
172 }
173
174 private SortOption fSortOption;
175 private Comparator<ITimeGraphEntry> fThreadComparator = null;
176 private Action fSortByNameAction;
177 private Action fSortByIdAction;
178 private Action fSortByTimeAction;
179
180 // ------------------------------------------------------------------------
181 // Fields
182 // ------------------------------------------------------------------------
183
184 // The time graph combo
185 private TimeGraphCombo fTimeGraphCombo;
186
187 // The selected trace
188 private ITmfTrace fTrace;
189
190 // The selected thread map
191 private final Map<ITmfTrace, String> fSelectedThreadMap = new HashMap<>();
192
193 // The time graph entry list
194 private List<TraceEntry> fEntryList;
195
196 // The trace to entry list hash map
197 private final Map<ITmfTrace, List<TraceEntry>> fEntryListMap = new HashMap<>();
198
199 // The trace to build thread hash map
200 private final Map<ITmfTrace, BuildThread> fBuildThreadMap = new HashMap<>();
201
202 /** The map to map function addresses to function names */
203 private Map<String, String> fNameMapping;
204
205 // The start time
206 private long fStartTime;
207
208 // The end time
209 private long fEndTime;
210
211 // The display width
212 private int fDisplayWidth;
213
214 // The next event action
215 private Action fNextEventAction;
216
217 // The previous event action
218 private Action fPrevEventAction;
219
220 // The next item action
221 private Action fNextItemAction;
222
223 // The previous item action
224 private Action fPreviousItemAction;
225
226 // The action to import a function-name mapping file
227 private Action fImportMappingAction;
228
229 // The action to import a binary file mapping */
230 private Action fImportBinaryFileMappingAction;
231
232 // The zoom thread
233 private ZoomThread fZoomThread;
234
235 // The redraw state used to prevent unnecessary queuing of display runnables
236 private State fRedrawState = State.IDLE;
237
238 // The redraw synchronization object
239 private final Object fSyncObj = new Object();
240
241 // The saved time sync. signal used when switching off the pinning of a view
242 private TmfSelectionRangeUpdatedSignal fSavedTimeSyncSignal;
243
244 // The saved window range signal used when switching off the pinning of
245 // a view
246 private TmfWindowRangeUpdatedSignal fSavedRangeSyncSignal;
247
248 // ------------------------------------------------------------------------
249 // Classes
250 // ------------------------------------------------------------------------
251
252 private class TraceEntry extends TimeGraphEntry {
253 public TraceEntry(String name, long startTime, long endTime) {
254 super(name, startTime, endTime);
255 }
256
257 @Override
258 public boolean hasTimeEvents() {
259 return false;
260 }
261 }
262
263 private class ThreadEntry extends TimeGraphEntry {
264 // The call stack quark
265 private final int fCallStackQuark;
266 // The state system from which this entry comes
267 private final ITmfStateSystem fSS;
268 // The thread id
269 private final long fThreadId;
270
271 public ThreadEntry(ITmfStateSystem ss, String name, long threadId, int callStackQuark, long startTime, long endTime) {
272 super(name, startTime, endTime);
273 fCallStackQuark = callStackQuark;
274 fThreadId = threadId;
275 fSS = ss;
276 }
277
278 @Override
279 public boolean hasTimeEvents() {
280 return false;
281 }
282
283 public int getCallStackQuark() {
284 return fCallStackQuark;
285 }
286
287 public long getThreadId() {
288 return fThreadId;
289 }
290
291 @Nullable
292 public ITmfStateSystem getStateSystem() {
293 return fSS;
294 }
295 }
296
297 private class ThreadNameComparator implements Comparator<ITimeGraphEntry> {
298 private boolean reverse;
299
300 public ThreadNameComparator(boolean reverse) {
301 this.reverse = reverse;
302 }
303
304 @Override
305 public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
306 return reverse ? o2.getName().compareTo(o1.getName()) :
307 o1.getName().compareTo(o2.getName());
308 }
309 }
310
311 private class ThreadIdComparator implements Comparator<ITimeGraphEntry> {
312 private boolean reverse;
313
314 public ThreadIdComparator(boolean reverse) {
315 this.reverse = reverse;
316 }
317
318 @Override
319 public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
320 ThreadEntry t1 = (ThreadEntry) o1;
321 ThreadEntry t2 = (ThreadEntry) o2;
322 return reverse ? Long.compare(t2.getThreadId(), t1.getThreadId()) :
323 Long.compare(t1.getThreadId(), t2.getThreadId());
324 }
325 }
326
327 private class ThreadTimeComparator implements Comparator<ITimeGraphEntry> {
328 private boolean reverse;
329
330 public ThreadTimeComparator(boolean reverse) {
331 this.reverse = reverse;
332 }
333
334 @Override
335 public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
336 return reverse ? Long.compare(o2.getStartTime(), o1.getStartTime()) :
337 Long.compare(o1.getStartTime(), o2.getStartTime());
338 }
339 }
340
341 private class TreeLabelProvider implements ITableLabelProvider {
342
343 @Override
344 public void addListener(ILabelProviderListener listener) {
345 }
346
347 @Override
348 public void dispose() {
349 }
350
351 @Override
352 public boolean isLabelProperty(Object element, String property) {
353 return false;
354 }
355
356 @Override
357 public void removeListener(ILabelProviderListener listener) {
358 }
359
360 @Override
361 public Image getColumnImage(Object element, int columnIndex) {
362 if (columnIndex == 0) {
363 if (element instanceof ThreadEntry) {
364 return THREAD_IMAGE;
365 } else if (element instanceof CallStackEntry) {
366 CallStackEntry entry = (CallStackEntry) element;
367 if (entry.getFunctionName().length() > 0) {
368 return STACKFRAME_IMAGE;
369 }
370 }
371 }
372 return null;
373 }
374
375 @Override
376 public String getColumnText(Object element, int columnIndex) {
377 if (element instanceof CallStackEntry) {
378 CallStackEntry entry = (CallStackEntry) element;
379 if (columnIndex == 0) {
380 return entry.getFunctionName();
381 } else if (columnIndex == 1 && entry.getFunctionName().length() > 0) {
382 int depth = entry.getStackLevel();
383 return Integer.toString(depth);
384 } else if (columnIndex == 2 && entry.getFunctionName().length() > 0) {
385 ITmfTimestamp ts = new TmfTimestamp(entry.getFunctionEntryTime(), ITmfTimestamp.NANOSECOND_SCALE);
386 return ts.toString();
387 } else if (columnIndex == 3 && entry.getFunctionName().length() > 0) {
388 ITmfTimestamp ts = new TmfTimestamp(entry.getFunctionExitTime(), ITmfTimestamp.NANOSECOND_SCALE);
389 return ts.toString();
390 } else if (columnIndex == 4 && entry.getFunctionName().length() > 0) {
391 ITmfTimestamp ts = new TmfTimestampDelta(entry.getFunctionExitTime() - entry.getFunctionEntryTime(), ITmfTimestamp.NANOSECOND_SCALE);
392 return ts.toString();
393 }
394 } else if (element instanceof ITimeGraphEntry) {
395 if (columnIndex == 0) {
396 return ((ITimeGraphEntry) element).getName();
397 }
398 }
399 return ""; //$NON-NLS-1$
400 }
401
402 }
403
404 private class BuildThread extends Thread {
405 private final @NonNull ITmfTrace fBuildTrace;
406 private final ITmfTrace fParentTrace;
407 private final IProgressMonitor fMonitor;
408
409 public BuildThread(@NonNull ITmfTrace trace, ITmfTrace parentTrace) {
410 super("CallStackView build"); //$NON-NLS-1$
411 fBuildTrace = trace;
412 fParentTrace = parentTrace;
413 fMonitor = new NullProgressMonitor();
414 }
415
416 @Override
417 public void run() {
418 buildThreadList(fBuildTrace, fParentTrace, fMonitor);
419 synchronized (fBuildThreadMap) {
420 fBuildThreadMap.remove(fBuildTrace);
421 }
422 }
423
424 public void cancel() {
425 fMonitor.setCanceled(true);
426 }
427 }
428
429 private class ZoomThread extends Thread {
430 private final List<TraceEntry> fZoomEntryList;
431 private final long fZoomStartTime;
432 private final long fZoomEndTime;
433 private final IProgressMonitor fMonitor;
434
435 public ZoomThread(List<TraceEntry> entryList, long startTime, long endTime) {
436 super("CallStackView zoom"); //$NON-NLS-1$
437 fZoomEntryList = entryList;
438 fZoomStartTime = startTime;
439 fZoomEndTime = endTime;
440 fMonitor = new NullProgressMonitor();
441 }
442
443 @Override
444 public void run() {
445 if (fZoomEntryList == null) {
446 return;
447 }
448 long resolution = Math.max(1, (fZoomEndTime - fZoomStartTime) / fDisplayWidth);
449 for (TraceEntry traceEntry : fZoomEntryList) {
450 for (ITimeGraphEntry threadEntry : traceEntry.getChildren()) {
451 ITmfStateSystem ss = ((ThreadEntry) threadEntry).getStateSystem();
452 if (ss == null) {
453 continue;
454 }
455 ss.waitUntilBuilt();
456 if (ss.isCancelled()) {
457 continue;
458 }
459 for (ITimeGraphEntry child : threadEntry.getChildren()) {
460 if (fMonitor.isCanceled()) {
461 break;
462 }
463 CallStackEntry entry = (CallStackEntry) child;
464 if (fZoomStartTime <= fStartTime && fZoomEndTime >= fEndTime) {
465 entry.setZoomedEventList(null);
466 } else {
467 List<ITimeEvent> zoomedEventList = getEventList(entry, fZoomStartTime, fZoomEndTime, resolution, fMonitor);
468 if (zoomedEventList != null) {
469 entry.setZoomedEventList(zoomedEventList);
470 }
471 }
472 redraw();
473 }
474 }
475 }
476 }
477
478 public void cancel() {
479 fMonitor.setCanceled(true);
480 }
481 }
482
483 // ------------------------------------------------------------------------
484 // Constructors
485 // ------------------------------------------------------------------------
486
487 /**
488 * Default constructor
489 */
490 public CallStackView() {
491 super(ID);
492 fDisplayWidth = Display.getDefault().getBounds().width;
493 }
494
495 // ------------------------------------------------------------------------
496 // ViewPart
497 // ------------------------------------------------------------------------
498
499 @Override
500 public void createPartControl(Composite parent) {
501 super.createPartControl(parent);
502 fTimeGraphCombo = new TimeGraphCombo(parent, SWT.NONE);
503
504 fTimeGraphCombo.setTreeContentProvider(new TimeGraphContentProvider());
505
506 fTimeGraphCombo.setTreeLabelProvider(new TreeLabelProvider());
507
508 fTimeGraphCombo.setTreeColumns(COLUMN_TIMES);
509
510 fTimeGraphCombo.getTreeViewer().getTree().getColumn(0).setWidth(COLUMN_WIDTHS[0]);
511 fTimeGraphCombo.getTreeViewer().getTree().getColumn(1).setWidth(COLUMN_WIDTHS[1]);
512 fTimeGraphCombo.getTreeViewer().getTree().getColumn(2).setWidth(COLUMN_WIDTHS[2]);
513 fTimeGraphCombo.getTreeViewer().getTree().getColumn(3).setWidth(COLUMN_WIDTHS[3]);
514 fTimeGraphCombo.getTreeViewer().getTree().getColumn(4).setWidth(COLUMN_WIDTHS[4]);
515
516 fTimeGraphCombo.setTimeGraphContentProvider(new TimeGraphContentProvider());
517 fTimeGraphCombo.setTimeGraphProvider(new CallStackPresentationProvider(this));
518 fTimeGraphCombo.getTimeGraphViewer().setTimeFormat(TimeFormat.CALENDAR);
519
520 fTimeGraphCombo.getTimeGraphViewer().addRangeListener(new ITimeGraphRangeListener() {
521 @Override
522 public void timeRangeUpdated(TimeGraphRangeUpdateEvent event) {
523 long startTime = event.getStartTime();
524 long endTime = event.getEndTime();
525 TmfTimeRange range = new TmfTimeRange(new TmfNanoTimestamp(startTime), new TmfNanoTimestamp(endTime));
526 broadcast(new TmfWindowRangeUpdatedSignal(CallStackView.this, range));
527 startZoomThread(startTime, endTime);
528 }
529 });
530
531 fTimeGraphCombo.getTimeGraphViewer().addTimeListener(new ITimeGraphTimeListener() {
532 @Override
533 public void timeSelected(TimeGraphTimeEvent event) {
534 long beginTime = event.getBeginTime();
535 long endTime = event.getEndTime();
536 synchingToTime(beginTime);
537 broadcast(new TmfSelectionRangeUpdatedSignal(CallStackView.this, new TmfNanoTimestamp(beginTime), new TmfNanoTimestamp(endTime)));
538 }
539 });
540
541 fTimeGraphCombo.getTimeGraphViewer().getControl().addControlListener(new ControlAdapter() {
542 @Override
543 public void controlResized(ControlEvent e) {
544 fDisplayWidth = fTimeGraphCombo.getTimeGraphViewer().getControl().getSize().x;
545 if (fEntryList != null) {
546 startZoomThread(fTimeGraphCombo.getTimeGraphViewer().getTime0(), fTimeGraphCombo.getTimeGraphViewer().getTime1());
547 }
548 }
549 });
550
551 fTimeGraphCombo.getTreeViewer().addDoubleClickListener(new IDoubleClickListener() {
552 @Override
553 public void doubleClick(DoubleClickEvent event) {
554 Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
555 if (selection instanceof CallStackEntry) {
556 CallStackEntry entry = (CallStackEntry) selection;
557 if (entry.getFunctionName().length() > 0) {
558 long entryTime = entry.getFunctionEntryTime();
559 long exitTime = entry.getFunctionExitTime();
560 long spacingTime = (long) ((exitTime - entryTime) * SPACING_RATIO);
561 entryTime -= spacingTime;
562 exitTime += spacingTime;
563 TmfTimeRange range = new TmfTimeRange(new TmfNanoTimestamp(entryTime), new TmfNanoTimestamp(exitTime));
564 broadcast(new TmfWindowRangeUpdatedSignal(CallStackView.this, range));
565 fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(entryTime, exitTime);
566 startZoomThread(entryTime, exitTime);
567 }
568 }
569 }
570 });
571
572 fTimeGraphCombo.getTimeGraphViewer().getTimeGraphControl().addMouseListener(new MouseAdapter() {
573 @Override
574 public void mouseDoubleClick(MouseEvent e) {
575 TimeGraphControl timeGraphControl = fTimeGraphCombo.getTimeGraphViewer().getTimeGraphControl();
576 ISelection selection = timeGraphControl.getSelection();
577 if (selection instanceof TimeGraphSelection) {
578 Object o = ((TimeGraphSelection) selection).getFirstElement();
579 if (o instanceof CallStackEvent) {
580 CallStackEvent event = (CallStackEvent) o;
581 long startTime = event.getTime();
582 long endTime = startTime + event.getDuration();
583 long spacingTime = (long) ((endTime - startTime) * SPACING_RATIO);
584 startTime -= spacingTime;
585 endTime += spacingTime;
586 TmfTimeRange range = new TmfTimeRange(new TmfNanoTimestamp(startTime), new TmfNanoTimestamp(endTime));
587 broadcast(new TmfWindowRangeUpdatedSignal(CallStackView.this, range));
588 fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
589 startZoomThread(startTime, endTime);
590 }
591 }
592 }
593 });
594
595 IStatusLineManager statusLineManager = getViewSite().getActionBars().getStatusLineManager();
596 fTimeGraphCombo.getTimeGraphViewer().getTimeGraphControl().setStatusLineManager(statusLineManager);
597
598 // View Action Handling
599 makeActions();
600 contributeToActionBars();
601 createContextMenu();
602 loadSortOption();
603
604 IEditorPart editor = getSite().getPage().getActiveEditor();
605 if (editor instanceof ITmfTraceEditor) {
606 ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace();
607 if (trace != null) {
608 traceSelected(new TmfTraceSelectedSignal(this, trace));
609 }
610 }
611 }
612
613 @Override
614 public void setFocus() {
615 fTimeGraphCombo.setFocus();
616 }
617
618 // ------------------------------------------------------------------------
619 // Signal handlers
620 // ------------------------------------------------------------------------
621 /**
622 * Handler for the trace opened signal.
623 *
624 * @param signal
625 * The incoming signal
626 */
627 @TmfSignalHandler
628 public void traceOpened(TmfTraceOpenedSignal signal) {
629 fTrace = signal.getTrace();
630 loadTrace();
631 }
632
633 /**
634 * Handler for the trace selected signal
635 *
636 * @param signal
637 * The incoming signal
638 */
639 @TmfSignalHandler
640 public void traceSelected(final TmfTraceSelectedSignal signal) {
641 if (signal.getTrace() == fTrace) {
642 return;
643 }
644 fTrace = signal.getTrace();
645 loadTrace();
646 }
647
648 /**
649 * Trace is closed: clear the data structures and the view
650 *
651 * @param signal
652 * the signal received
653 */
654 @TmfSignalHandler
655 public void traceClosed(final TmfTraceClosedSignal signal) {
656 synchronized (fBuildThreadMap) {
657 for (ITmfTrace trace : TmfTraceManager.getTraceSet(signal.getTrace())) {
658 BuildThread buildThread = fBuildThreadMap.remove(trace);
659 if (buildThread != null) {
660 buildThread.cancel();
661 }
662 }
663 }
664 synchronized (fEntryListMap) {
665 fEntryListMap.remove(signal.getTrace());
666 }
667 fSelectedThreadMap.remove(signal.getTrace());
668 if (signal.getTrace() == fTrace) {
669 fTrace = null;
670 fStartTime = 0;
671 fEndTime = 0;
672 refresh();
673 }
674 }
675
676 /**
677 * Handler for the selection range signal.
678 *
679 * @param signal
680 * The incoming signal
681 * @since 1.0
682 */
683 @TmfSignalHandler
684 public void selectionRangeUpdated(final TmfSelectionRangeUpdatedSignal signal) {
685
686 fSavedTimeSyncSignal = isPinned() ? new TmfSelectionRangeUpdatedSignal(signal.getSource(), signal.getBeginTime(), signal.getEndTime()) : null;
687
688 if (signal.getSource() == this || fTrace == null || isPinned()) {
689 return;
690 }
691 final long beginTime = signal.getBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
692 final long endTime = signal.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
693 Display.getDefault().asyncExec(new Runnable() {
694 @Override
695 public void run() {
696 if (fTimeGraphCombo.isDisposed()) {
697 return;
698 }
699 if (beginTime == endTime) {
700 fTimeGraphCombo.getTimeGraphViewer().setSelectedTime(beginTime, true);
701 } else {
702 fTimeGraphCombo.getTimeGraphViewer().setSelectionRange(beginTime, endTime);
703 }
704 synchingToTime(beginTime);
705 startZoomThread(fTimeGraphCombo.getTimeGraphViewer().getTime0(), fTimeGraphCombo.getTimeGraphViewer().getTime1());
706 if (fEntryList == null) {
707 return;
708 }
709 TimeGraphViewer viewer = fTimeGraphCombo.getTimeGraphViewer();
710 for (TraceEntry traceEntry : fEntryList) {
711 for (ITimeGraphEntry child : traceEntry.getChildren()) {
712 ThreadEntry threadEntry = (ThreadEntry) child;
713 ITmfStateSystem ss = threadEntry.getStateSystem();
714 if (ss == null || beginTime < ss.getStartTime() || beginTime > ss.getCurrentEndTime()) {
715 continue;
716 }
717 try {
718 int quark = threadEntry.getCallStackQuark();
719 ITmfStateInterval stackInterval = ss.querySingleState(beginTime, quark);
720 if (beginTime == stackInterval.getStartTime()) {
721 int stackLevel = stackInterval.getStateValue().unboxInt();
722 ITimeGraphEntry selectedEntry = threadEntry.getChildren().get(Math.max(0, stackLevel - 1));
723 fTimeGraphCombo.setSelection(selectedEntry);
724 viewer.getTimeGraphControl().fireSelectionChanged();
725 break;
726 }
727 } catch (AttributeNotFoundException | TimeRangeException | StateSystemDisposedException | StateValueTypeException e) {
728 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
729 }
730 }
731 }
732 }
733 });
734 }
735
736 /**
737 * Handler for the window range signal.
738 *
739 * @param signal
740 * The incoming signal
741 * @since 1.0
742 */
743 @TmfSignalHandler
744 public void windowRangeUpdated(final TmfWindowRangeUpdatedSignal signal) {
745
746 if (isPinned()) {
747 fSavedRangeSyncSignal =
748 new TmfWindowRangeUpdatedSignal(signal.getSource(), new TmfTimeRange(signal.getCurrentRange().getStartTime(), signal.getCurrentRange().getEndTime()));
749
750 fSavedTimeSyncSignal = null;
751 }
752
753 if (signal.getSource() == this || fTrace == null || isPinned()) {
754 return;
755 }
756 if (signal.getCurrentRange().getIntersection(fTrace.getTimeRange()) == null) {
757 return;
758 }
759 final long startTime = signal.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
760 final long endTime = signal.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
761 Display.getDefault().asyncExec(new Runnable() {
762 @Override
763 public void run() {
764 if (fTimeGraphCombo.isDisposed()) {
765 return;
766 }
767 fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
768 startZoomThread(startTime, endTime);
769 }
770 });
771 }
772
773 // ------------------------------------------------------------------------
774 // Internal
775 // ------------------------------------------------------------------------
776
777 private void loadTrace() {
778 synchronized (fEntryListMap) {
779 fEntryList = fEntryListMap.get(fTrace);
780 if (fEntryList == null) {
781 fStartTime = Long.MAX_VALUE;
782 fEndTime = Long.MIN_VALUE;
783 refresh();
784 synchronized (fBuildThreadMap) {
785 for (ITmfTrace trace : TmfTraceManager.getTraceSet(fTrace)) {
786 trace = checkNotNull(trace);
787 BuildThread buildThread = new BuildThread(trace, fTrace);
788 fBuildThreadMap.put(trace, buildThread);
789 buildThread.start();
790 }
791 }
792 } else {
793 fStartTime = fTrace.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
794 fEndTime = fTrace.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
795 refresh();
796 }
797 }
798 }
799
800 private void buildThreadList(final @NonNull ITmfTrace trace, final ITmfTrace parentTrace, IProgressMonitor monitor) {
801 if (monitor.isCanceled()) {
802 return;
803 }
804 AbstractCallStackAnalysis module = getCallStackModule(trace);
805 if (module == null) {
806 addUnavailableEntry(trace, parentTrace);
807 return;
808 }
809 ITmfStateSystem ss = module.getStateSystem();
810 if (ss == null) {
811 addUnavailableEntry(trace, parentTrace);
812 return;
813 }
814
815 Map<ITmfTrace, TraceEntry> traceEntryMap = new HashMap<>();
816 Map<Integer, ThreadEntry> threadEntryMap = new HashMap<>();
817 String[] threadPaths = module.getThreadsPattern();
818
819 long start = ss.getStartTime();
820
821 boolean complete = false;
822 while (!complete) {
823 if (monitor.isCanceled()) {
824 return;
825 }
826 complete = ss.waitUntilBuilt(BUILD_UPDATE_TIMEOUT);
827 if (ss.isCancelled()) {
828 return;
829 }
830 long end = ss.getCurrentEndTime();
831 if (start == end && !complete) { // when complete execute one last time regardless of end time
832 continue;
833 }
834 List<Integer> threadQuarks = ss.getQuarks(threadPaths);
835 TraceEntry traceEntry = traceEntryMap.get(trace);
836 if (traceEntry == null) {
837 traceEntry = new TraceEntry(trace.getName(), start, end + 1);
838 traceEntryMap.put(trace, traceEntry);
839 traceEntry.sortChildren(fThreadComparator);
840 addToEntryList(parentTrace, Collections.singletonList(traceEntry));
841 } else {
842 traceEntry.updateEndTime(end);
843 }
844 for (int i = 0; i < threadQuarks.size(); i++) {
845 if (monitor.isCanceled()) {
846 return;
847 }
848 int threadQuark = threadQuarks.get(i);
849 try {
850 String[] callStackPath = module.getCallStackPath();
851 int callStackQuark = ss.getQuarkRelative(threadQuark, callStackPath);
852 String threadName = ss.getAttributeName(threadQuark);
853 long threadEnd = end + 1;
854 ITmfStateInterval endInterval = ss.querySingleState(ss.getCurrentEndTime(), callStackQuark);
855 if (endInterval.getStateValue().isNull() && endInterval.getStartTime() != ss.getStartTime()) {
856 threadEnd = endInterval.getStartTime();
857 }
858 ThreadEntry threadEntry = threadEntryMap.get(threadQuark);
859 if (threadEntry == null) {
860 long threadId = ss.querySingleState(ss.getCurrentEndTime(), threadQuark).getStateValue().unboxLong();
861 long threadStart = start;
862 ITmfStateInterval startInterval = ss.querySingleState(start, callStackQuark);
863 if (startInterval.getStateValue().isNull()) {
864 threadStart = Math.min(startInterval.getEndTime() + 1, end + 1);
865 }
866 threadEntry = new ThreadEntry(ss, threadName, threadId, callStackQuark, threadStart, threadEnd);
867 threadEntryMap.put(threadQuark, threadEntry);
868 traceEntry.addChild(threadEntry);
869 } else {
870 threadEntry.updateEndTime(threadEnd);
871 }
872 int level = 1;
873 for (int stackLevelQuark : ss.getSubAttributes(callStackQuark, false)) {
874 if (level > threadEntry.getChildren().size()) {
875 CallStackEntry callStackEntry = new CallStackEntry(threadName, stackLevelQuark, level, trace, ss);
876 threadEntry.addChild(callStackEntry);
877 }
878 level++;
879 }
880 } catch (AttributeNotFoundException e) {
881 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
882 } catch (StateSystemDisposedException e) {
883 /* Ignored */
884 }
885 }
886 if (parentTrace == fTrace) {
887 synchronized (fEntryListMap) {
888 fStartTime = Math.min(fStartTime, start);
889 fEndTime = Math.max(fEndTime, end + 1);
890 }
891 refresh();
892 }
893 for (ITimeGraphEntry threadEntry : traceEntry.getChildren()) {
894 for (ITimeGraphEntry callStackEntry : threadEntry.getChildren()) {
895 if (monitor.isCanceled()) {
896 return;
897 }
898 buildStatusEvents(parentTrace, (CallStackEntry) callStackEntry, monitor, start, end);
899 }
900 }
901 start = end;
902 }
903 }
904
905 private void addToEntryList(ITmfTrace trace, List<TraceEntry> list) {
906 synchronized (fEntryListMap) {
907 List<TraceEntry> entryList = fEntryListMap.get(trace);
908 if (entryList == null) {
909 fEntryListMap.put(trace, new CopyOnWriteArrayList<>(list));
910 } else {
911 entryList.addAll(list);
912 }
913 }
914 }
915
916 private void addUnavailableEntry(ITmfTrace trace, ITmfTrace parentTrace) {
917 String name = Messages.CallStackView_StackInfoNotAvailable + ' ' + '(' + trace.getName() + ')';
918 TraceEntry unavailableEntry = new TraceEntry(name, 0, 0);
919 addToEntryList(parentTrace, Collections.singletonList(unavailableEntry));
920 if (parentTrace == fTrace) {
921 refresh();
922 }
923 }
924
925 private void buildStatusEvents(ITmfTrace trace, CallStackEntry entry, IProgressMonitor monitor, long start, long end) {
926 ITmfStateSystem ss = entry.getStateSystem();
927 long resolution = Math.max(1, (end - ss.getStartTime()) / fDisplayWidth);
928 List<ITimeEvent> eventList = getEventList(entry, start, end + 1, resolution, monitor);
929 if (eventList != null) {
930 for (ITimeEvent event : eventList) {
931 entry.addEvent(event);
932 }
933 }
934 if (trace == fTrace) {
935 redraw();
936 }
937 }
938
939 private static List<ITimeEvent> getEventList(CallStackEntry entry,
940 long startTime, long endTime, long resolution,
941 IProgressMonitor monitor) {
942 ITmfStateSystem ss = entry.getStateSystem();
943 long start = Math.max(startTime, ss.getStartTime());
944 long end = Math.min(endTime, ss.getCurrentEndTime() + 1);
945 if (end <= start) {
946 return null;
947 }
948 List<ITimeEvent> eventList = null;
949 try {
950 List<ITmfStateInterval> stackIntervals = StateSystemUtils.queryHistoryRange(ss, entry.getQuark(), start, end - 1, resolution, monitor);
951 eventList = new ArrayList<>(stackIntervals.size());
952 long lastEndTime = -1;
953 boolean lastIsNull = true;
954 for (ITmfStateInterval statusInterval : stackIntervals) {
955 if (monitor.isCanceled()) {
956 return null;
957 }
958 long time = statusInterval.getStartTime();
959 long duration = statusInterval.getEndTime() - time + 1;
960 if (!statusInterval.getStateValue().isNull()) {
961 final int modulo = CallStackPresentationProvider.NUM_COLORS / 2;
962 int value = statusInterval.getStateValue().toString().hashCode() % modulo + modulo;
963 eventList.add(new CallStackEvent(entry, time, duration, value));
964 lastIsNull = false;
965 } else {
966 if (lastEndTime == -1) {
967 // add null event if it intersects the start time
968 eventList.add(new NullTimeEvent(entry, time, duration));
969 } else {
970 if (lastEndTime != time && lastIsNull) {
971 // add unknown event if between two null states
972 eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime));
973 }
974 if (time + duration >= endTime) {
975 // add null event if it intersects the end time
976 eventList.add(new NullTimeEvent(entry, time, duration));
977 }
978 }
979 lastIsNull = true;
980 }
981 lastEndTime = time + duration;
982 }
983 } catch (AttributeNotFoundException e) {
984 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
985 } catch (TimeRangeException e) {
986 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
987 } catch (StateSystemDisposedException e) {
988 /* Ignored */
989 }
990 return eventList;
991 }
992
993 private void synchingToTime(long time) {
994 if (fEntryList == null) {
995 return;
996 }
997 for (TraceEntry traceEntry : fEntryList) {
998 for (ITimeGraphEntry threadEntry : traceEntry.getChildren()) {
999 ITmfStateSystem ss = ((ThreadEntry) threadEntry).getStateSystem();
1000 if (ss == null) {
1001 continue;
1002 }
1003 if (ss.isCancelled()) {
1004 continue;
1005 }
1006 if (time < ss.getStartTime() || time > ss.getCurrentEndTime()) {
1007 continue;
1008 }
1009 for (ITimeGraphEntry child : threadEntry.getChildren()) {
1010 CallStackEntry callStackEntry = (CallStackEntry) child;
1011 try {
1012 ITmfStateInterval stackLevelInterval = ss.querySingleState(time, callStackEntry.getQuark());
1013 ITmfStateValue nameValue = stackLevelInterval.getStateValue();
1014 String name = ""; //$NON-NLS-1$
1015 try {
1016 if (nameValue.getType() == Type.STRING) {
1017 String address = nameValue.unboxStr();
1018 name = getFunctionName(address);
1019 } else if (nameValue.getType() == Type.INTEGER) {
1020 name = "0x" + Integer.toHexString(nameValue.unboxInt()); //$NON-NLS-1$
1021 } else if (nameValue.getType() == Type.LONG) {
1022 name = "0x" + Long.toHexString(nameValue.unboxLong()); //$NON-NLS-1$
1023 }
1024 } catch (StateValueTypeException e) {
1025 }
1026 callStackEntry.setFunctionName(name);
1027 if (name.length() > 0) {
1028 callStackEntry.setFunctionEntryTime(stackLevelInterval.getStartTime());
1029 callStackEntry.setFunctionExitTime(stackLevelInterval.getEndTime() + 1);
1030 }
1031 } catch (AttributeNotFoundException e) {
1032 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1033 } catch (StateSystemDisposedException e) {
1034 /* Ignored */
1035 }
1036 }
1037 }
1038 }
1039 fTimeGraphCombo.refresh();
1040 }
1041
1042 private void refresh() {
1043 Display.getDefault().asyncExec(new Runnable() {
1044 @Override
1045 public void run() {
1046 if (fTimeGraphCombo.isDisposed()) {
1047 return;
1048 }
1049 synchronized (fEntryListMap) {
1050 fEntryList = fEntryListMap.get(fTrace);
1051 if (fEntryList == null) {
1052 fEntryList = new ArrayList<>();
1053 }
1054 for (TraceEntry traceEntry : fEntryList) {
1055 traceEntry.sortChildren(fThreadComparator);
1056 }
1057 }
1058 if (fEntryList != fTimeGraphCombo.getInput()) {
1059 fTimeGraphCombo.setInput(fEntryList);
1060 } else {
1061 fTimeGraphCombo.refresh();
1062 }
1063 fTimeGraphCombo.getTimeGraphViewer().setTimeBounds(fStartTime, fEndTime);
1064
1065 TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
1066 long selectionBeginTime = fTrace == null ? 0 : ctx.getSelectionRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
1067 long selectionEndTime = fTrace == null ? 0 : ctx.getSelectionRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
1068 long startTime = fTrace == null ? 0 : ctx.getWindowRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
1069 long endTime = fTrace == null ? 0 : ctx.getWindowRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
1070 startTime = Math.max(startTime, fStartTime);
1071 endTime = Math.min(endTime, fEndTime);
1072 fTimeGraphCombo.getTimeGraphViewer().setSelectionRange(selectionBeginTime, selectionEndTime);
1073 synchingToTime(selectionBeginTime);
1074 fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
1075 startZoomThread(startTime, endTime);
1076 }
1077 });
1078 }
1079
1080 private void redraw() {
1081 synchronized (fSyncObj) {
1082 if (fRedrawState == State.IDLE) {
1083 fRedrawState = State.BUSY;
1084 } else {
1085 fRedrawState = State.PENDING;
1086 return;
1087 }
1088 }
1089 Display.getDefault().asyncExec(new Runnable() {
1090 @Override
1091 public void run() {
1092 if (fTimeGraphCombo.isDisposed()) {
1093 return;
1094 }
1095 fTimeGraphCombo.redraw();
1096 fTimeGraphCombo.update();
1097 synchronized (fSyncObj) {
1098 if (fRedrawState == State.PENDING) {
1099 fRedrawState = State.IDLE;
1100 redraw();
1101 } else {
1102 fRedrawState = State.IDLE;
1103 }
1104 }
1105 }
1106 });
1107 }
1108
1109 private void startZoomThread(long startTime, long endTime) {
1110 if (fZoomThread != null) {
1111 fZoomThread.cancel();
1112 }
1113 fZoomThread = new ZoomThread(fEntryList, startTime, endTime);
1114 fZoomThread.start();
1115 }
1116
1117 private void makeActions() {
1118 fPreviousItemAction = fTimeGraphCombo.getTimeGraphViewer().getPreviousItemAction();
1119 fPreviousItemAction.setText(Messages.TmfTimeGraphViewer_PreviousItemActionNameText);
1120 fPreviousItemAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousItemActionToolTipText);
1121 fNextItemAction = fTimeGraphCombo.getTimeGraphViewer().getNextItemAction();
1122 fNextItemAction.setText(Messages.TmfTimeGraphViewer_NextItemActionNameText);
1123 fNextItemAction.setToolTipText(Messages.TmfTimeGraphViewer_NextItemActionToolTipText);
1124 }
1125
1126 private void contributeToActionBars() {
1127 IActionBars bars = getViewSite().getActionBars();
1128 fillLocalToolBar(bars.getToolBarManager());
1129
1130 // Create pin action
1131 contributePinActionToToolBar();
1132 fPinAction.addPropertyChangeListener(new IPropertyChangeListener() {
1133 @Override
1134 public void propertyChange(PropertyChangeEvent event) {
1135 if (IAction.CHECKED.equals(event.getProperty()) && !isPinned()) {
1136 if (fSavedRangeSyncSignal != null) {
1137 windowRangeUpdated(fSavedRangeSyncSignal);
1138 fSavedRangeSyncSignal = null;
1139 }
1140
1141 if (fSavedTimeSyncSignal != null) {
1142 selectionRangeUpdated(fSavedTimeSyncSignal);
1143 fSavedTimeSyncSignal = null;
1144 }
1145 }
1146 }
1147 });
1148 }
1149
1150 private void fillLocalToolBar(IToolBarManager manager) {
1151 manager.add(getImportBinaryAction());
1152 manager.add(getImportMappingAction());
1153 manager.add(new Separator());
1154 manager.add(getSortByNameAction());
1155 manager.add(getSortByIdAction());
1156 manager.add(getSortByTimeAction());
1157 manager.add(new Separator());
1158 manager.add(fTimeGraphCombo.getTimeGraphViewer().getResetScaleAction());
1159 manager.add(getPreviousEventAction());
1160 manager.add(getNextEventAction());
1161 manager.add(fPreviousItemAction);
1162 manager.add(fNextItemAction);
1163 manager.add(fTimeGraphCombo.getTimeGraphViewer().getZoomInAction());
1164 manager.add(fTimeGraphCombo.getTimeGraphViewer().getZoomOutAction());
1165 manager.add(new Separator());
1166 }
1167
1168 private void createContextMenu() {
1169 final MenuManager contextMenu = new MenuManager();
1170 contextMenu.add(getSortByNameAction());
1171 contextMenu.add(getSortByIdAction());
1172 contextMenu.add(getSortByTimeAction());
1173
1174 Tree tree = fTimeGraphCombo.getTreeViewer().getTree();
1175 Menu menu = contextMenu.createContextMenu(tree);
1176 tree.setMenu(menu);
1177 }
1178
1179 /**
1180 * Get the the next event action.
1181 *
1182 * @return The action object
1183 */
1184 private Action getNextEventAction() {
1185 if (fNextEventAction == null) {
1186 fNextEventAction = new Action() {
1187 @Override
1188 public void run() {
1189 TimeGraphViewer viewer = fTimeGraphCombo.getTimeGraphViewer();
1190 ITimeGraphEntry entry = viewer.getSelection();
1191 if (entry instanceof CallStackEntry) {
1192 try {
1193 CallStackEntry callStackEntry = (CallStackEntry) entry;
1194 ITmfStateSystem ss = callStackEntry.getStateSystem();
1195 long time = Math.max(ss.getStartTime(), Math.min(ss.getCurrentEndTime(), viewer.getSelectionBegin()));
1196 ThreadEntry threadEntry = (ThreadEntry) callStackEntry.getParent();
1197 int quark = ss.getParentAttributeQuark(callStackEntry.getQuark());
1198 ITmfStateInterval stackInterval = ss.querySingleState(time, quark);
1199 long newTime = stackInterval.getEndTime() + 1;
1200 viewer.setSelectedTimeNotify(newTime, true);
1201 stackInterval = ss.querySingleState(Math.min(ss.getCurrentEndTime(), newTime), quark);
1202 int stackLevel = stackInterval.getStateValue().unboxInt();
1203 ITimeGraphEntry selectedEntry = threadEntry.getChildren().get(Math.max(0, stackLevel - 1));
1204 fTimeGraphCombo.setSelection(selectedEntry);
1205 viewer.getTimeGraphControl().fireSelectionChanged();
1206 startZoomThread(viewer.getTime0(), viewer.getTime1());
1207
1208 } catch (AttributeNotFoundException | TimeRangeException | StateSystemDisposedException | StateValueTypeException e) {
1209 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1210 }
1211 }
1212 }
1213 };
1214
1215 fNextEventAction.setText(Messages.TmfTimeGraphViewer_NextEventActionNameText);
1216 fNextEventAction.setToolTipText(Messages.TmfTimeGraphViewer_NextEventActionToolTipText);
1217 fNextEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_EVENT));
1218 }
1219
1220 return fNextEventAction;
1221 }
1222
1223 /**
1224 * Get the previous event action.
1225 *
1226 * @return The Action object
1227 */
1228 private Action getPreviousEventAction() {
1229 if (fPrevEventAction == null) {
1230 fPrevEventAction = new Action() {
1231 @Override
1232 public void run() {
1233 TimeGraphViewer viewer = fTimeGraphCombo.getTimeGraphViewer();
1234 ITimeGraphEntry entry = viewer.getSelection();
1235 if (entry instanceof CallStackEntry) {
1236 try {
1237 CallStackEntry callStackEntry = (CallStackEntry) entry;
1238 ITmfStateSystem ss = callStackEntry.getStateSystem();
1239 long time = Math.max(ss.getStartTime(), Math.min(ss.getCurrentEndTime(), viewer.getSelectionBegin()));
1240 ThreadEntry threadEntry = (ThreadEntry) callStackEntry.getParent();
1241 int quark = ss.getParentAttributeQuark(callStackEntry.getQuark());
1242 ITmfStateInterval stackInterval = ss.querySingleState(time, quark);
1243 if (stackInterval.getStartTime() == time && time > ss.getStartTime()) {
1244 stackInterval = ss.querySingleState(time - 1, quark);
1245 }
1246 viewer.setSelectedTimeNotify(stackInterval.getStartTime(), true);
1247 int stackLevel = stackInterval.getStateValue().unboxInt();
1248 ITimeGraphEntry selectedEntry = threadEntry.getChildren().get(Math.max(0, stackLevel - 1));
1249 fTimeGraphCombo.setSelection(selectedEntry);
1250 viewer.getTimeGraphControl().fireSelectionChanged();
1251 startZoomThread(viewer.getTime0(), viewer.getTime1());
1252
1253 } catch (AttributeNotFoundException | TimeRangeException | StateSystemDisposedException | StateValueTypeException e) {
1254 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1255 }
1256 }
1257 }
1258 };
1259
1260 fPrevEventAction.setText(Messages.TmfTimeGraphViewer_PreviousEventActionNameText);
1261 fPrevEventAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousEventActionToolTipText);
1262 fPrevEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_EVENT));
1263 }
1264
1265 return fPrevEventAction;
1266 }
1267
1268 private static @Nullable AbstractCallStackAnalysis getCallStackModule(@NonNull ITmfTrace trace) {
1269 /*
1270 * Since we cannot know the exact analysis ID (in separate plugins), we
1271 * will search using the analysis type.
1272 */
1273 Iterable<AbstractCallStackAnalysis> modules =
1274 TmfTraceUtils.getAnalysisModulesOfClass(trace, AbstractCallStackAnalysis.class);
1275 Iterator<AbstractCallStackAnalysis> it = modules.iterator();
1276 if (!it.hasNext()) {
1277 /* This trace does not provide a call-stack analysis */
1278 return null;
1279 }
1280
1281 /*
1282 * We only look at the first module we find.
1283 *
1284 * TODO Handle the advanced case where one trace provides more than one
1285 * call-stack analysis.
1286 */
1287 AbstractCallStackAnalysis module = it.next();
1288 /* This analysis is not automatic, we need to schedule it on-demand */
1289 module.schedule();
1290 module.waitForInitialization();
1291 return module;
1292 }
1293
1294 // ------------------------------------------------------------------------
1295 // Methods related to function name mapping
1296 // ------------------------------------------------------------------------
1297
1298 /**
1299 * Common code for all import file mapping actions
1300 */
1301 private abstract class AbstractImportFileMappingAction extends Action {
1302 private final String fDialogTitle;
1303
1304 private AbstractImportFileMappingAction(String dialogTitle) {
1305 fDialogTitle = dialogTitle;
1306 }
1307
1308 @Override
1309 public void run() {
1310 FileDialog dialog = new FileDialog(getViewSite().getShell());
1311 dialog.setText(fDialogTitle);
1312 final String filePath = dialog.open();
1313 if (filePath == null) {
1314 /* No file was selected, don't change anything */
1315 return;
1316 }
1317
1318 /*
1319 * Start the mapping import in a separate thread (we do not want to
1320 * UI thread to do this).
1321 */
1322 Job job = new Job(Messages.CallStackView_ImportMappingJobName) {
1323 @Override
1324 public IStatus run(IProgressMonitor monitor) {
1325 fNameMapping = doMapping(new File(filePath));
1326
1327 /* Refresh call stack entries and event labels */
1328 Display.getDefault().asyncExec(new Runnable() {
1329 @Override
1330 public void run() {
1331 synchingToTime(fTimeGraphCombo.getTimeGraphViewer().getSelectionBegin());
1332 }
1333 });
1334 return Status.OK_STATUS;
1335 }
1336 };
1337 job.schedule();
1338 }
1339
1340 abstract Map<String, String> doMapping(File file);
1341 }
1342
1343 /**
1344 * Toolbar icon to import the function address-to-name mapping file.
1345 */
1346 private Action getImportMappingAction() {
1347 if (fImportMappingAction != null) {
1348 return fImportMappingAction;
1349 }
1350 fImportMappingAction = new AbstractImportFileMappingAction(Messages.CallStackView_ImportMappingDialogTitle) {
1351 @Override
1352 Map<String, String> doMapping(File file) {
1353 return FunctionNameMapper.mapFromNmTextFile(file);
1354 }
1355 };
1356
1357 fImportMappingAction.setText(Messages.CallStackView_ImportMappingButtonText);
1358 fImportMappingAction.setToolTipText(Messages.CallStackView_ImportMappingButtonTooltip);
1359 fImportMappingAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(IMPORT_MAPPING_ICON_PATH));
1360
1361 return fImportMappingAction;
1362 }
1363
1364 private Action getSortByNameAction() {
1365 if (fSortByNameAction == null) {
1366 fSortByNameAction = new Action(Messages.CallStackView_SortByThreadName, IAction.AS_CHECK_BOX) {
1367 @Override
1368 public void run() {
1369 if (fSortOption == SortOption.BY_NAME) {
1370 saveSortOption(SortOption.BY_NAME_REV);
1371 } else {
1372 saveSortOption(SortOption.BY_NAME);
1373 }
1374 }
1375 };
1376 fSortByNameAction.setToolTipText(Messages.CallStackView_SortByThreadName);
1377 fSortByNameAction.setImageDescriptor(SORT_BY_NAME_ICON);
1378 }
1379 return fSortByNameAction;
1380 }
1381
1382 private Action getSortByIdAction() {
1383 if (fSortByIdAction == null) {
1384 fSortByIdAction = new Action(Messages.CallStackView_SortByThreadId, IAction.AS_CHECK_BOX) {
1385 @Override
1386 public void run() {
1387 if (fSortOption == SortOption.BY_ID) {
1388 saveSortOption(SortOption.BY_ID_REV);
1389 } else {
1390 saveSortOption(SortOption.BY_ID);
1391 }
1392 }
1393 };
1394 fSortByIdAction.setToolTipText(Messages.CallStackView_SortByThreadId);
1395 fSortByIdAction.setImageDescriptor(SORT_BY_ID_ICON);
1396 }
1397 return fSortByIdAction;
1398 }
1399
1400 private Action getSortByTimeAction() {
1401 if (fSortByTimeAction == null) {
1402 fSortByTimeAction = new Action(Messages.CallStackView_SortByThreadTime, IAction.AS_CHECK_BOX) {
1403 @Override
1404 public void run() {
1405 if (fSortOption == SortOption.BY_TIME) {
1406 saveSortOption(SortOption.BY_TIME_REV);
1407 } else {
1408 saveSortOption(SortOption.BY_TIME);
1409 }
1410 }
1411 };
1412 fSortByTimeAction.setToolTipText(Messages.CallStackView_SortByThreadTime);
1413 fSortByTimeAction.setImageDescriptor(SORT_BY_TIME_ICON);
1414 }
1415 return fSortByTimeAction;
1416 }
1417
1418 private void loadSortOption() {
1419 IDialogSettings settings = Activator.getDefault().getDialogSettings();
1420 IDialogSettings section = settings.getSection(getClass().getName());
1421 if (section == null) {
1422 return;
1423 }
1424 String sortOption = section.get(SORT_OPTION_KEY);
1425 if (sortOption == null) {
1426 return;
1427 }
1428
1429 // reset defaults
1430 getSortByNameAction().setChecked(false);
1431 getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON);
1432 getSortByIdAction().setChecked(false);
1433 getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON);
1434 getSortByTimeAction().setChecked(false);
1435 getSortByTimeAction().setImageDescriptor(SORT_BY_TIME_ICON);
1436
1437 if (sortOption.equals(SortOption.BY_NAME.name())) {
1438 fSortOption = SortOption.BY_NAME;
1439 fThreadComparator = new ThreadNameComparator(false);
1440 getSortByNameAction().setChecked(true);
1441 } else if (sortOption.equals(SortOption.BY_NAME_REV.name())) {
1442 fSortOption = SortOption.BY_NAME_REV;
1443 fThreadComparator = new ThreadNameComparator(true);
1444 getSortByNameAction().setChecked(true);
1445 getSortByNameAction().setImageDescriptor(SORT_BY_NAME_REV_ICON);
1446 } else if (sortOption.equals(SortOption.BY_ID.name())) {
1447 fSortOption = SortOption.BY_ID;
1448 fThreadComparator = new ThreadIdComparator(false);
1449 getSortByIdAction().setChecked(true);
1450 } else if (sortOption.equals(SortOption.BY_ID_REV.name())) {
1451 fSortOption = SortOption.BY_ID_REV;
1452 fThreadComparator = new ThreadIdComparator(true);
1453 getSortByIdAction().setChecked(true);
1454 getSortByIdAction().setImageDescriptor(SORT_BY_ID_REV_ICON);
1455 } else if (sortOption.equals(SortOption.BY_TIME.name())) {
1456 fSortOption = SortOption.BY_TIME;
1457 fThreadComparator = new ThreadTimeComparator(false);
1458 getSortByTimeAction().setChecked(true);
1459 } else if (sortOption.equals(SortOption.BY_TIME_REV.name())) {
1460 fSortOption = SortOption.BY_TIME_REV;
1461 fThreadComparator = new ThreadTimeComparator(true);
1462 getSortByTimeAction().setChecked(true);
1463 getSortByTimeAction().setImageDescriptor(SORT_BY_TIME_REV_ICON);
1464 }
1465 }
1466
1467 private void saveSortOption(SortOption sortOption) {
1468 IDialogSettings settings = Activator.getDefault().getDialogSettings();
1469 IDialogSettings section = settings.getSection(getClass().getName());
1470 if (section == null) {
1471 section = settings.addNewSection(getClass().getName());
1472 }
1473 section.put(SORT_OPTION_KEY, sortOption.name());
1474 loadSortOption();
1475 if (fEntryList == null) {
1476 return;
1477 }
1478 for (TraceEntry traceEntry : fEntryList) {
1479 traceEntry.sortChildren(fThreadComparator);
1480 }
1481 refresh();
1482 }
1483
1484 /**
1485 * Toolbar icon to import the function address-to-name mapping binary file.
1486 */
1487 private Action getImportBinaryAction() {
1488 if (fImportBinaryFileMappingAction != null) {
1489 return fImportBinaryFileMappingAction;
1490 }
1491
1492 fImportBinaryFileMappingAction = new AbstractImportFileMappingAction(Messages.CallStackView_ImportBinaryFileDialogTitle) {
1493 @Override
1494 Map<String, String> doMapping(File file) {
1495 return FunctionNameMapper.mapFromBinaryFile(file);
1496 }
1497 };
1498
1499 fImportBinaryFileMappingAction.setText(Messages.CallStackView_ImportBinaryFileButtonText);
1500 fImportBinaryFileMappingAction.setToolTipText(Messages.CallStackView_ImportBinaryFileButtonTooltip);
1501 fImportBinaryFileMappingAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(IMPORT_BINARY_ICON_PATH));
1502
1503 return fImportBinaryFileMappingAction;
1504 }
1505
1506 String getFunctionName(String address) {
1507 if (fNameMapping == null) {
1508 /* No mapping available, just print the addresses */
1509 return address;
1510 }
1511 String ret = fNameMapping.get(address);
1512 if (ret == null) {
1513 /*
1514 * We didn't find this address in the mapping file, just use the
1515 * address
1516 */
1517 return address;
1518 }
1519 return ret;
1520 }
1521
1522 /**
1523 * @since 1.0
1524 */
1525 @Override
1526 public TmfTimeViewAlignmentInfo getTimeViewAlignmentInfo() {
1527 if (fTimeGraphCombo == null) {
1528 return null;
1529 }
1530 return fTimeGraphCombo.getTimeViewAlignmentInfo();
1531 }
1532
1533 /**
1534 * @since 1.0
1535 */
1536 @Override
1537 public int getAvailableWidth(int requestedOffset) {
1538 if (fTimeGraphCombo == null) {
1539 return 0;
1540 }
1541 return fTimeGraphCombo.getAvailableWidth(requestedOffset);
1542 }
1543
1544 /**
1545 * @since 1.0
1546 */
1547 @Override
1548 public void performAlign(int offset, int width) {
1549 fTimeGraphCombo.performAlign(offset, width);
1550 }
1551 }
This page took 0.078589 seconds and 5 git commands to generate.