1 /*******************************************************************************
2 * Copyright (c) 2012, 2016 Ericsson, École Polytechnique de Montréal 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 * Geneviève Bastien - Move code to provide base classes for time graph view
12 * Christian Mansky - Add check active / uncheck inactive buttons
13 * Mahdi Zolnouri & Samuel Gagnon - Add flat / hierarchical button
14 *******************************************************************************/
16 package org
.eclipse
.tracecompass
.internal
.analysis
.os
.linux
.ui
.views
.controlflow
;
18 import java
.util
.ArrayList
;
19 import java
.util
.Collections
;
20 import java
.util
.Comparator
;
21 import java
.util
.HashMap
;
22 import java
.util
.HashSet
;
23 import java
.util
.List
;
26 import java
.util
.stream
.Collectors
;
27 import java
.util
.stream
.Stream
;
29 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
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
.IAction
;
34 import org
.eclipse
.jface
.action
.IMenuManager
;
35 import org
.eclipse
.jface
.action
.IToolBarManager
;
36 import org
.eclipse
.jface
.action
.MenuManager
;
37 import org
.eclipse
.jface
.dialogs
.IDialogSettings
;
38 import org
.eclipse
.jface
.viewers
.ISelection
;
39 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
40 import org
.eclipse
.swt
.widgets
.Composite
;
41 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernel
.KernelAnalysisModule
;
42 import org
.eclipse
.tracecompass
.common
.core
.StreamUtils
.StreamFlattener
;
43 import org
.eclipse
.tracecompass
.internal
.analysis
.os
.linux
.core
.kernel
.Attributes
;
44 import org
.eclipse
.tracecompass
.internal
.analysis
.os
.linux
.ui
.Activator
;
45 import org
.eclipse
.tracecompass
.internal
.analysis
.os
.linux
.ui
.Messages
;
46 import org
.eclipse
.tracecompass
.internal
.analysis
.os
.linux
.ui
.actions
.FollowThreadAction
;
47 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
48 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
49 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
50 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
51 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
52 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
53 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
54 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
55 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceSelectedSignal
;
56 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.TmfStateSystemAnalysisModule
;
57 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
58 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
59 import org
.eclipse
.tracecompass
.tmf
.core
.util
.Pair
;
60 import org
.eclipse
.tracecompass
.tmf
.ui
.views
.timegraph
.AbstractStateSystemTimeGraphView
;
61 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ILinkEvent
;
62 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeEvent
;
63 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
64 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.NullTimeEvent
;
65 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeEvent
;
66 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeGraphEntry
;
67 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeLinkEvent
;
68 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.widgets
.Utils
;
69 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.widgets
.Utils
.Resolution
;
70 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.widgets
.Utils
.TimeFormat
;
72 import com
.google
.common
.collect
.ImmutableList
;
75 * The Control Flow view main object
78 public class ControlFlowView
extends AbstractStateSystemTimeGraphView
{
80 // ------------------------------------------------------------------------
82 // ------------------------------------------------------------------------
86 public static final String ID
= "org.eclipse.tracecompass.analysis.os.linux.views.controlflow"; //$NON-NLS-1$
88 private static final String PROCESS_COLUMN
= Messages
.ControlFlowView_processColumn
;
89 private static final String TID_COLUMN
= Messages
.ControlFlowView_tidColumn
;
90 private static final String PTID_COLUMN
= Messages
.ControlFlowView_ptidColumn
;
91 private static final String BIRTH_TIME_COLUMN
= Messages
.ControlFlowView_birthTimeColumn
;
93 private static final String
[] COLUMN_NAMES
= new String
[] {
100 private static final String
[] FILTER_COLUMN_NAMES
= new String
[] {
105 // Timeout between updates in the build thread in ms
106 private static final long BUILD_UPDATE_TIMEOUT
= 500;
108 private static final Comparator
<ITimeGraphEntry
>[] COLUMN_COMPARATORS
;
110 private static final int INITIAL_SORT_COLUMN_INDEX
= 3;
113 ImmutableList
.Builder
<Comparator
<ITimeGraphEntry
>> builder
= ImmutableList
.builder();
114 builder
.add(ControlFlowColumnComparators
.PROCESS_NAME_COLUMN_COMPARATOR
)
115 .add(ControlFlowColumnComparators
.TID_COLUMN_COMPARATOR
)
116 .add(ControlFlowColumnComparators
.PTID_COLUMN_COMPARATOR
)
117 .add(ControlFlowColumnComparators
.BIRTH_TIME_COLUMN_COMPARATOR
);
118 List
<Comparator
<ITimeGraphEntry
>> l
= builder
.build();
119 COLUMN_COMPARATORS
= l
.toArray(new Comparator
[l
.size()]);
122 private final Set
<ITmfTrace
> fFlatTraces
= new HashSet
<>();
124 private IAction fFlatAction
;
126 private IAction fHierarchicalAction
;
128 // ------------------------------------------------------------------------
130 // ------------------------------------------------------------------------
135 public ControlFlowView() {
136 super(ID
, new ControlFlowPresentationProvider());
137 setTreeColumns(COLUMN_NAMES
, COLUMN_COMPARATORS
, INITIAL_SORT_COLUMN_INDEX
);
138 setTreeLabelProvider(new ControlFlowTreeLabelProvider());
139 setFilterColumns(FILTER_COLUMN_NAMES
);
140 setFilterLabelProvider(new ControlFlowFilterLabelProvider());
141 setEntryComparator(ControlFlowColumnComparators
.BIRTH_TIME_COLUMN_COMPARATOR
);
145 public void createPartControl(Composite parent
) {
146 super.createPartControl(parent
);
147 // add "Check active" Button to TimeGraphFilterDialog
148 super.getTimeGraphCombo().addTimeGraphFilterCheckActiveButton(
149 new ControlFlowCheckActiveProvider(Messages
.ControlFlowView_checkActiveLabel
, Messages
.ControlFlowView_checkActiveToolTip
));
150 // add "Uncheck inactive" Button to TimeGraphFilterDialog
151 super.getTimeGraphCombo().addTimeGraphFilterUncheckInactiveButton(
152 new ControlFlowCheckActiveProvider(Messages
.ControlFlowView_uncheckInactiveLabel
, Messages
.ControlFlowView_uncheckInactiveToolTip
));
159 protected void fillTimeGraphEntryContextMenu(@NonNull IMenuManager menuManager
) {
160 ISelection selection
= getSite().getSelectionProvider().getSelection();
161 if (selection
instanceof StructuredSelection
) {
162 StructuredSelection sSel
= (StructuredSelection
) selection
;
163 if (sSel
.getFirstElement() instanceof ControlFlowEntry
) {
164 ControlFlowEntry entry
= (ControlFlowEntry
) sSel
.getFirstElement();
165 menuManager
.add(new FollowThreadAction(ControlFlowView
.this, entry
.getName(), entry
.getThreadId(), entry
.getTrace()));
171 protected void fillLocalToolBar(IToolBarManager manager
) {
172 super.fillLocalToolBar(manager
);
173 IDialogSettings settings
= Activator
.getDefault().getDialogSettings();
174 IDialogSettings section
= settings
.getSection(getClass().getName());
175 if (section
== null) {
176 section
= settings
.addNewSection(getClass().getName());
179 IAction hideArrowsAction
= getTimeGraphCombo().getTimeGraphViewer().getHideArrowsAction(section
);
180 manager
.add(hideArrowsAction
);
182 IAction followArrowBwdAction
= getTimeGraphCombo().getTimeGraphViewer().getFollowArrowBwdAction();
183 followArrowBwdAction
.setText(Messages
.ControlFlowView_followCPUBwdText
);
184 followArrowBwdAction
.setToolTipText(Messages
.ControlFlowView_followCPUBwdText
);
185 manager
.add(followArrowBwdAction
);
187 IAction followArrowFwdAction
= getTimeGraphCombo().getTimeGraphViewer().getFollowArrowFwdAction();
188 followArrowFwdAction
.setText(Messages
.ControlFlowView_followCPUFwdText
);
189 followArrowFwdAction
.setToolTipText(Messages
.ControlFlowView_followCPUFwdText
);
190 manager
.add(followArrowFwdAction
);
194 protected void fillLocalMenu(IMenuManager manager
) {
195 super.fillLocalMenu(manager
);
196 final MenuManager item
= new MenuManager(Messages
.ControlFlowView_threadPresentation
);
197 fFlatAction
= createFlatAction();
198 item
.add(fFlatAction
);
200 fHierarchicalAction
= createHierarchicalAction();
201 item
.add(fHierarchicalAction
);
206 private IAction
createHierarchicalAction() {
207 IAction action
= new Action(Messages
.ControlFlowView_hierarchicalViewLabel
, IAction
.AS_RADIO_BUTTON
) {
210 ITmfTrace parentTrace
= getTrace();
211 synchronized (fFlatTraces
) {
212 fFlatTraces
.remove(parentTrace
);
213 for (ITmfTrace trace
: TmfTraceManager
.getTraceSet(parentTrace
)) {
214 final ITmfStateSystem ss
= TmfStateSystemAnalysisModule
.getStateSystem(trace
, KernelAnalysisModule
.ID
);
215 for (TimeGraphEntry traceEntry
: getEntryList(ss
)) {
216 List
<ControlFlowEntry
> currentRootList
= traceEntry
.getChildren().stream()
217 .filter(e
-> e
instanceof ControlFlowEntry
)
218 .map(e
-> (ControlFlowEntry
) e
).collect(Collectors
.toList());
219 addEntriesToHierarchicalTree(currentRootList
, traceEntry
);
226 action
.setChecked(true);
227 action
.setToolTipText(Messages
.ControlFlowView_hierarchicalViewToolTip
);
231 private IAction
createFlatAction() {
232 IAction action
= new Action(Messages
.ControlFlowView_flatViewLabel
, IAction
.AS_RADIO_BUTTON
) {
235 ITmfTrace parentTrace
= getTrace();
236 synchronized (fFlatTraces
) {
237 fFlatTraces
.add(parentTrace
);
238 for (ITmfTrace trace
: TmfTraceManager
.getTraceSet(parentTrace
)) {
239 final ITmfStateSystem ss
= TmfStateSystemAnalysisModule
.getStateSystem(trace
, KernelAnalysisModule
.ID
);
240 for (TimeGraphEntry traceEntry
: getEntryList(ss
)) {
241 hierarchicalToFlatTree(traceEntry
);
248 action
.setToolTipText(Messages
.ControlFlowView_flatViewToolTip
);
253 protected String
getNextText() {
254 return Messages
.ControlFlowView_nextProcessActionNameText
;
258 protected String
getNextTooltip() {
259 return Messages
.ControlFlowView_nextProcessActionToolTipText
;
263 protected String
getPrevText() {
264 return Messages
.ControlFlowView_previousProcessActionNameText
;
268 protected String
getPrevTooltip() {
269 return Messages
.ControlFlowView_previousProcessActionToolTipText
;
276 protected static class ControlFlowTreeLabelProvider
extends TreeLabelProvider
{
279 public String
getColumnText(Object element
, int columnIndex
) {
280 if (element
instanceof TraceEntry
) {
281 if (columnIndex
== 0) {
282 return ((TraceEntry
) element
).getName();
284 return ""; //$NON-NLS-1$
286 ControlFlowEntry entry
= (ControlFlowEntry
) element
;
288 if (COLUMN_NAMES
[columnIndex
].equals(Messages
.ControlFlowView_processColumn
)) {
289 return entry
.getName();
290 } else if (COLUMN_NAMES
[columnIndex
].equals(Messages
.ControlFlowView_tidColumn
)) {
291 return Integer
.toString(entry
.getThreadId());
292 } else if (COLUMN_NAMES
[columnIndex
].equals(Messages
.ControlFlowView_ptidColumn
)) {
293 if (entry
.getParentThreadId() > 0) {
294 return Integer
.toString(entry
.getParentThreadId());
296 } else if (COLUMN_NAMES
[columnIndex
].equals(Messages
.ControlFlowView_birthTimeColumn
)) {
297 return Utils
.formatTime(entry
.getStartTime(), TimeFormat
.CALENDAR
, Resolution
.NANOSEC
);
299 return ""; //$NON-NLS-1$
304 private static class ControlFlowFilterLabelProvider
extends TreeLabelProvider
{
307 public String
getColumnText(Object element
, int columnIndex
) {
308 if (element
instanceof TraceEntry
) {
309 if (columnIndex
== 0) {
310 return ((TraceEntry
) element
).getName();
312 return ""; //$NON-NLS-1$
314 ControlFlowEntry entry
= (ControlFlowEntry
) element
;
316 if (columnIndex
== 0) {
317 return entry
.getName();
318 } else if (columnIndex
== 1) {
319 return Integer
.toString(entry
.getThreadId());
321 return ""; //$NON-NLS-1$
326 private static class TraceEntry
extends TimeGraphEntry
{
328 public TraceEntry(String name
, long startTime
, long endTime
) {
329 super(name
, startTime
, endTime
);
333 public boolean hasTimeEvents() {
340 public void traceClosed(org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceClosedSignal signal
) {
341 super.traceClosed(signal
);
342 synchronized (fFlatTraces
) {
343 fFlatTraces
.remove(signal
.getTrace());
349 public void traceSelected(TmfTraceSelectedSignal signal
) {
350 super.traceSelected(signal
);
351 synchronized (fFlatTraces
) {
352 if (fFlatTraces
.contains(signal
.getTrace())) {
353 fHierarchicalAction
.setChecked(false);
354 fFlatAction
.setChecked(true);
356 fFlatAction
.setChecked(false);
357 fHierarchicalAction
.setChecked(true);
362 // ------------------------------------------------------------------------
364 // ------------------------------------------------------------------------
367 protected void buildEntryList(final ITmfTrace trace
, final ITmfTrace parentTrace
, final IProgressMonitor monitor
) {
368 final ITmfStateSystem ssq
= TmfStateSystemAnalysisModule
.getStateSystem(trace
, KernelAnalysisModule
.ID
);
373 final List
<ControlFlowEntry
> entryList
= new ArrayList
<>();
374 /** Map of trace entries */
375 Map
<ITmfTrace
, TraceEntry
> traceEntryMap
= new HashMap
<>();
376 /** Map of control flow entries, key is a pair [threadId, cpuId] */
377 final Map
<Pair
<Integer
, Integer
>, ControlFlowEntry
> entryMap
= new HashMap
<>();
379 long start
= ssq
.getStartTime();
380 setStartTime(Math
.min(getStartTime(), start
));
382 boolean complete
= false;
384 if (monitor
.isCanceled()) {
387 complete
= ssq
.waitUntilBuilt(BUILD_UPDATE_TIMEOUT
);
388 if (ssq
.isCancelled()) {
391 long end
= ssq
.getCurrentEndTime();
392 if (start
== end
&& !complete
) { // when complete execute one last time regardless of end time
396 TraceEntry aTraceEntry
= traceEntryMap
.get(trace
);
397 if (aTraceEntry
== null) {
398 aTraceEntry
= new TraceEntry(trace
.getName(), start
, end
+ 1);
399 traceEntryMap
.put(trace
, aTraceEntry
);
400 addToEntryList(parentTrace
, ssq
, Collections
.singletonList(aTraceEntry
));
402 aTraceEntry
.updateEndTime(end
+ 1);
404 final TraceEntry traceEntry
= aTraceEntry
;
406 final long resolution
= Math
.max(1, (end
- ssq
.getStartTime()) / getDisplayWidth());
407 setEndTime(Math
.max(getEndTime(), end
+ 1));
408 final List
<Integer
> threadQuarks
= ssq
.getQuarks(Attributes
.THREADS
, "*"); //$NON-NLS-1$
409 queryFullStates(ssq
, start
, end
, resolution
, monitor
, new IQueryHandler() {
411 public void handle(List
<List
<ITmfStateInterval
>> fullStates
, List
<ITmfStateInterval
> prevFullState
) {
412 for (int threadQuark
: threadQuarks
) {
413 String threadAttributeName
= ssq
.getAttributeName(threadQuark
);
415 Pair
<Integer
, Integer
> entryKey
= Attributes
.parseThreadAttributeName(threadAttributeName
);
416 int threadId
= entryKey
.getFirst();
418 if (threadId
< 0) { // ignore the 'unknown' (-1) thread
425 execNameQuark
= ssq
.getQuarkRelative(threadQuark
, Attributes
.EXEC_NAME
);
426 ppidQuark
= ssq
.getQuarkRelative(threadQuark
, Attributes
.PPID
);
427 } catch (AttributeNotFoundException e
) {
428 /* No information on this thread (yet?), skip it for now */
431 ITmfStateInterval lastExecNameInterval
= prevFullState
== null || execNameQuark
>= prevFullState
.size() ?
null : prevFullState
.get(execNameQuark
);
432 long lastExecNameStartTime
= lastExecNameInterval
== null ?
-1 : lastExecNameInterval
.getStartTime();
433 long lastExecNameEndTime
= lastExecNameInterval
== null ?
-1 : lastExecNameInterval
.getEndTime() + 1;
434 long lastPpidStartTime
= prevFullState
== null || ppidQuark
>= prevFullState
.size() ?
-1 : prevFullState
.get(ppidQuark
).getStartTime();
435 for (List
<ITmfStateInterval
> fullState
: fullStates
) {
436 if (monitor
.isCanceled()) {
439 if (execNameQuark
>= fullState
.size() || ppidQuark
>= fullState
.size()) {
440 /* No information on this thread (yet?), skip it for now */
443 ITmfStateInterval execNameInterval
= fullState
.get(execNameQuark
);
444 ITmfStateInterval ppidInterval
= fullState
.get(ppidQuark
);
445 long startTime
= execNameInterval
.getStartTime();
446 long endTime
= execNameInterval
.getEndTime() + 1;
447 if (startTime
== lastExecNameStartTime
&& ppidInterval
.getStartTime() == lastPpidStartTime
) {
450 boolean isNull
= execNameInterval
.getStateValue().isNull();
451 if (isNull
&& lastExecNameEndTime
< startTime
&& lastExecNameEndTime
!= -1) {
453 * There was a non-null interval in between the
454 * full states, try to use it.
457 execNameInterval
= ssq
.querySingleState(startTime
- 1, execNameQuark
);
458 ppidInterval
= ssq
.querySingleState(startTime
- 1, ppidQuark
);
459 startTime
= execNameInterval
.getStartTime();
460 endTime
= execNameInterval
.getEndTime() + 1;
461 } catch (StateSystemDisposedException e
) {
465 if (!execNameInterval
.getStateValue().isNull() &&
466 execNameInterval
.getStateValue().getType() == ITmfStateValue
.Type
.STRING
) {
467 String execName
= execNameInterval
.getStateValue().unboxStr();
468 int ppid
= ppidInterval
.getStateValue().unboxInt();
469 ControlFlowEntry entry
= entryMap
.get(entryKey
);
471 entry
= new ControlFlowEntry(threadQuark
, trace
, execName
, threadId
, ppid
, startTime
, endTime
);
472 entryList
.add(entry
);
473 entryMap
.put(entryKey
, entry
);
476 * Update the name of the entry to the
477 * latest execName and the parent thread id
478 * to the latest ppid.
480 entry
.setName(execName
);
481 entry
.setParentThreadId(ppid
);
482 entry
.updateEndTime(endTime
);
486 entryMap
.remove(entryKey
);
488 lastExecNameStartTime
= startTime
;
489 lastExecNameEndTime
= endTime
;
490 lastPpidStartTime
= ppidInterval
.getStartTime();
493 synchronized (fFlatTraces
) {
494 if (fFlatTraces
.contains(parentTrace
)) {
495 addEntriesToFlatTree(entryList
, traceEntry
);
497 addEntriesToHierarchicalTree(entryList
, traceEntry
);
503 queryFullStates(ssq
, ssq
.getStartTime(), end
, resolution
, monitor
, new IQueryHandler() {
505 public void handle(@NonNull List
<List
<ITmfStateInterval
>> fullStates
, @Nullable List
<ITmfStateInterval
> prevFullState
) {
506 for (final TimeGraphEntry entry
: traceEntry
.getChildren()) {
507 if (monitor
.isCanceled()) {
510 buildStatusEvents(trace
, parentTrace
, ssq
, fullStates
, prevFullState
, (ControlFlowEntry
) entry
, monitor
, ssq
.getStartTime(), end
);
515 if (parentTrace
.equals(getTrace())) {
524 * Add entries to the traces's child list in a flat fashion (no hierarchy).
525 * If one entry has children, we do a depth first search to add each child
526 * to the trace's child list and update the parent and child relations.
528 private static void hierarchicalToFlatTree(TimeGraphEntry traceEntry
) {
529 List
<@NonNull TimeGraphEntry
> rootList
= traceEntry
.getChildren();
530 // We visit the children of every entry to add
531 StreamFlattener
<TimeGraphEntry
> sf
= new StreamFlattener
<>(entry
-> entry
.getChildren().stream());
532 Stream
<TimeGraphEntry
> allEntries
= rootList
.stream().flatMap(entry
-> sf
.flatten(entry
));
534 // We add every entry that is missing from the trace's entry list
535 List
<@NonNull TimeGraphEntry
> rootListToAdd
= allEntries
536 .filter(entry
-> !rootList
.contains(entry
))
537 .collect(Collectors
.toList());
538 rootList
.forEach(entry
-> {
539 entry
.clearChildren();
541 rootListToAdd
.forEach(entry
-> {
542 traceEntry
.addChild(entry
);
543 entry
.clearChildren();
548 * Add entries to the traces's child list in a flat fashion (no hierarchy).
550 private static void addEntriesToFlatTree(List
<@NonNull ControlFlowEntry
> entryList
, TimeGraphEntry traceEntry
) {
551 List
<TimeGraphEntry
> rootList
= traceEntry
.getChildren();
552 for (ControlFlowEntry entry
: entryList
) {
553 if (!rootList
.contains(entry
)) {
554 traceEntry
.addChild(entry
);
560 * Add entries to the trace's child list in a hierarchical fashion.
562 private static void addEntriesToHierarchicalTree(List
<ControlFlowEntry
> entryList
, TimeGraphEntry traceEntry
) {
563 List
<TimeGraphEntry
> rootList
= traceEntry
.getChildren();
565 for (ControlFlowEntry entry
: entryList
) {
566 boolean root
= (entry
.getParent() == null || entry
.getParent() == traceEntry
);
567 if (root
&& entry
.getParentThreadId() > 0) {
568 for (ControlFlowEntry parent
: entryList
) {
570 * Associate the parent entry only if their time overlap. A
571 * child entry may start before its parent, for example at
572 * the beginning of the trace if a parent has not yet
573 * appeared in the state system. We just want to make sure
574 * that the entry didn't start after the parent ended or
575 * ended before the parent started.
577 if (parent
.getThreadId() == entry
.getParentThreadId() &&
578 !(entry
.getStartTime() > parent
.getEndTime() ||
579 entry
.getEndTime() < parent
.getStartTime())) {
580 parent
.addChild(entry
);
582 if (rootList
.contains(entry
)) {
583 traceEntry
.removeChild(entry
);
589 if (root
&& (!rootList
.contains(entry
))) {
590 traceEntry
.addChild(entry
);
595 private void buildStatusEvents(ITmfTrace trace
, ITmfTrace parentTrace
, ITmfStateSystem ss
, @NonNull List
<List
<ITmfStateInterval
>> fullStates
,
596 @Nullable List
<ITmfStateInterval
> prevFullState
, ControlFlowEntry entry
, @NonNull IProgressMonitor monitor
, long start
, long end
) {
597 if (start
< entry
.getEndTime() && end
> entry
.getStartTime()) {
598 List
<ITimeEvent
> eventList
= getEventList(entry
, ss
, fullStates
, prevFullState
, monitor
);
599 if (eventList
== null) {
602 /* Start a new event list on first iteration, then append to it */
603 if (prevFullState
== null) {
604 entry
.setEventList(eventList
);
606 for (ITimeEvent event
: eventList
) {
607 entry
.addEvent(event
);
610 if (parentTrace
.equals(getTrace())) {
614 for (ITimeGraphEntry child
: entry
.getChildren()) {
615 if (monitor
.isCanceled()) {
618 buildStatusEvents(trace
, parentTrace
, ss
, fullStates
, prevFullState
, (ControlFlowEntry
) child
, monitor
, start
, end
);
623 protected @Nullable List
<ITimeEvent
> getEventList(@NonNull TimeGraphEntry tgentry
, ITmfStateSystem ss
,
624 @NonNull List
<List
<ITmfStateInterval
>> fullStates
, @Nullable List
<ITmfStateInterval
> prevFullState
, @NonNull IProgressMonitor monitor
) {
625 List
<ITimeEvent
> eventList
= null;
626 if (!(tgentry
instanceof ControlFlowEntry
)) {
629 ControlFlowEntry entry
= (ControlFlowEntry
) tgentry
;
631 int threadQuark
= entry
.getThreadQuark();
632 int statusQuark
= ss
.getQuarkRelative(threadQuark
, Attributes
.STATUS
);
633 eventList
= new ArrayList
<>(fullStates
.size());
634 ITmfStateInterval lastInterval
= prevFullState
== null || statusQuark
>= prevFullState
.size() ?
null : prevFullState
.get(statusQuark
);
635 long lastStartTime
= lastInterval
== null ?
-1 : lastInterval
.getStartTime();
636 long lastEndTime
= lastInterval
== null ?
-1 : lastInterval
.getEndTime() + 1;
637 for (List
<ITmfStateInterval
> fullState
: fullStates
) {
638 if (monitor
.isCanceled()) {
641 if (statusQuark
>= fullState
.size()) {
642 /* No information on this thread (yet?), skip it for now */
645 ITmfStateInterval statusInterval
= fullState
.get(statusQuark
);
646 long time
= statusInterval
.getStartTime();
647 if (time
== lastStartTime
) {
650 long duration
= statusInterval
.getEndTime() - time
+ 1;
653 status
= statusInterval
.getStateValue().unboxInt();
654 } catch (StateValueTypeException e
) {
655 Activator
.getDefault().logError(e
.getMessage());
657 if (lastEndTime
!= time
&& lastEndTime
!= -1) {
658 eventList
.add(new TimeEvent(entry
, lastEndTime
, time
- lastEndTime
));
660 if (!statusInterval
.getStateValue().isNull()) {
661 eventList
.add(new TimeEvent(entry
, time
, duration
, status
));
663 eventList
.add(new NullTimeEvent(entry
, time
, duration
));
665 lastStartTime
= time
;
666 lastEndTime
= time
+ duration
;
668 } catch (AttributeNotFoundException
| TimeRangeException e
) {
669 Activator
.getDefault().logError(e
.getMessage());
675 * Returns a value corresponding to the selected entry.
677 * Used in conjunction with synchingToTime to change the selected entry. If
678 * one of these methods is overridden in child class, then both should be.
681 * The currently selected time
682 * @return a value identifying the entry
684 private int getSelectionValue(long time
) {
686 for (ITmfTrace trace
: TmfTraceManager
.getTraceSet(getTrace())) {
690 ITmfStateSystem ssq
= TmfStateSystemAnalysisModule
.getStateSystem(trace
, KernelAnalysisModule
.ID
);
694 if (time
>= ssq
.getStartTime() && time
<= ssq
.getCurrentEndTime()) {
695 List
<Integer
> currentThreadQuarks
= ssq
.getQuarks(Attributes
.CPUS
, "*", Attributes
.CURRENT_THREAD
); //$NON-NLS-1$
696 for (int currentThreadQuark
: currentThreadQuarks
) {
698 ITmfStateInterval currentThreadInterval
= ssq
.querySingleState(time
, currentThreadQuark
);
699 int currentThread
= currentThreadInterval
.getStateValue().unboxInt();
700 if (currentThread
> 0) {
701 int statusQuark
= ssq
.getQuarkAbsolute(Attributes
.THREADS
, Integer
.toString(currentThread
), Attributes
.STATUS
);
702 ITmfStateInterval statusInterval
= ssq
.querySingleState(time
, statusQuark
);
703 if (statusInterval
.getStartTime() == time
) {
704 thread
= currentThread
;
708 } catch (AttributeNotFoundException
| TimeRangeException
| StateValueTypeException e
) {
709 Activator
.getDefault().logError(e
.getMessage());
710 } catch (StateSystemDisposedException e
) {
720 protected void synchingToTime(long time
) {
721 int selected
= getSelectionValue(time
);
723 for (Object element
: getTimeGraphViewer().getExpandedElements()) {
724 if (element
instanceof ControlFlowEntry
) {
725 ControlFlowEntry entry
= (ControlFlowEntry
) element
;
726 if (entry
.getThreadId() == selected
) {
727 getTimeGraphCombo().setSelection(entry
);
736 protected @NonNull List
<ILinkEvent
> getLinkList(ITmfStateSystem ss
,
737 @NonNull List
<List
<ITmfStateInterval
>> fullStates
, @Nullable List
<ITmfStateInterval
> prevFullState
, @NonNull IProgressMonitor monitor
) {
738 List
<ILinkEvent
> list
= new ArrayList
<>();
739 List
<TimeGraphEntry
> entryList
= getEntryList(ss
);
740 if (entryList
== null) {
743 for (ITmfTrace trace
: TmfTraceManager
.getTraceSet(getTrace())) {
744 List
<Integer
> currentThreadQuarks
= ss
.getQuarks(Attributes
.CPUS
, "*", Attributes
.CURRENT_THREAD
); //$NON-NLS-1$
745 for (int currentThreadQuark
: currentThreadQuarks
) {
746 if (currentThreadQuark
>= fullStates
.get(0).size()) {
747 /* No information on this cpu (yet?), skip it for now */
750 List
<ITmfStateInterval
> currentThreadIntervals
= new ArrayList
<>(fullStates
.size() + 2);
753 * Add the previous interval if it is the first query
754 * iteration and the first interval has currentThread=0. Add
755 * the following interval if the last interval has
756 * currentThread=0. These are diagonal arrows crossing the
757 * query iteration range.
759 if (prevFullState
== null) {
760 ITmfStateInterval currentThreadInterval
= fullStates
.get(0).get(currentThreadQuark
);
761 if (currentThreadInterval
.getStateValue().unboxInt() == 0) {
762 long start
= Math
.max(currentThreadInterval
.getStartTime() - 1, ss
.getStartTime());
763 currentThreadIntervals
.add(ss
.querySingleState(start
, currentThreadQuark
));
766 for (List
<ITmfStateInterval
> fullState
: fullStates
) {
767 currentThreadIntervals
.add(fullState
.get(currentThreadQuark
));
769 ITmfStateInterval currentThreadInterval
= fullStates
.get(fullStates
.size() - 1).get(currentThreadQuark
);
770 if (currentThreadInterval
.getStateValue().unboxInt() == 0) {
771 long end
= Math
.min(currentThreadInterval
.getEndTime() + 1, ss
.getCurrentEndTime());
772 currentThreadIntervals
.add(ss
.querySingleState(end
, currentThreadQuark
));
774 } catch (StateSystemDisposedException e
) {
781 for (ITmfStateInterval currentThreadInterval
: currentThreadIntervals
) {
782 if (monitor
.isCanceled()) {
785 if (currentThreadInterval
.getEndTime() + 1 == lastEnd
) {
788 long time
= currentThreadInterval
.getStartTime();
789 if (time
!= lastEnd
) {
790 // don't create links where there are gaps in intervals due to the resolution
794 int thread
= currentThreadInterval
.getStateValue().unboxInt();
795 if (thread
> 0 && prevThread
> 0) {
796 ITimeGraphEntry prevEntry
= findEntry(entryList
, trace
, prevThread
);
797 ITimeGraphEntry nextEntry
= findEntry(entryList
, trace
, thread
);
798 list
.add(new TimeLinkEvent(prevEntry
, nextEntry
, prevEnd
, time
- prevEnd
, 0));
800 lastEnd
= currentThreadInterval
.getEndTime() + 1;
811 private ControlFlowEntry
findEntry(List
<TimeGraphEntry
> entryList
, ITmfTrace trace
, int threadId
) {
812 for (TimeGraphEntry entry
: entryList
) {
813 if (entry
instanceof ControlFlowEntry
) {
814 ControlFlowEntry controlFlowEntry
= (ControlFlowEntry
) entry
;
815 if (controlFlowEntry
.getThreadId() == threadId
&& controlFlowEntry
.getTrace() == trace
) {
816 return controlFlowEntry
;
819 if (entry
.hasChildren()) {
820 ControlFlowEntry controlFlowEntry
= findEntry(entry
.getChildren(), trace
, threadId
);
821 if (controlFlowEntry
!= null) {
822 return controlFlowEntry
;