Skip to content

Commit

Permalink
callstack: Bring incubated CallGraph, internal 1st
Browse files Browse the repository at this point in the history
Bring CallGraph from Incubator for an eventual ICallGraphProvider as
next step. Make this CallGraph internal (non-API) only for now, or until
proven as API once/if that becomes the case.

Bring these other classes/interfaces as minimally required dependencies.

Except for .tree which is trivial enough, wait before introducing
sub-packages as Incubator's, to not prematurely create some that could
be misnamed or contain too few classes/interfaces.

CallGraph and its dependencies added herein are tested through upstream
classes in Incubator. Thus postpone their test coverage until the latter
can be ported in turn.

Change-Id: I614575fac6464158f85f0d1a578131f3a4a8014c
Signed-off-by: Marco Miller <marco.miller@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/c/tracecompass/org.eclipse.tracecompass/+/197758
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
  • Loading branch information
marco-miller committed Mar 31, 2023
1 parent 9a6d5a0 commit 72a48a5
Show file tree
Hide file tree
Showing 17 changed files with 1,631 additions and 1 deletion.
Expand Up @@ -11,5 +11,9 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Automatic-Module-Name: org.eclipse.tracecompass.analysis.callstack.core
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional,
org.eclipse.tracecompass.common.core
org.eclipse.tracecompass.analysis.timing.core,
org.eclipse.tracecompass.common.core,
org.eclipse.tracecompass.tmf.core
Export-Package: org.eclipse.tracecompass.internal.analysis.callstack.core;x-friends:="org.eclipse.tracecompass.analysis.callstack.core.tests"
Import-Package: com.google.common.annotations,
com.google.common.collect
@@ -0,0 +1,97 @@
/*******************************************************************************
* Copyright (c) 2017-2019 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/

package org.eclipse.tracecompass.internal.analysis.callstack.core;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.eclipse.tracecompass.analysis.timing.core.statistics.IStatistics;
import org.eclipse.tracecompass.internal.analysis.callstack.core.tree.WeightedTree;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;

/**
* Base class for aggregating call site data from either sampled or instrumented
* call stacks.
*
* @author Geneviève Bastien
*/
public class AggregatedCallSite extends WeightedTree<ICallStackSymbol> {

/**
* Constructor
*
* @param symbol
* The symbol of the call site. It can eventually be resolved to
* a string using the symbol providers
* @param initialLength
* The initial length of this object
*/
public AggregatedCallSite(ICallStackSymbol symbol, long initialLength) {
super(symbol, initialLength);
}

/**
* Copy constructor
*
* @param copy
* The call site to copy
*/
protected AggregatedCallSite(AggregatedCallSite copy) {
super(copy);
}

/**
* Return the children as a collection of aggregatedCallSite
*
* @return The children as callees
*/
@VisibleForTesting
public Collection<AggregatedCallSite> getCallees() {
List<AggregatedCallSite> list = new ArrayList<>();
for (WeightedTree<ICallStackSymbol> child : getChildren()) {
if (child instanceof AggregatedCallSite) {
list.add((AggregatedCallSite) child);
}
}
return list;
}

/**
* Make a copy of this callsite, with its statistics. Implementing classes
* should make sure they copy all fields of the callsite, including the
* statistics.
*
* @return A copy of this aggregated call site
*/
@Override
public AggregatedCallSite copyOf() {
return new AggregatedCallSite(this);
}

/**
* Get additional statistics for this call site
*
* @return A map of statistics title with statistics
*/
public Map<String, IStatistics<?>> getStatistics() {
return ImmutableMap.of();
}

@Override
public String toString() {
return "CallSite: " + getObject(); //$NON-NLS-1$
}
}
@@ -0,0 +1,110 @@
/*******************************************************************************
* Copyright (c) 2017 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/

package org.eclipse.tracecompass.internal.analysis.callstack.core;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.eclipse.tracecompass.internal.analysis.callstack.core.tree.IWeightedTreeSet;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;

/**
* Represents a callgraph, ie the aggregation of callsites per elements.
*
* TODO: Have an interface and keep the add* method internal
*
* @author Geneviève Bastien
*/
public class CallGraph implements IWeightedTreeSet<ICallStackSymbol, ICallStackElement, AggregatedCallSite> {

/**
* An empty graph that can be returned when there is no other call graph
* available
*/
public static final CallGraph EMPTY_GRAPH = new CallGraph();

private Set<ICallStackElement> fRootElements = new HashSet<>();
private final Multimap<ICallStackElement, AggregatedCallSite> fCcts = HashMultimap.create();

/**
* Constructor
*/
public CallGraph() {
// Empty
}

/**
* Gets the calling context tree for an element.
*
* The calling context tree is the callgraph data aggregated by keeping the
* context of each call.
*
* @param element
* The element for which to get the calling context tree
* @return The aggregated data for the first level of the callgraph
*/
@SuppressWarnings("null")
public Collection<AggregatedCallSite> getCallingContextTree(ICallStackElement element) {
return fCcts.get(element);
}

/**
* Add an aggregated callsite to a callstack element.
*
* @param dstGroup
* the destination group
* @param callsite
* the callsite to add
*/
public void addAggregatedCallSite(ICallStackElement dstGroup, AggregatedCallSite callsite) {
// Make sure the root element is present
ICallStackElement root = dstGroup;
ICallStackElement parent = dstGroup.getParentElement();
while (parent != null) {
root = parent;
parent = parent.getParentElement();
}
fRootElements.add(root);
// Add the callsite to the appropriate group
Collection<AggregatedCallSite> callsites = fCcts.get(dstGroup);
for (AggregatedCallSite site : callsites) {
if (site.getObject().equals(callsite.getObject())) {
site.merge(callsite);
return;
}
}
fCcts.put(dstGroup, callsite);
}

/**
* Get the root elements containing the call graph data.
*
* @return The root elements of the call graph
*/
@Override
public Collection<ICallStackElement> getElements() {
return ImmutableSet.copyOf(fRootElements);
}

@Override
public Collection<AggregatedCallSite> getTreesFor(Object element) {
if (element instanceof ICallStackElement) {
return getCallingContextTree((ICallStackElement) element);
}
return Collections.emptyList();
}
}
@@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (c) 2017 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/

package org.eclipse.tracecompass.internal.analysis.callstack.core;

import java.util.Collection;

import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.analysis.callstack.core.tree.ITree;
import org.eclipse.tracecompass.internal.analysis.callstack.core.tree.IWeightedTreeGroupDescriptor;

/**
* Interface that classes representing a single element in the callstack
* hierarchy must implement. Typically, a {@link ICallStackElement} will be
* associated with a {@link ICallStackGroupDescriptor}. It will have children
* that will correspond to the next group in the hierarchy.
*
* The actual data of the various available analyses containing those elements
* will be available only at the leaf elements.
*
* @author Geneviève Bastien
*/
public interface ICallStackElement extends ITree {

/**
* Get the elements at the next level of the callstack hierarchy from this
* element
*
* FIXME: Can this method be completely replaced by
* {@link ITree#getChildren()}?
*
* @return The list of children elements in the hierarchy
*/
Collection<ICallStackElement> getChildrenElements();

/**
* Get the corresponding group descriptor
*
* @return The group descriptor of this element
*/
IWeightedTreeGroupDescriptor getGroup();

/**
* Get the next group descriptor
*
* @return The next group descriptor, or <code>null</code> if this is a leaf
* element
*/
@Nullable IWeightedTreeGroupDescriptor getNextGroup();

/**
* Get the key for symbol resolution at a given time
*
* @param time
* The time at which to get the symbol key
* @return The symbol key at time
*/
int getSymbolKeyAt(long time);

/**
* Set the symbol key element to use for this hierarchy
*
* @param element
* The symbol key element
*/
void setSymbolKeyElement(ICallStackElement element);

/**
* Return whether this element is the symbol key element
*
* @return Whether the element is the symbol key
*/
boolean isSymbolKeyElement();

/**
* Get the parent element, or <code>null</code> if this element corresponds
* to the first group of the hierarchy
*
* FIXME: Can this method be completely replaced by
* {@link ITree#getParent()}?
*
* @return The parent element
*/
@Nullable ICallStackElement getParentElement();

/**
* Get whether this element is a leaf element in the callstack hierarchy.
* Leaf elements are expected to contain the proper analysis data.
*
* @return Whether this element is a leaf, i.e. contains analysis data or
* not
*/
boolean isLeaf();
}
@@ -0,0 +1,76 @@
/*******************************************************************************
* Copyright (c) 2016 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/

package org.eclipse.tracecompass.internal.analysis.callstack.core;

import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.analysis.callstack.core.tree.IWeightedTreeGroupDescriptor;

/**
* This interface describes a group in the callstack. A group can either be a
* source group under which other groups are, or a leaf group, under which is
* the actual stack.
*
* Example: Let's take a trace that registers function entry and exit for
* threads and where events also provide information on some other stackable
* application component:
*
* A possible group hierarchy would be the following:
*
* <pre>
* Per PID:
* [pid]
* [tid]
* data
* </pre>
*
* or
*
* <pre>
* With additional component information:
* [pid]
* [application component]
* [tid]
* data
* </pre>
*
* In the first case, there would be 2 groups, and in the second 3 groups. It is
* the analysis's responsibility to implement how to retrieve which group a
* trace event belongs to and add the data to the proper leaf group. These
* groups are indication for the various analyses on how to divide the data and
* some analyses may do some aggregation based on those groups.
*
* To each group will correspond a number of {@link ICallStackElement} that
* represent single elements of this group. In the example above, to the [pid]
* group will correspond each individual process being analyses, eg. process 45,
* 10001, etc.
*
* If the function names happen to be addresses in an executable and the PID is
* the key to map those symbols to actual function names, then the first group
* "[pid]" would be the symbol key group, used to resolve the symbols.
*
* @author Geneviève Bastien
*/
public interface ICallStackGroupDescriptor extends IWeightedTreeGroupDescriptor {

@Override
@Nullable ICallStackGroupDescriptor getNextGroup();

/**
* Get whether the value of this group should be used as the key for the
* symbol provider. For instance, for some callstack, the group
* corresponding to the process ID would be the symbol key group.
*
* @return <code>true</code> if the values of this group are used as the
* symbol mapping key.
*/
boolean isSymbolKeyGroup();
}

0 comments on commit 72a48a5

Please sign in to comment.