/*******************************************************************************
* 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 final AggregatedCalledFunctionStatistics fStatistics;
private long fDuration;
private long fSelfTime;
private final int fProcessId;
/**
* Constructor, parent is not null
*
* @param calledFunction
* called function
* @param parent
* the parent entry
*
*/
public AggregatedCalledFunction(AbstractCalledFunction calledFunction, AggregatedCalledFunction parent) {
fSymbol = calledFunction.getSymbol();
fDuration = calledFunction.getLength();
fSelfTime = calledFunction.getLength();
fDepth = calledFunction.getDepth();
fProcessId = calledFunction.getProcessId();
fMaxDepth = parent.getMaxDepth();
fParent = parent;
fStatistics = new AggregatedCalledFunctionStatistics();
}
/**
* Root constructor, parent is null
*
* @param calledFunction
* the called function
* @param maxDepth
* the maximum depth
*/
public AggregatedCalledFunction(AbstractCalledFunction calledFunction, int maxDepth) {
fSymbol = calledFunction.getSymbol();
fDuration = calledFunction.getLength();
fSelfTime = calledFunction.getLength();
fDepth = calledFunction.getDepth();
fProcessId = calledFunction.getProcessId();
fMaxDepth = maxDepth;
fParent = null;
fStatistics = new AggregatedCalledFunctionStatistics();
}
/**
* 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 callee to add to this function
* @param aggregatedChild
* The aggregated data of the callee
*/
public synchronized void addChild(AbstractCalledFunction child, AggregatedCalledFunction aggregatedChild) {
// Update the child's statistics with itself
fSelfTime -= aggregatedChild.getDuration();
aggregatedChild.getFunctionStatistics().update(child);
AggregatedCalledFunction node = fChildren.get(aggregatedChild.getSymbol());
if (node == null) {
fChildren.put(aggregatedChild.getSymbol(), aggregatedChild);
} else {
merge(node, aggregatedChild);
fChildren.replace(node.getSymbol(), node);
}
}
/**
* 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) {
long sourceDuration = source.getDuration();
long sourceSelfTime = source.getSelfTime();
destination.addToDuration(sourceDuration);
destination.addToSelfTime(sourceSelfTime);
destination.getFunctionStatistics().merge(source.getFunctionStatistics());
// 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 long getNbCalls() {
return fStatistics.getDurationStatistics().getNbElements();
}
/**
* 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;
}
/**
* The process ID of the trace application.
* @return The process Id
*/
public int getProcessId() {
return fProcessId;
}
/**
* Returns whether the function has callees.
*
* @return Boolean
*/
public Boolean hasChildren() {
return !fChildren.isEmpty();
}
/**
* The function's statistics
*
* @return The function's statistics
*/
public AggregatedCalledFunctionStatistics getFunctionStatistics() {
return fStatistics;
}
@Override
public String toString() {
return "Aggregate Function: " + getSymbol() + ", Duration: " + getDuration() + ", Self Time: " + fSelfTime + " on " + getNbCalls() + " calls"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
}
}