--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.analysis.timing.core.callgraph;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+
+/**
+ * This class represents a function call in a certain level in the call
+ * stack. It's used to build an aggregation segment tree (aggregated by depth and
+ * callers). Per example,the two calls to the function A() in the call graph
+ * below will be combined into one node in the generated tree:
+ *
+ * <pre>
+ * (Depth=0) main main
+ * ↓↑ ↓↑ ↓↑ => ↓↑ ↓↑
+ * (Depth=1) A() B() A() A() B()
+ * </pre>
+ *
+ * @author Sonia Farrah
+ *
+ */
+public class AggregatedCalledFunction {
+
+ // ------------------------------------------------------------------------
+ // Attributes
+ // ------------------------------------------------------------------------
+
+ private final Object fSymbol;
+ private final int fDepth;
+ private final int fMaxDepth;
+ private final Map<Object, AggregatedCalledFunction> fChildren = new HashMap<>();
+ private final @Nullable AggregatedCalledFunction fParent;
+ private int fNbCalls = 0;
+ private long fDuration;
+ private long fSelfTime;
+
+ /**
+ * Constructor
+ * @param symbol
+ * The function's name or address
+ * @param duration
+ * The function's duration
+ * @param depth
+ * The function's depth
+ * @param maxDepth
+ * The aggregation tree's maximum depth
+ * @param parent
+ * The function's caller
+ */
+ public AggregatedCalledFunction(Object symbol, long duration, int depth, int maxDepth, @Nullable AggregatedCalledFunction parent) {
+ fSymbol = symbol;
+ fDuration = duration;
+ fSelfTime = duration;
+ fDepth = depth;
+ fMaxDepth = maxDepth;
+ fParent = parent;
+ }
+
+ /**
+ * The function's symbol (address or name)
+ *
+ * @return The function's symbol
+ */
+ public Object getSymbol() {
+ return fSymbol;
+ }
+
+ /**
+ * The callees of the function
+ *
+ * @return The function's callees
+ */
+ public synchronized Collection<AggregatedCalledFunction> getChildren() {
+ return fChildren.values();
+ }
+
+ /**
+ * The function's caller
+ *
+ * @return The caller of a function
+ */
+ public @Nullable AggregatedCalledFunction getParent() {
+ return fParent;
+ }
+
+ /**
+ * Add a new callee into the Callees list. If the function exists in the
+ * callees list, the new callee's duration will be added to its duration and
+ * it'll combine their callees.
+ *
+ * @param child
+ * The callees of a function
+ */
+ public synchronized void addChild(AggregatedCalledFunction child) {
+ AggregatedCalledFunction node = fChildren.get(child.getSymbol());
+ if (node == null) {
+ child.incrementCalls();
+ fChildren.put(child.getSymbol(), child);
+ } else {
+ merge(node, child);
+ fChildren.replace(node.getSymbol(), node);
+ }
+ fSelfTime -= child.fDuration;
+ }
+
+ /**
+ * Modify the function's duration
+ *
+ * @param duration
+ * The amount to increment the duration by
+ */
+ private void addToDuration(long duration) {
+ fDuration += duration;
+ }
+
+ /**
+ * Merge the callees of two functions.
+ *
+ * @param firstNode
+ * The first parent secondNode The second parent
+ */
+ private static void mergeChildren(AggregatedCalledFunction firstNode, AggregatedCalledFunction secondNode) {
+ for (Map.Entry<Object, AggregatedCalledFunction> FunctionEntry : secondNode.fChildren.entrySet()) {
+ Object childSymbol = NonNullUtils.checkNotNull(FunctionEntry.getKey());
+ AggregatedCalledFunction secondNodeChild = NonNullUtils.checkNotNull(FunctionEntry.getValue());
+ AggregatedCalledFunction aggregatedCalledFunction = firstNode.fChildren.get(childSymbol);
+ if (aggregatedCalledFunction == null) {
+ firstNode.fChildren.put(secondNodeChild.getSymbol(), secondNodeChild);
+ } else {
+ // combine children
+ AggregatedCalledFunction firstNodeChild = aggregatedCalledFunction;
+ merge(firstNodeChild, secondNodeChild);
+ firstNode.fChildren.replace(firstNodeChild.getSymbol(), firstNodeChild);
+ }
+ }
+ }
+
+ /**
+ * Merge two functions, add durations, self times, increment the calls,
+ * update statistics and merge children.
+ *
+ * @param destination
+ * the node to merge to
+ * @param source
+ * the node to merge
+ */
+ private static void merge(AggregatedCalledFunction destination, AggregatedCalledFunction source) {
+ destination.addToDuration(source.getDuration());
+ destination.addToSelfTime(source.getSelfTime());
+ destination.incrementCalls();
+ // merge the children callees.
+ mergeChildren(destination, source);
+ }
+
+ /**
+ * The function's duration
+ *
+ * @return The duration of the function
+ */
+ public long getDuration() {
+ return fDuration;
+ }
+
+ /**
+ * The function's depth
+ *
+ * @return The depth of the function
+ */
+ public int getDepth() {
+ return fDepth;
+ }
+
+ /**
+ * The depth of the aggregated tree
+ *
+ * @return The depth of the aggregated tree
+ */
+ public int getMaxDepth() {
+ return fMaxDepth;
+ }
+
+ /**
+ * The number of calls of a function
+ *
+ * @return The number of calls of a function
+ */
+ public int getNbCalls() {
+ return fNbCalls;
+ }
+
+ /**
+ * The number of calls of a function
+ */
+ public void incrementCalls() {
+ fNbCalls++;
+ }
+
+ /**
+ * The self time of an aggregated function
+ *
+ * @return The self time
+ */
+ public long getSelfTime() {
+ return fSelfTime;
+ }
+
+ /**
+ * Add to the self time of an aggregated function
+ *
+ * @param selfTime
+ * The amount of self time to add
+ */
+ public void addToSelfTime(long selfTime) {
+ fSelfTime += selfTime;
+ }
+
+ /**
+ * Returns whether the function has callees.
+ *
+ * @return Boolean
+ */
+ public Boolean hasChildren() {
+ return !fChildren.isEmpty();
+ }
+}
* The sub attributes of a certain thread
*/
private List<Integer> fCurrentQuarks = Collections.emptyList();
+ /**
+ * The List of thread nodes. Each thread has a virtual node having the root
+ * function as children
+ */
+ private List<AggregatedCalledFunction> fThreadNodes = new ArrayList<>();
/**
* Default constructor
* @return Boolean
*/
private boolean iterateOverQuark(ITmfStateSystem stateSystem, int quark, String[] subAttributePath, IProgressMonitor monitor) {
+ String threadName = stateSystem.getAttributeName(quark);
+ AggregatedCalledFunction init = new AggregatedCalledFunction(threadName, 0L, 0, 0, null);
try {
long curTime = stateSystem.getStartTime();
long limit = stateSystem.getCurrentEndTime();
// Create the segment for the first call event.
AbstractCalledFunction segment = CalledFunctionFactory.create(intervalStart, intervalEnd + 1, depth, stateValue, null);
fRootFunctions.add(segment);
- if (!findChildren(segment, depth, stateSystem, fCurrentQuarks.size() + fCurrentQuarks.get(depth), monitor)) {
+ AggregatedCalledFunction firstNode = new AggregatedCalledFunction(stateValue, intervalEnd - intervalStart + 1, segment.getDepth(), fCurrentQuarks.size(), null);
+ if (!findChildren(segment, depth, stateSystem, fCurrentQuarks.size() + fCurrentQuarks.get(depth), firstNode, monitor)) {
return false;
}
-
+ init.addChild(firstNode);
}
curTime = interval.getEndTime() + 1;
Activator.getInstance().logError(Messages.QueringStateSystemError, e);
return false;
}
+ fThreadNodes.add(init);
return true;
}
* The quark of the segment parent ss The actual state system
* @param maxQuark
* The last quark in the state system
+ * @param AggregatedCalledFunction
+ * A node in the aggregation tree
* @param monitor
* The progress monitor The progress monitor TODO: if stack size
* is an issue, convert to a stack instead of recursive function
*/
- private boolean findChildren(AbstractCalledFunction node, int depth, ITmfStateSystem ss, int maxQuark, IProgressMonitor monitor) {
+ private boolean findChildren(AbstractCalledFunction node, int depth, ITmfStateSystem ss, int maxQuark, AggregatedCalledFunction aggregatedCalledFunction, IProgressMonitor monitor) {
fStore.add(node);
long curTime = node.getStart();
long limit = node.getEnd();
return true;
}
AbstractCalledFunction segment = CalledFunctionFactory.create(intervalStart, intervalEnd + 1, node.getDepth() + 1, stateValue, node);
+ AggregatedCalledFunction childNode = new AggregatedCalledFunction(stateValue, segment.getLength(), segment.getDepth(), aggregatedCalledFunction.getMaxDepth(), aggregatedCalledFunction);
// Search for the children with the next quark.
- findChildren(segment, depth + 1, ss, maxQuark, monitor);
+ findChildren(segment, depth + 1, ss, maxQuark, childNode, monitor);
+ aggregatedCalledFunction.addChild(childNode);
node.addChild(segment);
}
curTime = interval.getEndTime() + 1;
return ImmutableList.copyOf(fRootFunctions);
}
+ /**
+ * List of thread nodes. Each thread has a virtual node having the root
+ * functions called as children.
+ *
+ * @return The thread nodes
+ */
+ public List<AggregatedCalledFunction> getThreadNodes() {
+ return ImmutableList.copyOf(fThreadNodes);
+ }
+
}
\ No newline at end of file
* @author Sonia Farrah
*/
public class Messages extends NLS {
- private static final String BUNDLE_NAME = "org.eclipse.tracecompass.internal.analysis.timing.core.callgraph.messages"; //$NON-NLS-1$
+ private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
/**
* Analysis description for the help
*/
org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.table,
org.eclipse.tracecompass.internal.analysis.timing.ui,
org.eclipse.tracecompass.internal.analysis.timing.ui.callgraph;x-internal:=true,
+ org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph;x-internal:=true,
org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density,
org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.scatter,
org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.statistics,
view.criticalpath = Critical Flow View
view.execgraph = Execution Graph View
view.callgraphDensity= Function Durations Distribution
+view.flameGraph= Flame Graph
callgraph.analysis = Call Graph Analysis
\ No newline at end of file
class="org.eclipse.tracecompass.internal.analysis.timing.ui.callgraph.CallGraphAnalysisUI">
</analysisModuleClass>
</output>
+ <output
+ class="org.eclipse.tracecompass.tmf.ui.analysis.TmfAnalysisViewOutput"
+ id="org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph.flamegraphView">
+ <analysisModuleClass
+ class="org.eclipse.tracecompass.internal.analysis.timing.ui.callgraph.CallGraphAnalysisUI">
+ </analysisModuleClass>
+ </output>
</extension>
<extension
point="org.eclipse.ui.views">
name="%view.callgraphDensity"
restorable="true">
</view>
+ <view
+ category="org.eclipse.linuxtools.tmf.ui.views.category"
+ class="org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph.FlameGraphView"
+ icon="icons/elcl16/flame.png"
+ id="org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph.flamegraphView"
+ name="%view.flameGraph"
+ restorable="true">
+ </view>
</extension>
</plugin>
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.List;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.internal.analysis.timing.core.callgraph.AggregatedCalledFunction;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphContentProvider;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Content provider for the flame graph view
+ *
+ * @author Sonia Farrah
+ *
+ */
+public class FlameGraphContentProvider implements ITimeGraphContentProvider {
+
+ private static final int MODULO = FlameGraphPresentationProvider.NUM_COLORS / 2;
+ private List<FlamegraphDepthEntry> fFlameGraphEntries = new ArrayList<>();
+ private long fThreadDuration;
+ private ITmfTrace fActiveTrace;
+
+ /**
+ * Parse the aggregated tree created by the callGraphAnalysis and creates
+ * the event list (functions) for each entry (depth)
+ *
+ * @param firstNode
+ * The first node of the aggregation tree
+ * @param childrenEntries
+ * The list of entries for one thread
+ * @param timestampStack
+ * A stack used to save the functions timeStamps
+ */
+ private void setData(AggregatedCalledFunction firstNode, List<FlamegraphDepthEntry> childrenEntries, Deque<Long> timestampStack) {
+ for (int i = 0; i < firstNode.getMaxDepth(); i++) {
+ if (i >= childrenEntries.size()) {
+ FlamegraphDepthEntry entry = new FlamegraphDepthEntry(String.valueOf(i), 0, fActiveTrace.getEndTime().toNanos() - fActiveTrace.getStartTime().toNanos(), i);
+ childrenEntries.add(entry);
+ }
+ }
+ int value = String.valueOf(firstNode.getSymbol()).hashCode() % MODULO + MODULO;
+ FlamegraphDepthEntry firstEntry = NonNullUtils.checkNotNull(childrenEntries.get(0));
+ firstEntry.addEvent(new FlamegraphEvent(firstEntry, timestampStack.peek(), firstNode.getDuration(), value, firstNode.getSymbol(), 1, firstNode.getSelfTime()));
+ // Build the event list for next entries (next depth)
+ addEvent(firstNode, childrenEntries, timestampStack);
+ }
+
+ /**
+ * Build the events list for an entry (depth), then creates recursively the
+ * events for the next entries. This parses the aggregation tree starting
+ * from the bottom. This uses a stack to save the timestamp for each
+ * function. Once we save a function's timestamp we'll use it to create the
+ * callees events.
+ *
+ * @param node
+ * The node of the aggregation tree
+ * @param childrenEntries
+ * The list of entries for one thread
+ * @param timestampStack
+ * A stack used to save the functions timeStamps
+ */
+ private void addEvent(AggregatedCalledFunction node, List<FlamegraphDepthEntry> childrenEntries, Deque<Long> timestampStack) {
+ if (node.hasChildren()) {
+ node.getChildren().stream()
+ .sorted(Comparator.comparingLong(AggregatedCalledFunction::getDuration))
+ .forEach(child -> {
+ addEvent(child, childrenEntries, timestampStack);
+ });
+ // Pop the children timestamps from the stack
+ node.getChildren().stream().forEach(child -> {
+ timestampStack.pop();
+ });
+ }
+ FlamegraphDepthEntry entry = NonNullUtils.checkNotNull(childrenEntries.get(node.getDepth()));
+ // Get the color of the event
+ int value = String.valueOf(node.getSymbol()).hashCode() % MODULO + MODULO;
+ // Create the event corresponding to the function using the caller's
+ // timestamp
+ entry.addEvent(new FlamegraphEvent(entry, timestampStack.peek(),
+ node.getDuration(), value, node.getSymbol(), node.getNbCalls(),
+ node.getSelfTime()));
+ timestampStack.push(timestampStack.peek() + node.getDuration());
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ return !fFlameGraphEntries.isEmpty();
+ }
+
+ @Override
+ public ITimeGraphEntry[] getElements(Object inputElement) {
+ fFlameGraphEntries.clear();
+ // Get the root of each thread
+ fActiveTrace = TmfTraceManager.getInstance().getActiveTrace();
+ if (fActiveTrace == null) {
+ return new ITimeGraphEntry[0];
+ }
+
+ if (inputElement instanceof List<?>) {
+ List<?> threadNodes = (List<?>) inputElement;
+ for (Object object : threadNodes) {
+ if (object instanceof AggregatedCalledFunction) {
+ buildChildrenEntries((AggregatedCalledFunction) object);
+ }
+ }
+ }
+ // Reverse the order of threads
+ fFlameGraphEntries = Lists.reverse(fFlameGraphEntries);
+ return fFlameGraphEntries.toArray(new ITimeGraphEntry[fFlameGraphEntries.size()]);
+ }
+
+ /**
+ * Build the entry list for one thread
+ *
+ * @param threadNode
+ * The node of the aggregation tree
+ */
+ private void buildChildrenEntries(AggregatedCalledFunction threadNode) {
+ FlamegraphDepthEntry threadEntry = new FlamegraphDepthEntry("", 0, fActiveTrace.getEndTime().toNanos() - fActiveTrace.getStartTime().toNanos(), fFlameGraphEntries.size()); //$NON-NLS-1$
+ List<FlamegraphDepthEntry> childrenEntries = new ArrayList<>();
+ fThreadDuration = 0L;
+ // Sort children by duration
+ threadNode.getChildren().stream()
+ .sorted(Comparator.comparingLong(AggregatedCalledFunction::getDuration))
+ .forEach(rootFunction -> {
+ Deque<Long> timestampStack = new ArrayDeque<>();
+ timestampStack.push(fThreadDuration);
+ setData(rootFunction, childrenEntries, timestampStack);
+ fThreadDuration += rootFunction.getDuration();
+ timestampStack.pop();
+ });
+ childrenEntries.forEach(child -> {
+ if (child != null) {
+ threadEntry.addChild(child);
+ }
+ });
+ threadEntry.setName(threadNode.getSymbol().toString());
+ fFlameGraphEntries.add(threadEntry);
+ }
+
+ @Override
+ public ITimeGraphEntry[] getChildren(Object parentElement) {
+ return fFlameGraphEntries.toArray(new TimeGraphEntry[fFlameGraphEntries.size()]);
+ }
+
+ @Override
+ public ITimeGraphEntry getParent(Object element) {
+ // Do nothing
+ return null;
+ }
+
+ @Override
+ public void dispose() {
+ // Do nothing
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // Do nothing
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph;
+
+import java.text.Format;
+import java.text.NumberFormat;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.SubSecondTimeWithUnitFormat;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
+import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.ui.symbols.ISymbolProvider;
+import org.eclipse.tracecompass.tmf.ui.symbols.SymbolProviderManager;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Presentation provider for the flame graph view, based on the generic TMF
+ * presentation provider.
+ *
+ * @author Sonia Farrah
+ */
+public class FlameGraphPresentationProvider extends TimeGraphPresentationProvider {
+
+ /** Number of colors used for flameGraph events */
+ public static final int NUM_COLORS = 360;
+
+ private static final Format FORMATTER = new SubSecondTimeWithUnitFormat();
+
+ private FlameGraphView fView;
+
+ private Integer fAverageCharWidth;
+
+ private enum State {
+ MULTIPLE(new RGB(100, 100, 100)), EXEC(new RGB(0, 200, 0));
+
+ private final RGB rgb;
+
+ private State(RGB rgb) {
+ this.rgb = rgb;
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ */
+ public FlameGraphPresentationProvider() {
+ }
+
+ @Override
+ public StateItem[] getStateTable() {
+ final float saturation = 0.6f;
+ final float brightness = 0.6f;
+ StateItem[] stateTable = new StateItem[NUM_COLORS + 1];
+ stateTable[0] = new StateItem(State.MULTIPLE.rgb, State.MULTIPLE.toString());
+ for (int i = 0; i < NUM_COLORS; i++) {
+ RGB rgb = new RGB(i, saturation, brightness);
+ stateTable[i + 1] = new StateItem(rgb, State.EXEC.toString());
+ }
+ return stateTable;
+ }
+
+ @Override
+ public boolean displayTimesInTooltip() {
+ return false;
+ }
+
+ @Override
+ public String getStateTypeName() {
+ return Messages.FlameGraph_Depth;
+ }
+
+ @Override
+ public Map<String, String> getEventHoverToolTipInfo(ITimeEvent event, long hoverTime) {
+ return ImmutableMap.of(
+ NonNullUtils.checkNotNull(Messages.FlameGraph_Duration), String.format("%s", FORMATTER.format(event.getDuration())), //$NON-NLS-1$
+ NonNullUtils.checkNotNull(Messages.FlameGraph_SelfTime), String.format("%s", FORMATTER.format(((FlamegraphEvent) event).getSelfTime())), //$NON-NLS-1$
+ NonNullUtils.checkNotNull(Messages.FlameGraph_NbCalls), NonNullUtils.checkNotNull(NumberFormat.getIntegerInstance().format(((FlamegraphEvent) event).getNbCalls())) // $NON-NLS-1$
+ );
+ }
+
+ @Override
+ public int getStateTableIndex(ITimeEvent event) {
+ if (event instanceof FlamegraphEvent) {
+ FlamegraphEvent flameGraphEvent = (FlamegraphEvent) event;
+ return flameGraphEvent.getValue() + 1;
+ } else if (event instanceof NullTimeEvent) {
+ return INVISIBLE;
+ }
+ return State.MULTIPLE.ordinal();
+ }
+
+ /**
+ * Get the event's symbol. It could be an address or a name.
+ *
+ * @param fGEvent
+ * An event
+ * @param symbolProvider
+ * A symbol provider
+ */
+ private static String getFuntionSymbol(FlamegraphEvent event, ISymbolProvider symbolProvider) {
+ String funcSymbol = ""; //$NON-NLS-1$
+ if (event.getSymbol() instanceof TmfStateValue) {
+ ITmfStateValue symbol = (ITmfStateValue) event.getSymbol();
+ switch (symbol.getType()) {
+ case LONG:
+ Long longAddress = symbol.unboxLong();
+ funcSymbol = symbolProvider.getSymbolText(longAddress);
+ if (funcSymbol == null) {
+ return "0x" + Long.toHexString(longAddress); //$NON-NLS-1$
+ }
+ return funcSymbol;
+ case STRING:
+ return symbol.unboxStr();
+ case INTEGER:
+ Integer intAddress = symbol.unboxInt();
+ funcSymbol = symbolProvider.getSymbolText(intAddress);
+ if (funcSymbol == null) {
+ return "0x" + Integer.toHexString(intAddress); //$NON-NLS-1$
+ }
+ return funcSymbol;
+ case CUSTOM:
+ case DOUBLE:
+ case NULL:
+ default:
+ break;
+ }
+ }
+ return funcSymbol;
+ }
+
+ @Override
+ public void postDrawEvent(ITimeEvent event, Rectangle bounds, GC gc) {
+ if (fAverageCharWidth == null) {
+ fAverageCharWidth = gc.getFontMetrics().getAverageCharWidth();
+ }
+ if (bounds.width <= fAverageCharWidth) {
+ return;
+ }
+ if (!(event instanceof FlamegraphEvent)) {
+ return;
+ }
+ String funcSymbol = ""; //$NON-NLS-1$
+ ITmfTrace activeTrace = TmfTraceManager.getInstance().getActiveTrace();
+ if (activeTrace != null) {
+ FlamegraphEvent fgEvent = (FlamegraphEvent) event;
+ ISymbolProvider symbolProvider = SymbolProviderManager.getInstance().getSymbolProvider(activeTrace);
+ funcSymbol = getFuntionSymbol(fgEvent, symbolProvider);
+ }
+ gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
+ Utils.drawText(gc, funcSymbol, bounds.x, bounds.y, bounds.width, bounds.height, true, true);
+ }
+
+ /**
+ * The flame graph view
+ *
+ * @return The flame graph view
+ */
+ public FlameGraphView getView() {
+ return fView;
+ }
+
+ /**
+ * The flame graph view
+ *
+ * @param view
+ * The flame graph view
+ */
+ public void setView(FlameGraphView view) {
+ fView = view;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Author:
+ * Sonia Farrah
+ *******************************************************************************/
+package org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tracecompass.internal.analysis.timing.core.callgraph.CallGraphAnalysis;
+import org.eclipse.tracecompass.internal.analysis.timing.ui.callgraph.CallGraphAnalysisUI;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor;
+import org.eclipse.tracecompass.tmf.ui.views.TmfView;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphViewer;
+import org.eclipse.ui.IEditorPart;
+
+/**
+ * View to display the flame graph .This uses the flameGraphNode tree generated
+ * by CallGraphAnalysisUI.
+ *
+ * @author Sonia Farrah
+ */
+public class FlameGraphView extends TmfView {
+
+ /**
+ *
+ */
+ public static final String ID = FlameGraphView.class.getPackage().getName() + ".flamegraphView"; //$NON-NLS-1$
+
+ private TimeGraphViewer fTimeGraphViewer;
+
+ private FlameGraphContentProvider fTimeGraphContentProvider;
+
+ private TimeGraphPresentationProvider fPresentationProvider;
+
+ private ITmfTrace fTrace;
+
+ /**
+ * Constructor
+ */
+ public FlameGraphView() {
+ super(ID);
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ super.createPartControl(parent);
+ fTimeGraphViewer = new TimeGraphViewer(parent, SWT.NONE);
+ fTimeGraphContentProvider = new FlameGraphContentProvider();
+ fPresentationProvider = new FlameGraphPresentationProvider();
+ fTimeGraphViewer.setTimeGraphContentProvider(fTimeGraphContentProvider);
+ fTimeGraphViewer.setTimeGraphProvider(fPresentationProvider);
+ IEditorPart editor = getSite().getPage().getActiveEditor();
+ if (editor instanceof ITmfTraceEditor) {
+ ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace();
+ if (trace != null) {
+ traceSelected(new TmfTraceSelectedSignal(this, trace));
+ }
+ }
+ }
+
+ /**
+ * Signal updated
+ *
+ * @param sig
+ * signal
+ */
+ @TmfSignalHandler
+ public void selectionUpdated(TmfSelectionRangeUpdatedSignal sig) {
+ fTrace = TmfTraceManager.getInstance().getActiveTrace();
+ if (fTrace != null) {
+ CallGraphAnalysis flamegraphModule = TmfTraceUtils.getAnalysisModuleOfClass(fTrace, CallGraphAnalysis.class, CallGraphAnalysisUI.ID);
+ buildFlameGraph(flamegraphModule);
+ }
+ }
+
+ /**
+ * Handler for the trace opened signal
+ *
+ * @param signal
+ * The incoming signal
+ */
+ @TmfSignalHandler
+ public void TraceOpened(TmfTraceOpenedSignal signal) {
+ fTrace = signal.getTrace();
+ if (fTrace != null) {
+ CallGraphAnalysis flamegraphModule = TmfTraceUtils.getAnalysisModuleOfClass(fTrace, CallGraphAnalysis.class, CallGraphAnalysisUI.ID);
+ buildFlameGraph(flamegraphModule);
+ }
+ }
+
+ /**
+ * Handler for the trace selected signal
+ *
+ * @param signal
+ * The incoming signal
+ */
+ @TmfSignalHandler
+ public void traceSelected(final TmfTraceSelectedSignal signal) {
+ fTrace = signal.getTrace();
+ if (fTrace != null) {
+ CallGraphAnalysis flamegraphModule = TmfTraceUtils.getAnalysisModuleOfClass(fTrace, CallGraphAnalysis.class, CallGraphAnalysisUI.ID);
+ buildFlameGraph(flamegraphModule);
+ }
+ }
+
+ /**
+ * Get the necessary data for the flame graph and display it
+ *
+ * @param flamegraphModule
+ * the callGraphAnalysis
+ */
+ private void buildFlameGraph(CallGraphAnalysis callGraphAnalysis) {
+ fTimeGraphViewer.setInput(null);
+ callGraphAnalysis.schedule();
+ Job j = new Job(Messages.CallGraphAnalysis_Execution) {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ callGraphAnalysis.waitForCompletion(monitor);
+ Display.getDefault().asyncExec(() -> {
+ fTimeGraphViewer.setInput(callGraphAnalysis.getThreadNodes());
+ });
+ return Status.OK_STATUS;
+ }
+ };
+ j.schedule();
+ }
+
+ /**
+ * Trace is closed: clear the data structures and the view
+ *
+ * @param signal
+ * the signal received
+ */
+ @TmfSignalHandler
+ public void traceClosed(final TmfTraceClosedSignal signal) {
+ if (signal.getTrace() == fTrace) {
+ fTimeGraphViewer.setInput(null);
+ }
+ }
+
+ @Override
+ public void setFocus() {
+ fTimeGraphViewer.setFocus();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph;
+
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
+
+/**
+ * An entry, or row, in the flame Graph view
+ *
+ * @author Sonia Farrah
+ */
+public class FlamegraphDepthEntry extends TimeGraphEntry {
+
+ private final int fDepth;
+
+ /**
+ * Constructor
+ *
+ * @param name
+ * name of an entry
+ * @param startTime
+ * Start time of an entry
+ * @param endTime
+ * The end time of an entry
+ * @param depth
+ * The Depth of an entry
+ */
+ public FlamegraphDepthEntry(String name, long startTime, long endTime, int depth) {
+ super(name, startTime, endTime);
+ fDepth = depth;
+ }
+
+ /**
+ * The depth of a flame graph entry
+ *
+ * @return The depth of a flame graph entry
+ */
+ public int getDepth() {
+ return fDepth;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph;
+
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
+
+/**
+ * Time Event implementation specific to the FlameGraph View (it represents a
+ * function in a certain depth)
+ *
+ * @author Sonia Farrah
+ *
+ */
+public class FlamegraphEvent extends TimeEvent {
+
+ private final Object fSymbol;
+ private final int fNbCalls;
+ private final long fSelfTime;
+
+ /**
+ * Constructor
+ *
+ * @param source
+ * The Entry
+ * @param beginTime
+ * The event's begin time
+ * @param totalTime
+ * The event's total time
+ * @param value
+ * The event's value
+ * @param symbol
+ * The event's address or name
+ * @param nbCalls
+ * The event's number of calls
+ * @param selfTime
+ * The event's self time
+ */
+ public FlamegraphEvent(ITimeGraphEntry source, long beginTime, long totalTime, int value, Object symbol, int nbCalls, long selfTime) {
+ super(source, beginTime, totalTime, value);
+ fSymbol = symbol;
+ fNbCalls = nbCalls;
+ fSelfTime = selfTime;
+ }
+
+ /**
+ * The event's name or address
+ *
+ * @return The event's name or address
+ */
+ public Object getSymbol() {
+ return fSymbol;
+ }
+
+ /**
+ * The event's number of calls
+ *
+ * @return The event's number of a calls
+ */
+ public int getNbCalls() {
+ return fNbCalls;
+ }
+
+ /**
+ * The self time of an event
+ *
+ * @return The self time
+ */
+ public long getSelfTime() {
+ return fSelfTime;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.tracecompass.internal.analysis.timing.ui.flamegraph;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the flame graph view
+ *
+ * @author Sonia Farrah
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+ /**
+ * The duration of a function
+ */
+ public static String FlameGraph_Duration;
+ /**
+ * The time percentage of a function
+ */
+ public static String FlameGraph_Percentage;
+ /**
+ * The number of calls of a function
+ */
+ public static String FlameGraph_NbCalls;
+ /**
+ * The self time of a function
+ */
+ public static String FlameGraph_SelfTime;
+ /**
+ * The depth of a function
+ */
+ public static String FlameGraph_Depth;
+ /**
+ * Execution of the callGraph Analysis
+ */
+ public static String CallGraphAnalysis_Execution;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
\ No newline at end of file
--- /dev/null
+###############################################################################
+# Copyright (c) 2016 Ericsson
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+FlameGraph_Duration=Duration
+FlameGraph_Percentage=Percentage
+FlameGraph_NbCalls=Number of calls
+FlameGraph_SelfTime=Self time
+FlameGraph_Depth=Depth
+CallGraphAnalysis_Execution=CallGraphAnalysis execution
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.tracecompass.lttng2.ust.core.trace.layout.ILttngUstEventLayout;
-import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAnalysisEventFieldRequirement;
import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAbstractAnalysisRequirement;
+import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAnalysisEventFieldRequirement;
import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfCompositeAnalysisRequirement;
import com.google.common.collect.ImmutableSet;
import org.eclipse.osgi.util.NLS;
/**
- * Message bundle for the ust memory analysis module
+ * Message bundle for the call stack analysis module
*
- * @author Bernd Hufmann
+ * @author Sonia Farrah
*/
+
public class Messages extends NLS {
private static final String BUNDLE_NAME = "org.eclipse.tracecompass.internal.lttng2.ust.ui.analysis.callstack.messages"; //$NON-NLS-1$
- /** Information regarding events loading prior to the analysis execution */
- public static String LttnUstCallStackAnalysisModule_EventsLoadingInformation;
+ /**
+ * Self time of a function
+ */
+ public static String SegmentStoreStaticsViewer_selfTime;
+ /**
+ * Total time of a function
+ */
+ public static String SegmentStoreStaticsViewer_totalTime;
+
+ /**
+ * Adress of a function
+ */
+ public static String Function_address;
+ /**
+ * Total self Time of a function
+ */
+ public static String SegmentStoreStaticsViewer_totalSelfTime;
+
+ /**
+ * Total calls of a function
+ */
+ public static String SegmentStoreStaticsViewer_totalCalls;
+ /**
+ *The callers of a function
+ */
+ public static String SegmentStoreStaticsViewer_Callers;
+ /**
+ *The callees of a function
+ */
+ public static String SegmentStoreStaticsViewer_Callees;
+ /**
+ *The depth of a function
+ */
+ public static String SegmentStoreStaticsViewer_Depth;
+ /**
+ *The duration of a function
+ */
+ public static String FlameGraph_Duration;
+ /**
+ *The time percentage of a function
+ */
+ public static String FlameGraph_Percentage;
+ /**
+ *The number of calls of a function
+ */
+ public static String FlameGraph_NbreCalls;
static {
// initialize resource bundle
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
}
+ /** Information regarding events loading prior to the analysis execution */
+ public static String LttnUstCallStackAnalysisModule_EventsLoadingInformation;
+
private Messages() {
}
}
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Ericsson - Initial API and implementation
###############################################################################
LttnUstCallStackAnalysisModule_EventsLoadingInformation=Add the ''vtid'' and ''procname'' contexts to your trace session. \n\
Preload the ''liblttng-ust-cyg-profile'' library when running your program: \n\
LD_PRELOAD=/usr/lib/liblttng-ust-cyg-profile.so ./myprogram
+SegmentStoreStaticsViewer_selfTime=Self time
+SegmentStoreStaticsViewer_totalTime=Total time
+Function_address=address
+FlameGraph_Duration=Duration
+FlameGraph_Percentage=Percentage
+FlameGraph_NbreCalls=Number of calls
+
+SegmentStoreStaticsViewer_totalSelfTime=Total self time
+SegmentStoreStaticsViewer_totalCalls=Total calls
+SegmentStoreStaticsViewer_Callers=Callers
+SegmentStoreStaticsViewer_Callees=Callees
+SegmentStoreStaticsViewer_Depth=Depth