1 /*******************************************************************************
2 * Copyright (c) 2013, 2016 Ericsson and others.
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
10 * Patrick Tasse - Initial API and implementation
11 * Bernd Hufmann - Updated signal handling
12 * Marc-Andre Laperle - Map from binary file
13 * Mikael Ferland - Support multiple symbol providers for a trace
14 *******************************************************************************/
16 package org
.eclipse
.tracecompass
.tmf
.ui
.views
.callstack
;
18 import java
.util
.ArrayList
;
19 import java
.util
.Collection
;
20 import java
.util
.Collections
;
21 import java
.util
.Comparator
;
22 import java
.util
.HashMap
;
23 import java
.util
.Iterator
;
24 import java
.util
.List
;
26 import java
.util
.function
.Consumer
;
28 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
29 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
30 import org
.eclipse
.jdt
.annotation
.NonNull
;
31 import org
.eclipse
.jdt
.annotation
.Nullable
;
32 import org
.eclipse
.jface
.action
.Action
;
33 import org
.eclipse
.jface
.action
.GroupMarker
;
34 import org
.eclipse
.jface
.action
.IAction
;
35 import org
.eclipse
.jface
.action
.IMenuManager
;
36 import org
.eclipse
.jface
.action
.IToolBarManager
;
37 import org
.eclipse
.jface
.action
.Separator
;
38 import org
.eclipse
.jface
.dialogs
.IDialogConstants
;
39 import org
.eclipse
.jface
.dialogs
.IDialogSettings
;
40 import org
.eclipse
.jface
.resource
.ImageDescriptor
;
41 import org
.eclipse
.jface
.util
.IPropertyChangeListener
;
42 import org
.eclipse
.jface
.util
.PropertyChangeEvent
;
43 import org
.eclipse
.jface
.viewers
.ISelection
;
44 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
45 import org
.eclipse
.swt
.SWT
;
46 import org
.eclipse
.swt
.events
.MouseAdapter
;
47 import org
.eclipse
.swt
.events
.MouseEvent
;
48 import org
.eclipse
.swt
.graphics
.Image
;
49 import org
.eclipse
.swt
.widgets
.Composite
;
50 import org
.eclipse
.swt
.widgets
.Display
;
51 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Activator
;
52 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.ITmfImageConstants
;
53 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Messages
;
54 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
55 import org
.eclipse
.tracecompass
.statesystem
.core
.StateSystemUtils
;
56 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
57 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
58 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
59 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
60 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
61 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
62 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
.Type
;
63 import org
.eclipse
.tracecompass
.tmf
.core
.callstack
.CallStackAnalysis
;
64 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSelectionRangeUpdatedSignal
;
65 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
66 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceClosedSignal
;
67 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceSelectedSignal
;
68 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfWindowRangeUpdatedSignal
;
69 import org
.eclipse
.tracecompass
.tmf
.core
.symbols
.ISymbolProvider
;
70 import org
.eclipse
.tracecompass
.tmf
.core
.symbols
.SymbolProviderManager
;
71 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.ITmfTimestamp
;
72 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
73 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestamp
;
74 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestampDelta
;
75 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
76 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
77 import org
.eclipse
.tracecompass
.tmf
.ui
.editors
.ITmfTraceEditor
;
78 import org
.eclipse
.tracecompass
.tmf
.ui
.symbols
.ISymbolProviderPreferencePage
;
79 import org
.eclipse
.tracecompass
.tmf
.ui
.symbols
.SymbolProviderConfigDialog
;
80 import org
.eclipse
.tracecompass
.tmf
.ui
.views
.timegraph
.AbstractTimeGraphView
;
81 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.ITimeGraphTimeListener
;
82 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.TimeGraphContentProvider
;
83 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.TimeGraphTimeEvent
;
84 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.TimeGraphViewer
;
85 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeEvent
;
86 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
87 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.NullTimeEvent
;
88 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeEvent
;
89 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeGraphEntry
;
90 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.widgets
.TimeGraphControl
;
91 import org
.eclipse
.ui
.IEditorPart
;
92 import org
.eclipse
.ui
.IWorkbenchActionConstants
;
94 import com
.google
.common
.collect
.LinkedHashMultimap
;
95 import com
.google
.common
.collect
.Multimap
;
98 * Main implementation for the Call Stack view
100 * @author Patrick Tasse
102 public class CallStackView
extends AbstractTimeGraphView
{
104 // ------------------------------------------------------------------------
106 // ------------------------------------------------------------------------
109 public static final @NonNull String ID
= "org.eclipse.linuxtools.tmf.ui.views.callstack"; //$NON-NLS-1$
111 private static final String
[] COLUMN_NAMES
= new String
[] {
112 Messages
.CallStackView_FunctionColumn
,
113 Messages
.CallStackView_DepthColumn
,
114 Messages
.CallStackView_EntryTimeColumn
,
115 Messages
.CallStackView_ExitTimeColumn
,
116 Messages
.CallStackView_DurationColumn
119 private static final String
[] FILTER_COLUMN_NAMES
= new String
[] {
120 Messages
.CallStackView_ThreadColumn
123 /** Timeout between updates in the build thread in ms */
124 private static final long BUILD_UPDATE_TIMEOUT
= 500;
126 private static final Image PROCESS_IMAGE
= Activator
.getDefault().getImageFromPath("icons/obj16/process_obj.gif"); //$NON-NLS-1$
127 private static final Image THREAD_IMAGE
= Activator
.getDefault().getImageFromPath("icons/obj16/thread_obj.gif"); //$NON-NLS-1$
128 private static final Image STACKFRAME_IMAGE
= Activator
.getDefault().getImageFromPath("icons/obj16/stckframe_obj.gif"); //$NON-NLS-1$
130 private static final String IMPORT_BINARY_ICON_PATH
= "icons/obj16/binaries_obj.gif"; //$NON-NLS-1$
132 private static final ImageDescriptor SORT_BY_NAME_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha.gif"); //$NON-NLS-1$
133 private static final ImageDescriptor SORT_BY_NAME_REV_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_alpha_rev.gif"); //$NON-NLS-1$
134 private static final ImageDescriptor SORT_BY_ID_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_num.gif"); //$NON-NLS-1$
135 private static final ImageDescriptor SORT_BY_ID_REV_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_num_rev.gif"); //$NON-NLS-1$
136 private static final ImageDescriptor SORT_BY_TIME_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_time.gif"); //$NON-NLS-1$
137 private static final ImageDescriptor SORT_BY_TIME_REV_ICON
= Activator
.getDefault().getImageDescripterFromPath("icons/etool16/sort_time_rev.gif"); //$NON-NLS-1$
138 private static final String SORT_OPTION_KEY
= "sort.option"; //$NON-NLS-1$
140 private enum SortOption
{
141 BY_NAME
, BY_NAME_REV
, BY_ID
, BY_ID_REV
, BY_TIME
, BY_TIME_REV
144 private @NonNull SortOption fSortOption
= SortOption
.BY_NAME
;
145 private @NonNull Comparator
<ITimeGraphEntry
> fThreadComparator
= new ThreadNameComparator(false);
146 private Action fSortByNameAction
;
147 private Action fSortByIdAction
;
148 private Action fSortByTimeAction
;
150 // ------------------------------------------------------------------------
152 // ------------------------------------------------------------------------
154 private final Multimap
<ITmfTrace
, ISymbolProvider
> fSymbolProviders
= LinkedHashMultimap
.create();
156 // The next event action
157 private Action fNextEventAction
;
159 // The previous event action
160 private Action fPrevEventAction
;
162 // The next item action
163 private Action fNextItemAction
;
165 // The previous item action
166 private Action fPreviousItemAction
;
168 // The action to import a binary file mapping */
169 private Action fConfigureSymbolsAction
;
171 // The saved time sync. signal used when switching off the pinning of a view
172 private TmfSelectionRangeUpdatedSignal fSavedTimeSyncSignal
;
174 // The saved window range signal used when switching off the pinning of
176 private TmfWindowRangeUpdatedSignal fSavedRangeSyncSignal
;
178 // When set to true, syncToTime() will select the first call stack entry
179 // whose current state start time exactly matches the sync time.
180 private boolean fSyncSelection
= false;
182 // ------------------------------------------------------------------------
184 // ------------------------------------------------------------------------
186 private static class TraceEntry
extends TimeGraphEntry
{
187 public TraceEntry(String name
, long startTime
, long endTime
) {
188 super(name
, startTime
, endTime
);
192 public boolean hasTimeEvents() {
197 private static class ProcessEntry
extends TimeGraphEntry
{
199 private final int fProcessId
;
201 public ProcessEntry(String name
, int processId
, long startTime
, long endTime
) {
202 super(name
, startTime
, endTime
);
203 fProcessId
= processId
;
207 public boolean hasTimeEvents() {
212 private static class ThreadEntry
extends TimeGraphEntry
{
214 private final long fThreadId
;
216 public ThreadEntry(String name
, long threadId
, long startTime
, long endTime
) {
217 super(name
, startTime
, endTime
);
218 fThreadId
= threadId
;
222 public boolean hasTimeEvents() {
226 public long getThreadId() {
231 private class CallStackComparator
implements Comparator
<ITimeGraphEntry
> {
233 public int compare(ITimeGraphEntry o1
, ITimeGraphEntry o2
) {
234 if (o1
instanceof ThreadEntry
&& o2
instanceof ThreadEntry
) {
235 return fThreadComparator
.compare(o1
, o2
);
236 } else if (o1
instanceof ProcessEntry
&& o2
instanceof ProcessEntry
) {
237 return Integer
.compare(((ProcessEntry
) o1
).fProcessId
, ((ProcessEntry
) o2
).fProcessId
);
243 private static class ThreadNameComparator
implements Comparator
<ITimeGraphEntry
> {
244 private boolean reverse
;
246 public ThreadNameComparator(boolean reverse
) {
247 this.reverse
= reverse
;
251 public int compare(ITimeGraphEntry o1
, ITimeGraphEntry o2
) {
252 return reverse ? o2
.getName().compareTo(o1
.getName()) : o1
.getName().compareTo(o2
.getName());
256 private static class ThreadIdComparator
implements Comparator
<ITimeGraphEntry
> {
257 private boolean reverse
;
259 public ThreadIdComparator(boolean reverse
) {
260 this.reverse
= reverse
;
264 public int compare(ITimeGraphEntry o1
, ITimeGraphEntry o2
) {
265 if (o1
instanceof ThreadEntry
&& o2
instanceof ThreadEntry
) {
266 ThreadEntry t1
= (ThreadEntry
) o1
;
267 ThreadEntry t2
= (ThreadEntry
) o2
;
268 return reverse ? Long
.compare(t2
.getThreadId(), t1
.getThreadId()) : Long
.compare(t1
.getThreadId(), t2
.getThreadId());
274 private static class ThreadTimeComparator
implements Comparator
<ITimeGraphEntry
> {
275 private boolean reverse
;
277 public ThreadTimeComparator(boolean reverse
) {
278 this.reverse
= reverse
;
282 public int compare(ITimeGraphEntry o1
, ITimeGraphEntry o2
) {
283 return reverse ? Long
.compare(o2
.getStartTime(), o1
.getStartTime()) : Long
.compare(o1
.getStartTime(), o2
.getStartTime());
287 private static class CallStackTreeLabelProvider
extends TreeLabelProvider
{
290 public Image
getColumnImage(Object element
, int columnIndex
) {
291 if (columnIndex
== 0) {
292 if (element
instanceof ProcessEntry
) {
293 return PROCESS_IMAGE
;
294 } else if (element
instanceof ThreadEntry
) {
296 } else if (element
instanceof CallStackEntry
) {
297 CallStackEntry entry
= (CallStackEntry
) element
;
298 if (entry
.getFunctionName().length() > 0) {
299 return STACKFRAME_IMAGE
;
307 public String
getColumnText(Object element
, int columnIndex
) {
308 if (element
instanceof CallStackEntry
) {
309 CallStackEntry entry
= (CallStackEntry
) element
;
310 if (columnIndex
== 0) {
311 return entry
.getFunctionName();
312 } else if (columnIndex
== 1 && entry
.getFunctionName().length() > 0) {
313 int depth
= entry
.getStackLevel();
314 return Integer
.toString(depth
);
315 } else if (columnIndex
== 2 && entry
.getFunctionName().length() > 0) {
316 ITmfTimestamp ts
= TmfTimestamp
.fromNanos(entry
.getFunctionEntryTime());
317 return ts
.toString();
318 } else if (columnIndex
== 3 && entry
.getFunctionName().length() > 0) {
319 ITmfTimestamp ts
= TmfTimestamp
.fromNanos(entry
.getFunctionExitTime());
320 return ts
.toString();
321 } else if (columnIndex
== 4 && entry
.getFunctionName().length() > 0) {
322 ITmfTimestamp ts
= new TmfTimestampDelta(entry
.getFunctionExitTime() - entry
.getFunctionEntryTime(), ITmfTimestamp
.NANOSECOND_SCALE
);
323 return ts
.toString();
325 } else if (element
instanceof ITimeGraphEntry
) {
326 if (columnIndex
== 0) {
327 return ((ITimeGraphEntry
) element
).getName();
330 return ""; //$NON-NLS-1$
335 private class CallStackFilterContentProvider
extends TimeGraphContentProvider
{
337 public boolean hasChildren(Object element
) {
338 if (element
instanceof TraceEntry
) {
339 return super.hasChildren(element
);
345 public ITimeGraphEntry
[] getChildren(Object parentElement
) {
346 if (parentElement
instanceof TraceEntry
) {
347 return super.getChildren(parentElement
);
349 return new ITimeGraphEntry
[0];
353 // ------------------------------------------------------------------------
355 // ------------------------------------------------------------------------
358 * Default constructor
360 public CallStackView() {
361 super(ID
, new CallStackPresentationProvider());
362 getPresentationProvider().setCallStackView(this);
363 setTreeColumns(COLUMN_NAMES
);
364 setTreeLabelProvider(new CallStackTreeLabelProvider());
365 setEntryComparator(new CallStackComparator());
366 setFilterColumns(FILTER_COLUMN_NAMES
);
367 setFilterContentProvider(new CallStackFilterContentProvider());
368 setFilterLabelProvider(new CallStackTreeLabelProvider());
371 // ------------------------------------------------------------------------
373 // ------------------------------------------------------------------------
376 public void createPartControl(Composite parent
) {
377 super.createPartControl(parent
);
379 getTimeGraphViewer().addTimeListener(new ITimeGraphTimeListener() {
381 public void timeSelected(TimeGraphTimeEvent event
) {
382 synchingToTime(event
.getBeginTime());
386 getTimeGraphViewer().getTimeGraphControl().addMouseListener(new MouseAdapter() {
388 public void mouseDoubleClick(MouseEvent event
) {
389 Object selection
= getTimeGraphViewer().getSelection();
390 if (selection
instanceof CallStackEntry
) {
391 CallStackEntry entry
= (CallStackEntry
) selection
;
392 if (entry
.getFunctionName().length() > 0) {
393 long entryTime
= entry
.getFunctionEntryTime();
394 long exitTime
= entry
.getFunctionExitTime();
395 TmfTimeRange range
= new TmfTimeRange(TmfTimestamp
.fromNanos(entryTime
), TmfTimestamp
.fromNanos(exitTime
));
396 broadcast(new TmfWindowRangeUpdatedSignal(CallStackView
.this, range
));
397 getTimeGraphViewer().setStartFinishTime(entryTime
, exitTime
);
398 startZoomThread(entryTime
, exitTime
);
404 getTimeGraphViewer().getTimeGraphControl().addMouseListener(new MouseAdapter() {
406 public void mouseDoubleClick(MouseEvent e
) {
407 TimeGraphControl timeGraphControl
= getTimeGraphViewer().getTimeGraphControl();
408 ISelection selection
= timeGraphControl
.getSelection();
409 if (selection
instanceof IStructuredSelection
) {
410 for (Object object
: ((IStructuredSelection
) selection
).toList()) {
411 if (object
instanceof CallStackEvent
) {
412 CallStackEvent event
= (CallStackEvent
) object
;
413 long startTime
= event
.getTime();
414 long endTime
= startTime
+ event
.getDuration();
415 TmfTimeRange range
= new TmfTimeRange(TmfTimestamp
.fromNanos(startTime
), TmfTimestamp
.fromNanos(endTime
));
416 broadcast(new TmfWindowRangeUpdatedSignal(CallStackView
.this, range
));
417 getTimeGraphViewer().setStartFinishTime(startTime
, endTime
);
418 startZoomThread(startTime
, endTime
);
426 contributeToActionBars();
429 IEditorPart editor
= getSite().getPage().getActiveEditor();
430 if (editor
instanceof ITmfTraceEditor
) {
431 ITmfTrace trace
= ((ITmfTraceEditor
) editor
).getTrace();
433 traceSelected(new TmfTraceSelectedSignal(this, trace
));
439 * Handler for the selection range signal.
442 * The incoming signal
447 public void selectionRangeUpdated(final TmfSelectionRangeUpdatedSignal signal
) {
449 fSavedTimeSyncSignal
= isPinned() ?
new TmfSelectionRangeUpdatedSignal(signal
.getSource(), signal
.getBeginTime(), signal
.getEndTime()) : null;
451 if (signal
.getSource() == this || getTrace() == null || isPinned()) {
454 final long beginTime
= signal
.getBeginTime().toNanos();
455 final long endTime
= signal
.getEndTime().toNanos();
456 Display
.getDefault().asyncExec(new Runnable() {
459 if (getTimeGraphViewer().getControl().isDisposed()) {
462 if (beginTime
== endTime
) {
463 getTimeGraphViewer().setSelectedTime(beginTime
, true);
465 getTimeGraphViewer().setSelectionRange(beginTime
, endTime
, true);
467 fSyncSelection
= true;
468 synchingToTime(beginTime
);
469 fSyncSelection
= false;
470 startZoomThread(getTimeGraphViewer().getTime0(), getTimeGraphViewer().getTime1());
481 public void windowRangeUpdated(final TmfWindowRangeUpdatedSignal signal
) {
484 fSavedRangeSyncSignal
= new TmfWindowRangeUpdatedSignal(signal
.getSource(), signal
.getCurrentRange());
485 fSavedTimeSyncSignal
= null;
488 if ((signal
.getSource() == this) || isPinned()) {
491 super.windowRangeUpdated(signal
);
494 // ------------------------------------------------------------------------
496 // ------------------------------------------------------------------------
502 protected CallStackPresentationProvider
getPresentationProvider() {
503 /* Set to this type by the constructor */
504 return (CallStackPresentationProvider
) super.getPresentationProvider();
509 public void traceClosed(TmfTraceClosedSignal signal
) {
510 super.traceClosed(signal
);
511 synchronized (fSymbolProviders
) {
512 for (ITmfTrace trace
: getTracesToBuild(signal
.getTrace())) {
513 fSymbolProviders
.removeAll(trace
);
522 protected void refresh() {
524 updateConfigureSymbolsAction();
528 protected void buildEntryList(final ITmfTrace trace
, final ITmfTrace parentTrace
, final IProgressMonitor monitor
) {
529 if (monitor
.isCanceled()) {
534 * Load the symbol provider for the current trace, even if it does not
535 * provide a call stack analysis module. See
536 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=494212
538 Collection
<ISymbolProvider
> providers
= fSymbolProviders
.get(trace
);
539 if (providers
.isEmpty()) {
540 providers
= SymbolProviderManager
.getInstance().getSymbolProviders(trace
);
541 providers
.forEach( (provider
) -> provider
.loadConfiguration(new NullProgressMonitor()));
542 fSymbolProviders
.putAll(trace
, providers
);
545 /* Continue with the call stack view specific operations */
546 CallStackAnalysis module
= getCallStackModule(trace
);
547 if (module
== null) {
548 addUnavailableEntry(trace
, parentTrace
);
551 ITmfStateSystem ss
= module
.getStateSystem();
553 addUnavailableEntry(trace
, parentTrace
);
557 Map
<ITmfTrace
, TraceEntry
> traceEntryMap
= new HashMap
<>();
558 Map
<Integer
, ProcessEntry
> processEntryMap
= new HashMap
<>();
559 Map
<Integer
, ThreadEntry
> threadEntryMap
= new HashMap
<>();
561 long start
= ss
.getStartTime();
563 boolean complete
= false;
565 if (monitor
.isCanceled()) {
568 complete
= ss
.waitUntilBuilt(BUILD_UPDATE_TIMEOUT
);
569 if (ss
.isCancelled()) {
572 long end
= ss
.getCurrentEndTime();
573 if (start
== end
&& !complete
) { // when complete execute one last
574 // time regardless of end time
578 TraceEntry traceEntry
= traceEntryMap
.get(trace
);
579 if (traceEntry
== null) {
580 traceEntry
= new TraceEntry(trace
.getName(), start
, end
+ 1);
581 traceEntryMap
.put(trace
, traceEntry
);
582 traceEntry
.sortChildren(fThreadComparator
);
583 addToEntryList(parentTrace
, Collections
.singletonList(traceEntry
));
585 traceEntry
.updateEndTime(end
);
590 * Get quarks first to make sure they are in the full query
593 List
<Integer
> processQuarks
= ss
.getQuarks(module
.getProcessesPattern());
594 List
<ITmfStateInterval
> endStates
= ss
.queryFullState(end
);
595 for (int processQuark
: processQuarks
) {
598 * Default to trace entry, overwrite if a process entry
601 TimeGraphEntry threadParent
= traceEntry
;
603 if (processQuark
!= ITmfStateSystem
.ROOT_ATTRIBUTE
) {
604 /* Create the entry for the process */
605 ProcessEntry processEntry
= processEntryMap
.get(processQuark
);
606 if (processEntry
== null) {
607 String processName
= ss
.getAttributeName(processQuark
);
608 ITmfStateValue processStateValue
= endStates
.get(processQuark
).getStateValue();
609 if (processStateValue
.getType() == Type
.INTEGER
) {
610 processId
= processStateValue
.unboxInt();
613 processId
= Integer
.parseInt(processName
);
614 } catch (NumberFormatException e
) {
615 /* use default processId */
618 processEntry
= new ProcessEntry(processName
, processId
, start
, end
);
619 processEntryMap
.put(processQuark
, processEntry
);
620 traceEntry
.addChild(processEntry
);
622 processEntry
.updateEndTime(end
);
624 /* The parent of the thread entries will be a process */
625 threadParent
= processEntry
;
628 /* Create the threads under the process */
629 List
<Integer
> threadQuarks
= ss
.getQuarks(processQuark
, module
.getThreadsPattern());
632 * Only query startStates if necessary (threadEntry == null)
634 List
<ITmfStateInterval
> startStates
= null;
635 for (int threadQuark
: threadQuarks
) {
636 if (monitor
.isCanceled()) {
640 String
[] callStackPath
= module
.getCallStackPath();
641 int callStackQuark
= ss
.getQuarkRelative(threadQuark
, callStackPath
);
642 String threadName
= ss
.getAttributeName(threadQuark
);
643 long threadEnd
= end
+ 1;
644 if (callStackQuark
>= endStates
.size()) {
645 /* attribute created after previous full query */
646 endStates
= ss
.queryFullState(end
);
648 ITmfStateInterval endInterval
= endStates
.get(callStackQuark
);
649 if (endInterval
.getStateValue().isNull() && endInterval
.getStartTime() != ss
.getStartTime()) {
650 threadEnd
= endInterval
.getStartTime();
653 * Default to process/trace entry, overwrite if a thread
656 TimeGraphEntry callStackParent
= threadParent
;
657 if (threadQuark
!= processQuark
) {
658 ThreadEntry threadEntry
= threadEntryMap
.get(threadQuark
);
659 if (threadEntry
== null) {
660 if (startStates
== null || callStackQuark
>= startStates
.size()) {
662 * attribute created after previous full
665 startStates
= ss
.queryFullState(ss
.getStartTime());
668 if (threadQuark
>= endStates
.size()) {
670 * attribute created after previous full
673 endStates
= ss
.queryFullState(end
);
675 ITmfStateValue threadStateValue
= endStates
.get(threadQuark
).getStateValue();
676 if (threadStateValue
.getType() == Type
.LONG
|| threadStateValue
.getType() == Type
.INTEGER
) {
677 threadId
= threadStateValue
.unboxLong();
680 threadId
= Long
.parseLong(threadName
);
681 } catch (NumberFormatException e
) {
682 /* use default threadId */
685 long threadStart
= start
;
686 ITmfStateInterval startInterval
= startStates
.get(callStackQuark
);
687 if (startInterval
.getStateValue().isNull()) {
688 threadStart
= Math
.min(startInterval
.getEndTime() + 1, end
+ 1);
690 threadEntry
= new ThreadEntry(threadName
, threadId
, threadStart
, threadEnd
);
691 threadEntryMap
.put(threadQuark
, threadEntry
);
692 threadParent
.addChild(threadEntry
);
694 threadEntry
.updateEndTime(threadEnd
);
697 * The parent of the call stack entries will be a
700 callStackParent
= threadEntry
;
703 for (int stackLevelQuark
: ss
.getSubAttributes(callStackQuark
, false)) {
704 if (level
> callStackParent
.getChildren().size()) {
705 CallStackEntry callStackEntry
= new CallStackEntry(threadName
, stackLevelQuark
, level
, processId
, trace
, ss
);
706 callStackParent
.addChild(callStackEntry
);
712 } catch (AttributeNotFoundException e
) {
713 Activator
.getDefault().logError("Error querying state system", e
); //$NON-NLS-1$
714 } catch (StateSystemDisposedException e
) {
718 if (parentTrace
== getTrace()) {
719 synchronized (this) {
720 setStartTime(getStartTime() == SWT
.DEFAULT ? start
: Math
.min(getStartTime(), start
));
721 setEndTime(getEndTime() == SWT
.DEFAULT ? end
: Math
.max(getEndTime(), end
));
723 synchingToTime(getTimeGraphViewer().getSelectionBegin());
727 Consumer
<TimeGraphEntry
> consumer
= new Consumer
<TimeGraphEntry
>() {
729 public void accept(TimeGraphEntry entry
) {
730 if (monitor
.isCanceled()) {
733 if (entry
instanceof CallStackEntry
) {
734 buildStatusEvents(parentTrace
, (CallStackEntry
) entry
, monitor
, ss
.getStartTime(), end
);
737 entry
.getChildren().forEach(this);
740 traceEntry
.getChildren().forEach(consumer
);
746 private void addUnavailableEntry(ITmfTrace trace
, ITmfTrace parentTrace
) {
747 String name
= Messages
.CallStackView_StackInfoNotAvailable
+ ' ' + '(' + trace
.getName() + ')';
748 TraceEntry unavailableEntry
= new TraceEntry(name
, 0, 0);
749 addToEntryList(parentTrace
, Collections
.singletonList(unavailableEntry
));
750 if (parentTrace
== getTrace()) {
755 private void buildStatusEvents(ITmfTrace trace
, CallStackEntry entry
, @NonNull IProgressMonitor monitor
, long start
, long end
) {
756 ITmfStateSystem ss
= entry
.getStateSystem();
757 long resolution
= Math
.max(1, (end
- ss
.getStartTime()) / getDisplayWidth());
758 List
<ITimeEvent
> eventList
= getEventList(entry
, start
, end
+ 1, resolution
, monitor
);
759 if (eventList
!= null) {
760 entry
.setEventList(eventList
);
762 if (trace
== getTrace()) {
771 protected final List
<ITimeEvent
> getEventList(TimeGraphEntry tgentry
, long startTime
, long endTime
, long resolution
, IProgressMonitor monitor
) {
772 if (!(tgentry
instanceof CallStackEntry
)) {
775 CallStackEntry entry
= (CallStackEntry
) tgentry
;
776 ITmfStateSystem ss
= entry
.getStateSystem();
777 long start
= Math
.max(startTime
, ss
.getStartTime());
778 long end
= Math
.min(endTime
, ss
.getCurrentEndTime() + 1);
782 boolean isZoomThread
= Thread
.currentThread() instanceof ZoomThread
;
783 List
<ITimeEvent
> eventList
= null;
785 List
<ITmfStateInterval
> stackIntervals
= StateSystemUtils
.queryHistoryRange(ss
, entry
.getQuark(), start
, end
- 1, resolution
, monitor
);
786 eventList
= new ArrayList
<>(stackIntervals
.size());
787 long lastEndTime
= -1;
788 boolean lastIsNull
= false;
789 for (ITmfStateInterval statusInterval
: stackIntervals
) {
790 if (monitor
.isCanceled()) {
793 long time
= statusInterval
.getStartTime();
794 long duration
= statusInterval
.getEndTime() - time
+ 1;
795 if (!statusInterval
.getStateValue().isNull()) {
796 final int modulo
= CallStackPresentationProvider
.NUM_COLORS
/ 2;
797 int value
= statusInterval
.getStateValue().toString().hashCode() % modulo
+ modulo
;
798 eventList
.add(new CallStackEvent(entry
, time
, duration
, value
));
801 if (lastEndTime
== -1 && isZoomThread
) {
802 // add null event if it intersects the start time
803 eventList
.add(new NullTimeEvent(entry
, time
, duration
));
805 if (lastEndTime
!= time
&& lastIsNull
) {
806 // add unknown event if between two null states
807 eventList
.add(new TimeEvent(entry
, lastEndTime
, time
- lastEndTime
));
809 if (time
+ duration
>= endTime
&& isZoomThread
) {
810 // add null event if it intersects the end time
811 eventList
.add(new NullTimeEvent(entry
, time
, duration
));
816 lastEndTime
= time
+ duration
;
818 } catch (AttributeNotFoundException e
) {
819 Activator
.getDefault().logError("Error querying state system", e
); //$NON-NLS-1$
820 } catch (TimeRangeException e
) {
821 Activator
.getDefault().logError("Error querying state system", e
); //$NON-NLS-1$
822 } catch (StateSystemDisposedException e
) {
832 protected void synchingToTime(final long time
) {
833 List
<TimeGraphEntry
> traceEntries
= getEntryList(getTrace());
834 Map
<ITmfStateSystem
, List
<ITmfStateInterval
>> fullStateMap
= new HashMap
<>();
835 if (traceEntries
== null) {
838 Consumer
<TimeGraphEntry
> consumer
= new Consumer
<TimeGraphEntry
>() {
840 public void accept(TimeGraphEntry entry
) {
841 if (entry
instanceof CallStackEntry
) {
842 CallStackEntry callStackEntry
= (CallStackEntry
) entry
;
843 ITmfStateSystem ss
= callStackEntry
.getStateSystem();
844 if (time
< ss
.getStartTime() || time
> ss
.getCurrentEndTime()) {
847 ITmfTrace trace
= callStackEntry
.getTrace();
849 List
<ITmfStateInterval
> fullState
= getFullState(ss
);
850 ITmfStateInterval stackLevelInterval
= fullState
.get(callStackEntry
.getQuark());
851 ITmfStateValue nameValue
= stackLevelInterval
.getStateValue();
853 String name
= getFunctionName(trace
, callStackEntry
.getProcessId(), time
, nameValue
);
854 callStackEntry
.setFunctionName(name
);
855 if (!name
.isEmpty()) {
856 callStackEntry
.setFunctionEntryTime(stackLevelInterval
.getStartTime());
857 callStackEntry
.setFunctionExitTime(stackLevelInterval
.getEndTime() + 1);
859 if (fSyncSelection
) {
860 int callStackQuark
= ss
.getParentAttributeQuark(callStackEntry
.getQuark());
861 ITmfStateInterval stackInterval
= fullState
.get(callStackQuark
);
862 if (time
== stackInterval
.getStartTime()) {
863 ITmfStateValue stackLevelState
= stackInterval
.getStateValue();
864 if (stackLevelState
.unboxInt() == callStackEntry
.getStackLevel() || stackLevelState
.isNull()) {
865 fSyncSelection
= false;
866 Display
.getDefault().asyncExec(() -> {
867 getTimeGraphViewer().setSelection(callStackEntry
, true);
868 getTimeGraphViewer().getTimeGraphControl().fireSelectionChanged();
873 } catch (StateSystemDisposedException e
) {
878 entry
.getChildren().forEach(this);
881 private List
<ITmfStateInterval
> getFullState(ITmfStateSystem ss
) throws StateSystemDisposedException
{
882 List
<ITmfStateInterval
> fullState
= fullStateMap
.get(ss
);
883 if (fullState
== null) {
884 fullState
= ss
.queryFullState(time
);
885 fullStateMap
.put(ss
, fullState
);
890 traceEntries
.forEach(consumer
);
891 if (Display
.getCurrent() != null) {
892 getTimeGraphViewer().refresh();
896 String
getFunctionName(ITmfTrace trace
, int processId
, long timestamp
, ITmfStateValue nameValue
) {
897 long address
= Long
.MAX_VALUE
;
898 String name
= ""; //$NON-NLS-1$
900 if (nameValue
.getType() == Type
.STRING
) {
901 name
= nameValue
.unboxStr();
903 address
= Long
.parseLong(name
, 16);
904 } catch (NumberFormatException e
) {
907 } else if (nameValue
.getType() == Type
.INTEGER
) {
908 name
= "0x" + Integer
.toUnsignedString(nameValue
.unboxInt(), 16); //$NON-NLS-1$
909 address
= nameValue
.unboxInt();
910 } else if (nameValue
.getType() == Type
.LONG
) {
911 name
= "0x" + Long
.toUnsignedString(nameValue
.unboxLong(), 16); //$NON-NLS-1$
912 address
= nameValue
.unboxLong();
914 } catch (StateValueTypeException e
) {
916 if (address
!= Long
.MAX_VALUE
) {
917 for (ISymbolProvider provider
: fSymbolProviders
.get(trace
)) {
918 String symbol
= provider
.getSymbolText(processId
, timestamp
, address
);
919 if (symbol
!= null) {
927 private void makeActions() {
928 fPreviousItemAction
= getTimeGraphViewer().getPreviousItemAction();
929 fPreviousItemAction
.setText(Messages
.TmfTimeGraphViewer_PreviousItemActionNameText
);
930 fPreviousItemAction
.setToolTipText(Messages
.TmfTimeGraphViewer_PreviousItemActionToolTipText
);
931 fNextItemAction
= getTimeGraphViewer().getNextItemAction();
932 fNextItemAction
.setText(Messages
.TmfTimeGraphViewer_NextItemActionNameText
);
933 fNextItemAction
.setToolTipText(Messages
.TmfTimeGraphViewer_NextItemActionToolTipText
);
936 private void contributeToActionBars() {
938 contributePinActionToToolBar();
939 fPinAction
.addPropertyChangeListener(new IPropertyChangeListener() {
941 public void propertyChange(PropertyChangeEvent event
) {
942 if (IAction
.CHECKED
.equals(event
.getProperty()) && !isPinned()) {
943 if (fSavedRangeSyncSignal
!= null) {
944 windowRangeUpdated(fSavedRangeSyncSignal
);
945 fSavedRangeSyncSignal
= null;
948 if (fSavedTimeSyncSignal
!= null) {
949 selectionRangeUpdated(fSavedTimeSyncSignal
);
950 fSavedTimeSyncSignal
= null;
961 protected void fillLocalToolBar(IToolBarManager manager
) {
963 manager
.add(getConfigureSymbolsAction());
964 manager
.add(new Separator());
965 manager
.add(getSortByNameAction());
966 manager
.add(getSortByIdAction());
967 manager
.add(getSortByTimeAction());
968 manager
.add(new Separator());
969 manager
.add(getTimeGraphViewer().getShowFilterDialogAction());
970 manager
.add(new Separator());
971 manager
.add(getTimeGraphViewer().getResetScaleAction());
972 manager
.add(getPreviousEventAction());
973 manager
.add(getNextEventAction());
974 manager
.add(new Separator());
975 manager
.add(getTimeGraphViewer().getToggleBookmarkAction());
976 manager
.add(getTimeGraphViewer().getPreviousMarkerAction());
977 manager
.add(getTimeGraphViewer().getNextMarkerAction());
978 manager
.add(new Separator());
979 manager
.add(fPreviousItemAction
);
980 manager
.add(fNextItemAction
);
981 manager
.add(getTimeGraphViewer().getZoomInAction());
982 manager
.add(getTimeGraphViewer().getZoomOutAction());
989 protected void fillTimeGraphEntryContextMenu(IMenuManager contextMenu
) {
990 contextMenu
.add(new GroupMarker(IWorkbenchActionConstants
.GROUP_REORGANIZE
));
991 contextMenu
.add(getSortByNameAction());
992 contextMenu
.add(getSortByIdAction());
993 contextMenu
.add(getSortByTimeAction());
997 * Get the the next event action.
999 * @return The action object
1001 private Action
getNextEventAction() {
1002 if (fNextEventAction
== null) {
1003 fNextEventAction
= new Action() {
1006 TimeGraphViewer viewer
= getTimeGraphViewer();
1007 ITimeGraphEntry entry
= viewer
.getSelection();
1008 if (entry
instanceof CallStackEntry
) {
1010 CallStackEntry callStackEntry
= (CallStackEntry
) entry
;
1011 ITmfStateSystem ss
= callStackEntry
.getStateSystem();
1012 long time
= Math
.max(ss
.getStartTime(), Math
.min(ss
.getCurrentEndTime(), viewer
.getSelectionBegin()));
1013 TimeGraphEntry parentEntry
= callStackEntry
.getParent();
1014 int quark
= ss
.getParentAttributeQuark(callStackEntry
.getQuark());
1015 ITmfStateInterval stackInterval
= ss
.querySingleState(time
, quark
);
1016 long newTime
= stackInterval
.getEndTime() + 1;
1017 viewer
.setSelectedTimeNotify(newTime
, true);
1018 stackInterval
= ss
.querySingleState(Math
.min(ss
.getCurrentEndTime(), newTime
), quark
);
1019 int stackLevel
= stackInterval
.getStateValue().unboxInt();
1020 ITimeGraphEntry selectedEntry
= parentEntry
.getChildren().get(Math
.max(0, stackLevel
- 1));
1021 viewer
.setSelection(selectedEntry
, true);
1022 viewer
.getTimeGraphControl().fireSelectionChanged();
1023 startZoomThread(viewer
.getTime0(), viewer
.getTime1());
1025 } catch (TimeRangeException
| StateSystemDisposedException
| StateValueTypeException e
) {
1026 Activator
.getDefault().logError("Error querying state system", e
); //$NON-NLS-1$
1032 fNextEventAction
.setText(Messages
.TmfTimeGraphViewer_NextStateChangeActionNameText
);
1033 fNextEventAction
.setToolTipText(Messages
.TmfTimeGraphViewer_NextStateChangeActionToolTipText
);
1034 fNextEventAction
.setImageDescriptor(Activator
.getDefault().getImageDescripterFromPath(ITmfImageConstants
.IMG_UI_NEXT_STATE_CHANGE
));
1037 return fNextEventAction
;
1041 * Get the previous event action.
1043 * @return The Action object
1045 private Action
getPreviousEventAction() {
1046 if (fPrevEventAction
== null) {
1047 fPrevEventAction
= new Action() {
1050 TimeGraphViewer viewer
= getTimeGraphViewer();
1051 ITimeGraphEntry entry
= viewer
.getSelection();
1052 if (entry
instanceof CallStackEntry
) {
1054 CallStackEntry callStackEntry
= (CallStackEntry
) entry
;
1055 ITmfStateSystem ss
= callStackEntry
.getStateSystem();
1056 long time
= Math
.max(ss
.getStartTime(), Math
.min(ss
.getCurrentEndTime(), viewer
.getSelectionBegin()));
1057 TimeGraphEntry parentEntry
= callStackEntry
.getParent();
1058 int quark
= ss
.getParentAttributeQuark(callStackEntry
.getQuark());
1059 ITmfStateInterval stackInterval
= ss
.querySingleState(time
, quark
);
1060 if (stackInterval
.getStartTime() == time
&& time
> ss
.getStartTime()) {
1061 stackInterval
= ss
.querySingleState(time
- 1, quark
);
1063 viewer
.setSelectedTimeNotify(stackInterval
.getStartTime(), true);
1064 int stackLevel
= stackInterval
.getStateValue().unboxInt();
1065 ITimeGraphEntry selectedEntry
= parentEntry
.getChildren().get(Math
.max(0, stackLevel
- 1));
1066 viewer
.setSelection(selectedEntry
, true);
1067 viewer
.getTimeGraphControl().fireSelectionChanged();
1068 startZoomThread(viewer
.getTime0(), viewer
.getTime1());
1070 } catch (TimeRangeException
| StateSystemDisposedException
| StateValueTypeException e
) {
1071 Activator
.getDefault().logError("Error querying state system", e
); //$NON-NLS-1$
1077 fPrevEventAction
.setText(Messages
.TmfTimeGraphViewer_PreviousStateChangeActionNameText
);
1078 fPrevEventAction
.setToolTipText(Messages
.TmfTimeGraphViewer_PreviousStateChangeActionToolTipText
);
1079 fPrevEventAction
.setImageDescriptor(Activator
.getDefault().getImageDescripterFromPath(ITmfImageConstants
.IMG_UI_PREV_STATE_CHANGE
));
1082 return fPrevEventAction
;
1085 private static @Nullable CallStackAnalysis
getCallStackModule(@NonNull ITmfTrace trace
) {
1087 * Since we cannot know the exact analysis ID (in separate plugins), we
1088 * will search using the analysis type.
1090 Iterable
<CallStackAnalysis
> modules
= TmfTraceUtils
.getAnalysisModulesOfClass(trace
, CallStackAnalysis
.class);
1091 Iterator
<CallStackAnalysis
> it
= modules
.iterator();
1092 if (!it
.hasNext()) {
1093 /* This trace does not provide a call-stack analysis */
1098 * We only look at the first module we find.
1100 * TODO Handle the advanced case where one trace provides more than one
1101 * call-stack analysis.
1103 CallStackAnalysis module
= it
.next();
1104 /* This analysis is not automatic, we need to schedule it on-demand */
1106 if (!module
.waitForInitialization()) {
1107 /* The initialization did not succeed */
1113 // ------------------------------------------------------------------------
1114 // Methods related to function name mapping
1115 // ------------------------------------------------------------------------
1117 private Action
getSortByNameAction() {
1118 if (fSortByNameAction
== null) {
1119 fSortByNameAction
= new Action(Messages
.CallStackView_SortByThreadName
, IAction
.AS_CHECK_BOX
) {
1122 if (fSortOption
== SortOption
.BY_NAME
) {
1123 saveSortOption(SortOption
.BY_NAME_REV
);
1125 saveSortOption(SortOption
.BY_NAME
);
1129 fSortByNameAction
.setToolTipText(Messages
.CallStackView_SortByThreadName
);
1130 fSortByNameAction
.setImageDescriptor(SORT_BY_NAME_ICON
);
1132 return fSortByNameAction
;
1135 private Action
getSortByIdAction() {
1136 if (fSortByIdAction
== null) {
1137 fSortByIdAction
= new Action(Messages
.CallStackView_SortByThreadId
, IAction
.AS_CHECK_BOX
) {
1140 if (fSortOption
== SortOption
.BY_ID
) {
1141 saveSortOption(SortOption
.BY_ID_REV
);
1143 saveSortOption(SortOption
.BY_ID
);
1147 fSortByIdAction
.setToolTipText(Messages
.CallStackView_SortByThreadId
);
1148 fSortByIdAction
.setImageDescriptor(SORT_BY_ID_ICON
);
1150 return fSortByIdAction
;
1153 private Action
getSortByTimeAction() {
1154 if (fSortByTimeAction
== null) {
1155 fSortByTimeAction
= new Action(Messages
.CallStackView_SortByThreadTime
, IAction
.AS_CHECK_BOX
) {
1158 if (fSortOption
== SortOption
.BY_TIME
) {
1159 saveSortOption(SortOption
.BY_TIME_REV
);
1161 saveSortOption(SortOption
.BY_TIME
);
1165 fSortByTimeAction
.setToolTipText(Messages
.CallStackView_SortByThreadTime
);
1166 fSortByTimeAction
.setImageDescriptor(SORT_BY_TIME_ICON
);
1168 return fSortByTimeAction
;
1171 private void loadSortOption() {
1172 IDialogSettings settings
= Activator
.getDefault().getDialogSettings();
1173 IDialogSettings section
= settings
.getSection(getClass().getName());
1174 if (section
== null) {
1177 String sortOption
= section
.get(SORT_OPTION_KEY
);
1178 if (sortOption
== null) {
1183 getSortByNameAction().setChecked(false);
1184 getSortByNameAction().setImageDescriptor(SORT_BY_NAME_ICON
);
1185 getSortByIdAction().setChecked(false);
1186 getSortByIdAction().setImageDescriptor(SORT_BY_ID_ICON
);
1187 getSortByTimeAction().setChecked(false);
1188 getSortByTimeAction().setImageDescriptor(SORT_BY_TIME_ICON
);
1190 if (sortOption
.equals(SortOption
.BY_NAME
.name())) {
1191 fSortOption
= SortOption
.BY_NAME
;
1192 fThreadComparator
= new ThreadNameComparator(false);
1193 getSortByNameAction().setChecked(true);
1194 } else if (sortOption
.equals(SortOption
.BY_NAME_REV
.name())) {
1195 fSortOption
= SortOption
.BY_NAME_REV
;
1196 fThreadComparator
= new ThreadNameComparator(true);
1197 getSortByNameAction().setChecked(true);
1198 getSortByNameAction().setImageDescriptor(SORT_BY_NAME_REV_ICON
);
1199 } else if (sortOption
.equals(SortOption
.BY_ID
.name())) {
1200 fSortOption
= SortOption
.BY_ID
;
1201 fThreadComparator
= new ThreadIdComparator(false);
1202 getSortByIdAction().setChecked(true);
1203 } else if (sortOption
.equals(SortOption
.BY_ID_REV
.name())) {
1204 fSortOption
= SortOption
.BY_ID_REV
;
1205 fThreadComparator
= new ThreadIdComparator(true);
1206 getSortByIdAction().setChecked(true);
1207 getSortByIdAction().setImageDescriptor(SORT_BY_ID_REV_ICON
);
1208 } else if (sortOption
.equals(SortOption
.BY_TIME
.name())) {
1209 fSortOption
= SortOption
.BY_TIME
;
1210 fThreadComparator
= new ThreadTimeComparator(false);
1211 getSortByTimeAction().setChecked(true);
1212 } else if (sortOption
.equals(SortOption
.BY_TIME_REV
.name())) {
1213 fSortOption
= SortOption
.BY_TIME_REV
;
1214 fThreadComparator
= new ThreadTimeComparator(true);
1215 getSortByTimeAction().setChecked(true);
1216 getSortByTimeAction().setImageDescriptor(SORT_BY_TIME_REV_ICON
);
1220 private void saveSortOption(SortOption sortOption
) {
1221 IDialogSettings settings
= Activator
.getDefault().getDialogSettings();
1222 IDialogSettings section
= settings
.getSection(getClass().getName());
1223 if (section
== null) {
1224 section
= settings
.addNewSection(getClass().getName());
1226 section
.put(SORT_OPTION_KEY
, sortOption
.name());
1228 List
<TimeGraphEntry
> entryList
= getEntryList(getTrace());
1229 if (entryList
== null) {
1232 for (TimeGraphEntry traceEntry
: entryList
) {
1233 traceEntry
.sortChildren(fThreadComparator
);
1238 private Action
getConfigureSymbolsAction() {
1239 if (fConfigureSymbolsAction
!= null) {
1240 return fConfigureSymbolsAction
;
1243 fConfigureSymbolsAction
= new Action(Messages
.CallStackView_ConfigureSymbolProvidersText
) {
1246 SymbolProviderConfigDialog dialog
= new SymbolProviderConfigDialog(getSite().getShell(), getProviderPages());
1247 if (dialog
.open() == IDialogConstants
.OK_ID
) {
1248 getPresentationProvider().resetFunctionNames();
1254 fConfigureSymbolsAction
.setToolTipText(Messages
.CallStackView_ConfigureSymbolProvidersTooltip
);
1255 fConfigureSymbolsAction
.setImageDescriptor(Activator
.getDefault().getImageDescripterFromPath(IMPORT_BINARY_ICON_PATH
));
1258 * The updateConfigureSymbolsAction() method (called by refresh()) will
1259 * set the action to true if applicable after the symbol provider has
1260 * been properly loaded.
1262 fConfigureSymbolsAction
.setEnabled(false);
1264 return fConfigureSymbolsAction
;
1268 * @return an array of {@link ISymbolProviderPreferencePage} that will
1269 * configure the current traces
1271 private ISymbolProviderPreferencePage
[] getProviderPages() {
1272 List
<ISymbolProviderPreferencePage
> pages
= new ArrayList
<>();
1273 ITmfTrace trace
= getTrace();
1274 if (trace
!= null) {
1275 for (ITmfTrace subTrace
: getTracesToBuild(trace
)) {
1276 for (ISymbolProvider provider
: fSymbolProviders
.get(subTrace
)) {
1277 if (provider
instanceof org
.eclipse
.tracecompass
.tmf
.ui
.symbols
.ISymbolProvider
) {
1278 org
.eclipse
.tracecompass
.tmf
.ui
.symbols
.ISymbolProvider provider2
= (org
.eclipse
.tracecompass
.tmf
.ui
.symbols
.ISymbolProvider
) provider
;
1279 ISymbolProviderPreferencePage page
= provider2
.createPreferencePage();
1287 return pages
.toArray(new ISymbolProviderPreferencePage
[pages
.size()]);
1291 * Update the enable status of the configure symbols action
1293 private void updateConfigureSymbolsAction() {
1294 ISymbolProviderPreferencePage
[] providerPages
= getProviderPages();
1295 getConfigureSymbolsAction().setEnabled(providerPages
.length
> 0);