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 java
.util
.ArrayDeque
;
13 import java
.util
.ArrayList
;
14 import java
.util
.Comparator
;
15 import java
.util
.Deque
;
16 import java
.util
.List
;
18 import org
.eclipse
.jface
.viewers
.Viewer
;
19 import org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
;
20 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.core
.callgraph
.AggregatedCalledFunction
;
21 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.core
.callgraph
.ThreadNode
;
22 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
23 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
24 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.ITimeGraphContentProvider
;
25 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
26 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeGraphEntry
;
28 import com
.google
.common
.collect
.Lists
;
31 * Content provider for the flame graph view
33 * @author Sonia Farrah
36 public class FlameGraphContentProvider
implements ITimeGraphContentProvider
{
38 private List
<FlamegraphDepthEntry
> fFlameGraphEntries
= new ArrayList
<>();
39 private long fThreadDuration
;
40 private ITmfTrace fActiveTrace
;
43 * Parse the aggregated tree created by the callGraphAnalysis and creates
44 * the event list (functions) for each entry (depth)
47 * The first node of the aggregation tree
48 * @param childrenEntries
49 * The list of entries for one thread
50 * @param timestampStack
51 * A stack used to save the functions timeStamps
53 private void setData(AggregatedCalledFunction firstNode
, List
<FlamegraphDepthEntry
> childrenEntries
, Deque
<Long
> timestampStack
) {
54 for (int i
= 0; i
< firstNode
.getMaxDepth(); i
++) {
55 if (i
>= childrenEntries
.size()) {
56 FlamegraphDepthEntry entry
= new FlamegraphDepthEntry(String
.valueOf(i
), 0, fActiveTrace
.getEndTime().toNanos() - fActiveTrace
.getStartTime().toNanos(), i
, i
);
57 childrenEntries
.add(entry
);
60 FlamegraphDepthEntry firstEntry
= NonNullUtils
.checkNotNull(childrenEntries
.get(0));
61 firstEntry
.addEvent(new FlamegraphEvent(firstEntry
, timestampStack
.peek(), firstNode
));
62 // Build the event list for next entries (next depth)
63 addEvent(firstNode
, childrenEntries
, timestampStack
);
67 * Build the events list for an entry (depth), then creates recursively the
68 * events for the next entries. This parses the aggregation tree starting
69 * from the bottom. This uses a stack to save the timestamp for each
70 * function. Once we save a function's timestamp we'll use it to create the
74 * The node of the aggregation tree
75 * @param childrenEntries
76 * The list of entries for one thread
77 * @param timestampStack
78 * A stack used to save the functions timeStamps
80 private void addEvent(AggregatedCalledFunction node
, List
<FlamegraphDepthEntry
> childrenEntries
, Deque
<Long
> timestampStack
) {
81 if (node
.hasChildren()) {
82 node
.getChildren().stream()
83 .sorted(Comparator
.comparingLong(AggregatedCalledFunction
::getDuration
))
85 addEvent(child
, childrenEntries
, timestampStack
);
87 node
.getChildren().stream().forEach(child
-> {
91 FlamegraphDepthEntry entry
= NonNullUtils
.checkNotNull(childrenEntries
.get(node
.getDepth()));
92 // Create the event corresponding to the function using the caller's
94 entry
.addEvent(new FlamegraphEvent(entry
, timestampStack
.peek(), node
));
95 timestampStack
.push(timestampStack
.peek() + node
.getDuration());
99 public boolean hasChildren(Object element
) {
100 return !fFlameGraphEntries
.isEmpty();
104 public ITimeGraphEntry
[] getElements(Object inputElement
) {
105 fFlameGraphEntries
.clear();
106 // Get the root of each thread
107 fActiveTrace
= TmfTraceManager
.getInstance().getActiveTrace();
108 if (fActiveTrace
== null) {
109 return new ITimeGraphEntry
[0];
112 if (inputElement
instanceof List
<?
>) {
113 List
<?
> threadNodes
= (List
<?
>) inputElement
;
114 for (Object object
: threadNodes
) {
115 if (object
instanceof ThreadNode
) {
116 buildChildrenEntries((ThreadNode
) object
);
120 // Reverse the order of threads
121 fFlameGraphEntries
= Lists
.reverse(fFlameGraphEntries
);
122 return fFlameGraphEntries
.toArray(new ITimeGraphEntry
[fFlameGraphEntries
.size()]);
126 * Build the entry list for one thread
129 * The node of the aggregation tree
131 private void buildChildrenEntries(ThreadNode threadNode
) {
132 FlamegraphDepthEntry threadEntry
= new FlamegraphDepthEntry("", 0, fActiveTrace
.getEndTime().toNanos() - fActiveTrace
.getStartTime().toNanos(), fFlameGraphEntries
.size(), threadNode
.getId()); //$NON-NLS-1$
133 List
<FlamegraphDepthEntry
> childrenEntries
= new ArrayList
<>();
134 fThreadDuration
= 0L;
135 // Sort children by duration
136 threadNode
.getChildren().stream()
137 .sorted(Comparator
.comparingLong(AggregatedCalledFunction
::getDuration
))
138 .forEach(rootFunction
-> {
139 Deque
<Long
> timestampStack
= new ArrayDeque
<>();
140 timestampStack
.push(fThreadDuration
);
141 setData(rootFunction
, childrenEntries
, timestampStack
);
142 fThreadDuration
+= rootFunction
.getDuration();
143 timestampStack
.pop();
145 childrenEntries
.forEach(child
-> {
147 threadEntry
.addChild(child
);
150 threadEntry
.setName(threadNode
.getSymbol().toString());
151 fFlameGraphEntries
.add(threadEntry
);
155 public ITimeGraphEntry
[] getChildren(Object parentElement
) {
156 return fFlameGraphEntries
.toArray(new TimeGraphEntry
[fFlameGraphEntries
.size()]);
160 public ITimeGraphEntry
getParent(Object element
) {
166 public void dispose() {
171 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {