1 /*******************************************************************************
2 * Copyright (c) 2016 Ericsson
4 * All rights reserved. This program and the accompanying materials are made
5 * available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.internal
.analysis
.timing
.ui
.flamegraph
;
12 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
14 import java
.util
.ArrayDeque
;
15 import java
.util
.ArrayList
;
16 import java
.util
.Comparator
;
17 import java
.util
.Deque
;
18 import java
.util
.List
;
20 import org
.eclipse
.jdt
.annotation
.NonNull
;
21 import org
.eclipse
.jface
.viewers
.Viewer
;
23 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.core
.callgraph
.AggregatedCalledFunction
;
24 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.core
.callgraph
.ThreadNode
;
25 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
26 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
27 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.ITimeGraphContentProvider
;
28 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
29 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeGraphEntry
;
32 * Content provider for the flame graph view
34 * @author Sonia Farrah
37 public class FlameGraphContentProvider
implements ITimeGraphContentProvider
{
39 private final List
<FlamegraphDepthEntry
> fFlameGraphEntries
= new ArrayList
<>();
40 private ITmfTrace fActiveTrace
;
41 private SortOption fSortOption
= SortOption
.BY_NAME
;
42 private @NonNull Comparator
<FlamegraphDepthEntry
> fThreadComparator
= new ThreadNameComparator();
46 * Parse the aggregated tree created by the callGraphAnalysis and creates
47 * the event list (functions) for each entry (depth)
50 * The first node of the aggregation tree
51 * @param childrenEntries
52 * The list of entries for one thread
53 * @param timestampStack
54 * A stack used to save the functions timeStamps
56 private void setData(AggregatedCalledFunction firstNode
, List
<FlamegraphDepthEntry
> childrenEntries
, Deque
<Long
> timestampStack
) {
57 long lastEnd
= timestampStack
.peek();
58 for (int i
= 0; i
< firstNode
.getMaxDepth(); i
++) {
59 if (i
>= childrenEntries
.size()) {
60 FlamegraphDepthEntry entry
= new FlamegraphDepthEntry(String
.valueOf(i
), 0, firstNode
.getDuration(), i
, i
);
61 childrenEntries
.add(entry
);
63 childrenEntries
.get(i
).updateEndTime(lastEnd
+ firstNode
.getDuration());
65 FlamegraphDepthEntry firstEntry
= checkNotNull(childrenEntries
.get(0));
66 firstEntry
.addEvent(new FlamegraphEvent(firstEntry
, lastEnd
, firstNode
));
67 // Build the event list for next entries (next depth)
68 addEvent(firstNode
, childrenEntries
, timestampStack
);
73 * Build the events list for an entry (depth), then creates recursively the
74 * events for the next entries. This parses the aggregation tree starting
75 * from the bottom. This uses a stack to save the timestamp for each
76 * function. Once we save a function's timestamp we'll use it to create the
80 * The node of the aggregation tree
81 * @param childrenEntries
82 * The list of entries for one thread
83 * @param timestampStack
84 * A stack used to save the functions timeStamps
86 private void addEvent(AggregatedCalledFunction node
, List
<FlamegraphDepthEntry
> childrenEntries
, Deque
<Long
> timestampStack
) {
87 if (node
.hasChildren()) {
88 node
.getChildren().stream()
89 .sorted(Comparator
.comparingLong(AggregatedCalledFunction
::getDuration
))
91 addEvent(child
, childrenEntries
, timestampStack
);
93 node
.getChildren().stream().forEach(child
-> {
97 FlamegraphDepthEntry entry
= checkNotNull(childrenEntries
.get(node
.getDepth()));
98 // Create the event corresponding to the function using the caller's
100 entry
.addEvent(new FlamegraphEvent(entry
, timestampStack
.peek(), node
));
101 timestampStack
.push(timestampStack
.peek() + node
.getDuration());
105 public boolean hasChildren(Object element
) {
106 return !fFlameGraphEntries
.isEmpty();
110 public ITimeGraphEntry
[] getElements(Object inputElement
) {
111 fFlameGraphEntries
.clear();
112 // Get the root of each thread
113 fActiveTrace
= TmfTraceManager
.getInstance().getActiveTrace();
114 if (fActiveTrace
== null) {
115 return new ITimeGraphEntry
[0];
118 if (inputElement
instanceof List
<?
>) {
119 List
<?
> threadNodes
= (List
<?
>) inputElement
;
120 for (Object object
: threadNodes
) {
121 if (object
instanceof ThreadNode
) {
122 buildChildrenEntries((ThreadNode
) object
);
127 fFlameGraphEntries
.sort(fThreadComparator
);
128 return fFlameGraphEntries
.toArray(new ITimeGraphEntry
[fFlameGraphEntries
.size()]);
132 * Build the entry list for one thread
135 * The node of the aggregation tree
137 private void buildChildrenEntries(ThreadNode threadNode
) {
138 FlamegraphDepthEntry threadEntry
= new FlamegraphDepthEntry("", 0, 0, fFlameGraphEntries
.size(), threadNode
.getId()); //$NON-NLS-1$
139 List
<FlamegraphDepthEntry
> childrenEntries
= new ArrayList
<>();
140 Deque
<Long
> timestampStack
= new ArrayDeque
<>();
141 timestampStack
.push(0L);
142 // Sort children by duration
143 threadNode
.getChildren().stream()
144 .sorted(Comparator
.comparingLong(AggregatedCalledFunction
::getDuration
))
145 .forEach(rootFunction
-> {
146 setData(rootFunction
, childrenEntries
, timestampStack
);
147 long currentThreadDuration
= timestampStack
.pop() + rootFunction
.getDuration();
148 timestampStack
.push(currentThreadDuration
);
150 childrenEntries
.forEach(child
-> {
152 threadEntry
.addChild(child
);
155 threadEntry
.updateEndTime(timestampStack
.pop());
156 threadEntry
.setName(threadNode
.getSymbol().toString());
157 fFlameGraphEntries
.add(threadEntry
);
161 public ITimeGraphEntry
[] getChildren(Object parentElement
) {
162 return fFlameGraphEntries
.toArray(new TimeGraphEntry
[fFlameGraphEntries
.size()]);
166 public ITimeGraphEntry
getParent(Object element
) {
172 public void dispose() {
177 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
183 * Get the sort option
185 * @return the sort option.
187 public SortOption
getSortOption() {
192 * Set the sort option for sorting the thread entries
195 * the sort option to set
198 public void setSortOption(SortOption sortOption
) {
199 fSortOption
= sortOption
;
200 switch (sortOption
) {
202 fThreadComparator
= new ThreadNameComparator();
205 fThreadComparator
= checkNotNull(new ThreadNameComparator().reversed());
208 fThreadComparator
= new ThreadIdComparator();
211 fThreadComparator
= checkNotNull(new ThreadIdComparator().reversed());