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
.tmf
.core
.trace
.ITmfTrace
;
22 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
23 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.ITimeGraphContentProvider
;
24 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
25 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeGraphEntry
;
27 import com
.google
.common
.collect
.Lists
;
30 * Content provider for the flame graph view
32 * @author Sonia Farrah
35 public class FlameGraphContentProvider
implements ITimeGraphContentProvider
{
37 private static final int MODULO
= FlameGraphPresentationProvider
.NUM_COLORS
/ 2;
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
);
57 childrenEntries
.add(entry
);
60 int value
= String
.valueOf(firstNode
.getSymbol()).hashCode() % MODULO
+ MODULO
;
61 FlamegraphDepthEntry firstEntry
= NonNullUtils
.checkNotNull(childrenEntries
.get(0));
62 firstEntry
.addEvent(new FlamegraphEvent(firstEntry
, timestampStack
.peek(), firstNode
.getDuration(), value
, firstNode
.getSymbol(), 1, firstNode
.getSelfTime()));
63 // Build the event list for next entries (next depth)
64 addEvent(firstNode
, childrenEntries
, timestampStack
);
68 * Build the events list for an entry (depth), then creates recursively the
69 * events for the next entries. This parses the aggregation tree starting
70 * from the bottom. This uses a stack to save the timestamp for each
71 * function. Once we save a function's timestamp we'll use it to create the
75 * The node of the aggregation tree
76 * @param childrenEntries
77 * The list of entries for one thread
78 * @param timestampStack
79 * A stack used to save the functions timeStamps
81 private void addEvent(AggregatedCalledFunction node
, List
<FlamegraphDepthEntry
> childrenEntries
, Deque
<Long
> timestampStack
) {
82 if (node
.hasChildren()) {
83 node
.getChildren().stream()
84 .sorted(Comparator
.comparingLong(AggregatedCalledFunction
::getDuration
))
86 addEvent(child
, childrenEntries
, timestampStack
);
88 // Pop the children timestamps from the stack
89 node
.getChildren().stream().forEach(child
-> {
93 FlamegraphDepthEntry entry
= NonNullUtils
.checkNotNull(childrenEntries
.get(node
.getDepth()));
94 // Get the color of the event
95 int value
= String
.valueOf(node
.getSymbol()).hashCode() % MODULO
+ MODULO
;
96 // Create the event corresponding to the function using the caller's
98 entry
.addEvent(new FlamegraphEvent(entry
, timestampStack
.peek(),
99 node
.getDuration(), value
, node
.getSymbol(), node
.getNbCalls(),
100 node
.getSelfTime()));
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 AggregatedCalledFunction
) {
122 buildChildrenEntries((AggregatedCalledFunction
) object
);
126 // Reverse the order of threads
127 fFlameGraphEntries
= Lists
.reverse(fFlameGraphEntries
);
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(AggregatedCalledFunction threadNode
) {
138 FlamegraphDepthEntry threadEntry
= new FlamegraphDepthEntry("", 0, fActiveTrace
.getEndTime().toNanos() - fActiveTrace
.getStartTime().toNanos(), fFlameGraphEntries
.size()); //$NON-NLS-1$
139 List
<FlamegraphDepthEntry
> childrenEntries
= new ArrayList
<>();
140 fThreadDuration
= 0L;
141 // Sort children by duration
142 threadNode
.getChildren().stream()
143 .sorted(Comparator
.comparingLong(AggregatedCalledFunction
::getDuration
))
144 .forEach(rootFunction
-> {
145 Deque
<Long
> timestampStack
= new ArrayDeque
<>();
146 timestampStack
.push(fThreadDuration
);
147 setData(rootFunction
, childrenEntries
, timestampStack
);
148 fThreadDuration
+= rootFunction
.getDuration();
149 timestampStack
.pop();
151 childrenEntries
.forEach(child
-> {
153 threadEntry
.addChild(child
);
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
) {