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 List
<FlamegraphDepthEntry
> fFlameGraphEntries
= new ArrayList
<>();
40 private long fThreadDuration
;
41 private ITmfTrace fActiveTrace
;
42 private SortOption fSortOption
= SortOption
.BY_NAME
;
43 private @NonNull Comparator
<FlamegraphDepthEntry
> fThreadComparator
= new ThreadNameComparator();
47 * Parse the aggregated tree created by the callGraphAnalysis and creates
48 * the event list (functions) for each entry (depth)
51 * The first node of the aggregation tree
52 * @param childrenEntries
53 * The list of entries for one thread
54 * @param timestampStack
55 * A stack used to save the functions timeStamps
57 private void setData(AggregatedCalledFunction firstNode
, List
<FlamegraphDepthEntry
> childrenEntries
, Deque
<Long
> timestampStack
) {
58 for (int i
= 0; i
< firstNode
.getMaxDepth(); i
++) {
59 if (i
>= childrenEntries
.size()) {
60 FlamegraphDepthEntry entry
= new FlamegraphDepthEntry(String
.valueOf(i
), 0, fActiveTrace
.getEndTime().toNanos() - fActiveTrace
.getStartTime().toNanos(), i
, i
);
61 childrenEntries
.add(entry
);
64 FlamegraphDepthEntry firstEntry
= checkNotNull(childrenEntries
.get(0));
65 firstEntry
.addEvent(new FlamegraphEvent(firstEntry
, timestampStack
.peek(), firstNode
));
66 // Build the event list for next entries (next depth)
67 addEvent(firstNode
, childrenEntries
, timestampStack
);
71 * Build the events list for an entry (depth), then creates recursively the
72 * events for the next entries. This parses the aggregation tree starting
73 * from the bottom. This uses a stack to save the timestamp for each
74 * function. Once we save a function's timestamp we'll use it to create the
78 * The node of the aggregation tree
79 * @param childrenEntries
80 * The list of entries for one thread
81 * @param timestampStack
82 * A stack used to save the functions timeStamps
84 private void addEvent(AggregatedCalledFunction node
, List
<FlamegraphDepthEntry
> childrenEntries
, Deque
<Long
> timestampStack
) {
85 if (node
.hasChildren()) {
86 node
.getChildren().stream()
87 .sorted(Comparator
.comparingLong(AggregatedCalledFunction
::getDuration
))
89 addEvent(child
, childrenEntries
, timestampStack
);
91 node
.getChildren().stream().forEach(child
-> {
95 FlamegraphDepthEntry entry
= checkNotNull(childrenEntries
.get(node
.getDepth()));
96 // Create the event corresponding to the function using the caller's
98 entry
.addEvent(new FlamegraphEvent(entry
, timestampStack
.peek(), node
));
99 timestampStack
.push(timestampStack
.peek() + node
.getDuration());
103 public boolean hasChildren(Object element
) {
104 return !fFlameGraphEntries
.isEmpty();
108 public ITimeGraphEntry
[] getElements(Object inputElement
) {
109 fFlameGraphEntries
.clear();
110 // Get the root of each thread
111 fActiveTrace
= TmfTraceManager
.getInstance().getActiveTrace();
112 if (fActiveTrace
== null) {
113 return new ITimeGraphEntry
[0];
116 if (inputElement
instanceof List
<?
>) {
117 List
<?
> threadNodes
= (List
<?
>) inputElement
;
118 for (Object object
: threadNodes
) {
119 if (object
instanceof ThreadNode
) {
120 buildChildrenEntries((ThreadNode
) object
);
125 fFlameGraphEntries
.sort(fThreadComparator
);
126 return fFlameGraphEntries
.toArray(new ITimeGraphEntry
[fFlameGraphEntries
.size()]);
130 * Build the entry list for one thread
133 * The node of the aggregation tree
135 private void buildChildrenEntries(ThreadNode threadNode
) {
136 FlamegraphDepthEntry threadEntry
= new FlamegraphDepthEntry("", 0, fActiveTrace
.getEndTime().toNanos() - fActiveTrace
.getStartTime().toNanos(), fFlameGraphEntries
.size(), threadNode
.getId()); //$NON-NLS-1$
137 List
<FlamegraphDepthEntry
> childrenEntries
= new ArrayList
<>();
138 fThreadDuration
= 0L;
139 // Sort children by duration
140 threadNode
.getChildren().stream()
141 .sorted(Comparator
.comparingLong(AggregatedCalledFunction
::getDuration
))
142 .forEach(rootFunction
-> {
143 Deque
<Long
> timestampStack
= new ArrayDeque
<>();
144 timestampStack
.push(fThreadDuration
);
145 setData(rootFunction
, childrenEntries
, timestampStack
);
146 fThreadDuration
+= rootFunction
.getDuration();
147 timestampStack
.pop();
149 childrenEntries
.forEach(child
-> {
151 threadEntry
.addChild(child
);
154 threadEntry
.setName(threadNode
.getSymbol().toString());
155 fFlameGraphEntries
.add(threadEntry
);
159 public ITimeGraphEntry
[] getChildren(Object parentElement
) {
160 return fFlameGraphEntries
.toArray(new TimeGraphEntry
[fFlameGraphEntries
.size()]);
164 public ITimeGraphEntry
getParent(Object element
) {
170 public void dispose() {
175 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
181 * Get the sort option
183 * @return the sort option.
185 public SortOption
getSortOption() {
190 * Set the sort option for sorting the thread entries
193 * the sort option to set
196 public void setSortOption(SortOption sortOption
) {
197 fSortOption
= sortOption
;
198 switch (sortOption
) {
200 fThreadComparator
= new ThreadNameComparator();
203 fThreadComparator
= checkNotNull(new ThreadNameComparator().reversed());
206 fThreadComparator
= new ThreadIdComparator();
209 fThreadComparator
= checkNotNull(new ThreadIdComparator().reversed());