diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/META-INF/MANIFEST.MF b/analysis/org.eclipse.tracecompass.analysis.callstack.core/META-INF/MANIFEST.MF index a015861504..b8e53796e1 100644 --- a/analysis/org.eclipse.tracecompass.analysis.callstack.core/META-INF/MANIFEST.MF +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/META-INF/MANIFEST.MF @@ -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 diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/AggregatedCallSite.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/AggregatedCallSite.java new file mode 100644 index 0000000000..b420843021 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/AggregatedCallSite.java @@ -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 { + + /** + * 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 getCallees() { + List list = new ArrayList<>(); + for (WeightedTree 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> getStatistics() { + return ImmutableMap.of(); + } + + @Override + public String toString() { + return "CallSite: " + getObject(); //$NON-NLS-1$ + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/CallGraph.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/CallGraph.java new file mode 100644 index 0000000000..d8e41ac471 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/CallGraph.java @@ -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 { + + /** + * 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 fRootElements = new HashSet<>(); + private final Multimap 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 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 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 getElements() { + return ImmutableSet.copyOf(fRootElements); + } + + @Override + public Collection getTreesFor(Object element) { + if (element instanceof ICallStackElement) { + return getCallingContextTree((ICallStackElement) element); + } + return Collections.emptyList(); + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/ICallStackElement.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/ICallStackElement.java new file mode 100644 index 0000000000..23520e31d3 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/ICallStackElement.java @@ -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 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 null 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 null 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(); +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/ICallStackGroupDescriptor.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/ICallStackGroupDescriptor.java new file mode 100644 index 0000000000..b1dc95c472 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/ICallStackGroupDescriptor.java @@ -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: + * + *
+ *  Per PID:
+ *    [pid]
+ *        [tid]
+ *            data
+ * 
+ * + * or + * + *
+ *  With additional component information:
+ *    [pid]
+ *       [application component]
+ *          [tid]
+ *              data
+ * 
+ * + * 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 true if the values of this group are used as the + * symbol mapping key. + */ + boolean isSymbolKeyGroup(); +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/ICallStackSymbol.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/ICallStackSymbol.java new file mode 100644 index 0000000000..df99d1f217 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/ICallStackSymbol.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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.tracecompass.tmf.core.symbols.ISymbolProvider; + +/** + * @author Geneviève Bastien + */ +public interface ICallStackSymbol { + + /** + * Resolve the current symbol to a string with the providers + * + * @param providers + * The collection of providers available + * @return The resolved symbol + */ + String resolve(Collection providers); +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/DefaultDataPalette.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/DefaultDataPalette.java new file mode 100644 index 0000000000..971441cdb7 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/DefaultDataPalette.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 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.tree; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.tmf.core.dataprovider.X11ColorUtils; +import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle; +import org.eclipse.tracecompass.tmf.core.model.StyleProperties; +import org.eclipse.tracecompass.tmf.core.presentation.IPaletteProvider; +import org.eclipse.tracecompass.tmf.core.presentation.QualitativePaletteProvider; +import org.eclipse.tracecompass.tmf.core.presentation.RGBAColor; + +import com.google.common.collect.ImmutableMap; + +/** + * A default data palette that uses the hashCode of an object to get a color + * palette from a small differentiated palette + * + * @author Geneviève Bastien + */ +public class DefaultDataPalette implements IDataPalette { + + private static final int NUM_COLORS = 12; + + // Map of base styles + private static final Map STYLES; + // Map of styles with the parent + private static final Map STYLE_MAP = Collections.synchronizedMap(new HashMap<>()); + + static { + IPaletteProvider palette = new QualitativePaletteProvider.Builder().setNbColors(NUM_COLORS).build(); + int i = 0; + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + for (RGBAColor color : palette.get()) { + builder.put(String.valueOf(i), new OutputElementStyle(null, ImmutableMap.of( + StyleProperties.STYLE_NAME, String.valueOf(i), + StyleProperties.BACKGROUND_COLOR, X11ColorUtils.toHexColor(color.getRed(), color.getGreen(), color.getBlue()), + StyleProperties.OPACITY, (float) color.getAlpha() / 255))); + i++; + } + STYLES = builder.build(); + } + + private static @Nullable DefaultDataPalette fInstance = null; + + private DefaultDataPalette() { + // Do nothing + } + + /** + * Get the instance of this palette + * + * @return The instance of the palette + */ + public static DefaultDataPalette getInstance() { + DefaultDataPalette instance = fInstance; + if (instance == null) { + instance = new DefaultDataPalette(); + fInstance = instance; + } + return instance; + } + + /** + * Get the map of styles for this palette + * + * @return The styles + */ + @Override + public Map getStyles() { + return STYLES; + } + + /** + * Get the style element for a given object + * + * @param object + * The object to get a style for + * @return The output style for the object + */ + @Override + public OutputElementStyle getStyleFor(Object object) { + return STYLE_MAP.computeIfAbsent(String.valueOf(Math.floorMod(object.hashCode(), NUM_COLORS)), style -> new OutputElementStyle(style)); + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/DepthGroupDescriptor.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/DepthGroupDescriptor.java new file mode 100644 index 0000000000..ae72693b3a --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/DepthGroupDescriptor.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 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.tree; + +import java.util.Objects; + +import org.eclipse.jdt.annotation.Nullable; + +/** + * A group descriptor adapted to {@link ITree} structure for weighted tree + * providers that do not provide group description. + * + * @author Geneviève Bastien + */ +public class DepthGroupDescriptor implements IWeightedTreeGroupDescriptor { + + private final int fDepth; + private @Nullable IWeightedTreeGroupDescriptor fNextGroup; + + /** + * Create a chain of group descriptor with elements up to depth. It will + * return the root group. The chain of next groups will be up to depth. The + * first-depth is 0. + * + * @param depth + * The depth of elements. If there is only one level of elements, + * the depth should be 0. + * @return The root group descriptor + */ + public static IWeightedTreeGroupDescriptor createChainForDepth(int depth) { + DepthGroupDescriptor group = new DepthGroupDescriptor(depth); + DepthGroupDescriptor prevGroup = group; + for (int i = depth - 1; i >= 0; i--) { + group = new DepthGroupDescriptor(i); + group.fNextGroup = prevGroup; + } + return group; + } + + /** + * Constructor + * + * @param depth + * The depth of the tree element to match this group + */ + private DepthGroupDescriptor(int depth) { + fDepth = depth; + } + + @Override + public @Nullable IWeightedTreeGroupDescriptor getNextGroup() { + return fNextGroup; + } + + @Override + public String getName() { + return Messages.GroupDescriptor_Level + ' ' + fDepth; + } + + @Override + public int hashCode() { + return Objects.hashCode(fDepth); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof DepthGroupDescriptor) { + return ((DepthGroupDescriptor) obj).fDepth == fDepth; + } + return false; + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IDataPalette.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IDataPalette.java new file mode 100644 index 0000000000..005beda285 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IDataPalette.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 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.tree; + +import java.util.Map; + +import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle; + +/** + * An interface for data palette that describe return the list of styles and get + * the specific style for an object + * + * @author Geneviève Bastien + */ +public interface IDataPalette { + + /** + * Get the style for an object. The returned style should not be one of the + * original style returned by the {@link #getStyles()} method, but a style + * object with the name of the base style as parent and a possibly empty + * map. + * + * @param object + * The object for which to get the style + * @return The style for the object + */ + OutputElementStyle getStyleFor(Object object); + + /** + * Get the map of all styles provided by this palette. These are the base + * styles, mapping to the key for each style. Styles for object can then + * refer to those base styles as parents. + * + * @return The map of style name to full style description. + */ + Map getStyles(); +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/ITree.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/ITree.java new file mode 100644 index 0000000000..6640409504 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/ITree.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 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.tree; + +import java.util.Collection; + +import org.eclipse.jdt.annotation.Nullable; + +/** + * A basic interface for a tree structure, ie hierarchical data where each node + * can be linked to a specific object. + * + * @author Geneviève Bastien + */ +public interface ITree { + + /** + * Get the name of this tree element, it should be human-readable as it will + * be displayed to the user + * + * @return The name of this tree element + */ + String getName(); + + /** + * Get the parent of this tree element + * + * @return The parent of this object + */ + @Nullable ITree getParent(); + + /** + * Get the children of this object + * + * @return A collection of children elements + */ + Collection getChildren(); + + /** + * Add a child to this tree object. This method should make sure to set the + * parent of the child to the current object + * + * @param child + * The child object + */ + void addChild(ITree child); + + /** + * Set the parent of this tree object + * + * @param parent + * The parent of the object, it can be null if there + * is no parent + */ + void setParent(@Nullable ITree parent); + + /** + * Create a new element that is a copy of the current object. This copy + * should copy only the object's data, not the hierarchy as callers of this + * method may want to create a new hierarchy for those elements. + * + * @return a new element, copy of the current element + */ + ITree copyElement(); + + /** + * Get the depth of this object, recursively with its children. An object + * with no children would have a depth of 1. + * + * @param tree + * The object for which to get the depth + * @return The depth + */ + static int getDepth(ITree tree) { + Collection children = tree.getChildren(); + if (children.isEmpty()) { + return 1; + } + int depth = 1; + for (ITree child : children) { + depth = Math.max(depth, getDepth(child) + 1); + } + return depth; + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IWeightedTreeGroupDescriptor.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IWeightedTreeGroupDescriptor.java new file mode 100644 index 0000000000..9d59d80026 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IWeightedTreeGroupDescriptor.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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.tree; + +import org.eclipse.jdt.annotation.Nullable; + +/** + * This interface describes a group for elements in a weighted tree structure. + * If the elements in the {@link IWeightedTreeSet} implement the {@link ITree} + * interface, then the {@link IWeightedTreeProvider} can provide group + * descriptors to describe each level of elements. + * + * Example: If the trees represent a callstack for threads that can be grouped + * and the trace also provide some other stackable application component, + * grouping of elements can be done in different ways: + * + * A possible group hierarchy would be the following: + * + *
+ *  Per PID:
+ *    [pid]
+ *        [tid]
+ *            data
+ * 
+ * + * or + * + *
+ *  With additional component information:
+ *    [pid]
+ *       [application component]
+ *          data
+ *          [tid]
+ *              data
+ * 
+ * + * In the first case, there would be 2 groups, and in the second 3 groups. This + * allows to give human-readable names to groups that are otherwise simply + * levels in a tree hierarchy. + * + * @author Geneviève Bastien + */ +public interface IWeightedTreeGroupDescriptor { + + /** + * Get the group descriptor at the next level. + * + * @return The next group or null if this is a leaf level + */ + @Nullable IWeightedTreeGroupDescriptor getNextGroup(); + + /** + * Get the human-readable name for this group descriptor + * + * @return The name of this group descriptor + */ + String getName(); +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IWeightedTreeProvider.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IWeightedTreeProvider.java new file mode 100644 index 0000000000..fe21ecc6cb --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IWeightedTreeProvider.java @@ -0,0 +1,377 @@ +/******************************************************************************* + * Copyright (c) 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.tree; + +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.analysis.timing.core.statistics.IStatistics; +import org.eclipse.tracecompass.common.core.format.DataSizeWithUnitFormat; +import org.eclipse.tracecompass.common.core.format.DataSpeedWithUnitFormat; +import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat; +import org.eclipse.tracecompass.common.core.format.SubSecondTimeWithUnitFormat; +import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; + +/** + * An interface that classes and analyses providing weighted trees can + * implement. This interface allows to add extra information about the specific + * trees that it provides. + * + * The trees are associated with elements that are used to group them. The + * elements can implement the {@link ITree} class if there is a hierarchy in the + * groupings. + * + * @author Geneviève Bastien + * @param + * The type of objects represented by each node in the tree + * @param + * The type of elements used to group the trees. If this type extends + * {@link ITree}, then the elements and their associated weighted + * trees will be grouped in a hierarchical style + * @param + * The type of the tree provided + */ +public interface IWeightedTreeProvider<@NonNull N, E, @NonNull T extends WeightedTree> { + + /** + * The type of data that a value represents. Mostly for numeric value, as + * the data type will help decide how to format the data to be displayed to + * the user + */ + public enum DataType { + /** + * Data represent a decimal number + */ + NUMBER(new DecimalUnitFormat()), + /** + * Data represent a time in nanoseconds, can be negative + */ + NANOSECONDS(SubSecondTimeWithUnitFormat.getInstance()), + /** + * Data represent a binary size, in bytes + */ + BYTES(DataSizeWithUnitFormat.getInstance()), + /** + * Data represent a binary speed, in bytes/second + */ + BINARY_SPEED(DataSpeedWithUnitFormat.getInstance()), + /** + * Any other type of data. Metric that use this data type may use + * additional formatter. + */ + OTHER(new Format() { + private static final long serialVersionUID = 1L; + + @Override + public StringBuffer format(@Nullable Object obj, @Nullable StringBuffer toAppendTo, @Nullable FieldPosition pos) { + if (toAppendTo == null) { + return new StringBuffer(String.valueOf(obj)); + } + return Objects.requireNonNull(toAppendTo.append(String.valueOf(obj))); + } + + @Override + public @Nullable Object parseObject(@Nullable String source, @Nullable ParsePosition pos) { + return null; + } + }); + + private Format fFormatter; + + DataType(Format formatter) { + fFormatter = formatter; + } + + /** + * Formats an object according to the specified formatter + * + * @param object + * The object to format + * @return The formatted string + */ + public String format(Object object) { + return String.valueOf(fFormatter.format(object)); + } + } + + /** + * This class associate a title to a data type for tree metrics + */ + class MetricType { + private final String fTitle; + private final DataType fDataType; + private final @Nullable Format fFormatter; + private final boolean fHasStatistics; + + /** + * Constructor + * + * @param title + * The title of this metric (a string meant for end users) + * @param dataType + * The type of data this metric represent + * @param format + * The formatter for this metric. If null, + * formatting will use the {@link DataType}'s default + * formatter + */ + public MetricType(String title, DataType dataType, @Nullable Format format) { + this(title, dataType, format, false); + } + + /** + * Constructor + * + * @param title + * The title of this metric (a string meant for end users) + * @param dataType + * The type of data this metric represent + * @param format + * The formatter for this metric. If null, + * formatting will use the {@link DataType}'s default + * formatter + * @param hasStatistics + * Whether this metric has statistics provided with it + */ + public MetricType(String title, DataType dataType, @Nullable Format format, boolean hasStatistics) { + fTitle = title; + fDataType = dataType; + fFormatter = format; + fHasStatistics = hasStatistics; + } + + /** + * Get the title of this metric, for the user + * + * @return The title + */ + public String getTitle() { + return fTitle; + } + + /** + * Get the type of data of this metric + * + * @return The data type of the metric + */ + public DataType getDataType() { + return fDataType; + } + + /** + * Formats an object for this metric + * + * @param obj + * The object to format + * @return The formatted string + */ + public String format(Object obj) { + if (fFormatter != null) { + return Objects.requireNonNull(fFormatter.format(obj)); + } + return fDataType.format(obj); + } + + /** + * Return whether this metric has statistics computed with it. If so, + * then calling + * {@link IWeightedTreeProvider#getStatistics(WeightedTree, int)} on + * this metric's index should return a statistics object for a tree. + * + * @return Whether this metric has statistics computed for it + */ + public boolean hasStatistics() { + return fHasStatistics; + } + } + + /** + * The default metric type for the tree's weight + */ + MetricType WEIGHT_TYPE = new MetricType("Weight", DataType.NUMBER, null); //$NON-NLS-1$ + + /** + * Get a weighted tree set for a time selection. It should be a subset of + * the complete tree, ie the elements, and weights of the weighted trees + * should be included in full tree, but its range should cover only the + * requested time range. If this provider does not support selection range, + * null should be returned. + * + * @param start + * The timestamp of the start of the range + * @param end + * The timestamp of the end of the range + * @return A weighted tree set that spans the selected range, or + * null if range is not supported. + */ + default @Nullable IWeightedTreeSet getSelection(ITmfTimestamp start, ITmfTimestamp end) { + return null; + } + + /** + * Get the complete tree set provided by this object. + * + * @return The complete weighted tree set + */ + IWeightedTreeSet getTreeSet(); + + /** + * Get the metric type for the weight value. The default metric is called + * "Weight" and is a number + * + * @return The metric type for the weight value. + */ + default MetricType getWeightType() { + return WEIGHT_TYPE; + } + + /** + * Get a list of additional metrics that are provided by this tree. + * + * @return A list of metrics provided by the trees, in addition to the + * weight + */ + default List getAdditionalMetrics() { + return Collections.emptyList(); + } + + /** + * Get an additional metric for a tree. The metric index corresponds to the + * position of the desired metric in the list of metric returned by the + * {@link #getAdditionalMetrics()} method and the return value should be of + * the proper data type + * + * @param object + * The tree object for which to get the metric + * @param metricIndex + * The index in the list of the metric metric to get + * @return The value of the metric for the tree in parameter + */ + default Object getAdditionalMetric(T object, int metricIndex) { + throw new UnsupportedOperationException("If the tree provider has metric, it should implement this method, or it should not be called"); //$NON-NLS-1$ + } + + /** + * Get the statistics for a metric. The metric index corresponds to the + * position of the desired metric in the list of metric returned by the + * {@link #getAdditionalMetrics()} method. If the index {@literal <} 0, then + * the metric is the main weight. + * + * @param object + * The weighted tree object for which to get the metric + * @param metricIndex + * The index in the list of the metric metric to get. If + * {@literal <} 0, then the metric is the main weight + * @return The statistics for the metric of null if there are + * no statistics for this metric. + */ + default @Nullable IStatistics getStatistics(T object, int metricIndex) { + if (metricIndex < 0) { + if (getWeightType().hasStatistics()) { + return object.getStatistics(metricIndex); + } + return null; + } + List metrics = getAdditionalMetrics(); + if (metricIndex >= metrics.size()) { + return null; + } + MetricType metricType = metrics.get(metricIndex); + if (!metricType.hasStatistics()) { + return null; + } + return object.getStatistics(metricIndex); + } + + /** + * Return a list of additional data sets' titles. These sets will be + * available by calling {@link WeightedTree#getExtraDataTrees(int)} on the + * trees, where the index in the list is the parameter that the children set + * should match + * + * @return The title of each child set + */ + default List getExtraDataSets() { + return Collections.emptyList(); + } + + /** + * Get a user-facing text to identify a tree object. By default, it is the + * string representation of the object. + * + * @param tree + * The tree whose value to display + * @return A user-facing string to identify this node + */ + default String toDisplayString(T tree) { + return String.valueOf(tree.getObject()); + } + + /** + * A title for this tree provider. This title will be visible by users and + * should describe what this tree provider's data represent. + * + * @return The title of this provider + */ + String getTitle(); + + /** + * Get the group descriptors that describe the hierarchical groups of + * elements. + * + * This method returns null if the elements are not + * {@link ITree} instances. If the elements implement the {@link ITree} + * interface, the implementations may override this method and return a + * group descriptor that gives names to each level of the hierarchy of + * elements. Otherwise, it returns a group for the root element, whose next + * groups will match the depth of the {@link ITree} structure. + * + * @return The collection of group descriptors for this call graph, or + * null if there is no hierarchy of elements + */ + default @Nullable IWeightedTreeGroupDescriptor getGroupDescriptor() { + IWeightedTreeSet<@NonNull N, E, @NonNull T> treeSet = getTreeSet(); + + Collection elements = treeSet.getElements(); + int lvl = 0; + for (E element : elements) { + if (element instanceof ITree) { + lvl = Math.max(lvl, ITree.getDepth((ITree) element)); + } + } + + // No tree level, default value is null + if (lvl == 0) { + return null; + } + + return DepthGroupDescriptor.createChainForDepth(lvl - 1); + } + + /** + * Weighted tree providers can provide a palette of styles for the data + * represented. By default, it uses a default palette of a few qualitative + * colors that will use the hash code of objects to assign a style + * + * @return The palette for this data provider + */ + default IDataPalette getPalette() { + return DefaultDataPalette.getInstance(); + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IWeightedTreeSet.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IWeightedTreeSet.java new file mode 100644 index 0000000000..1609de70bc --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/IWeightedTreeSet.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 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.tree; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * A structure that keeps elements and their weighted trees together for a given + * time range or grouping. It is the class that contains the actual data that + * the {@link IWeightedTreeProvider} provides. + * + * The tree set contains the data, but a {@link IWeightedTreeProvider} object is + * necessary to describe it, its format, their names, etc. + * + * @author Geneviève Bastien + * @param + * The type of objects represented by each node in the tree + * @param + * The type of elements used to group the trees. If this type extends + * {@link ITree}, then the elements and their associated weighted + * trees will be grouped in a hierarchical style + * @param + * The type of the tree provided + */ +public interface IWeightedTreeSet<@NonNull N, E, @NonNull T extends WeightedTree> { + + /** + * Get the elements under which are the weighted trees. It can be a single + * constant element if this treeset does not have the concept of grouping + * the trees. + * + * @return The elements used to group the trees + */ + Collection getElements(); + + /** + * Get the weighted trees for a given element + * + * @param element + * The element for which to get the trees + * @return A collection of weighted trees for the requested element + */ + Collection getTreesFor(Object element); + + /** + * Return a list of additional data sets' titles. These sets will be + * available by calling {@link WeightedTree#getExtraDataTrees(int)} on the + * trees, where the index in the list is the parameter that the children set + * should match + * + * @return The title of each child set + */ + default List getExtraDataSets() { + return Collections.emptyList(); + } + + /** + * Get the trees for an element with the given string representation. If + * many names are entered, then it is assumed the elements should be + * {@link ITree}s and the hierarchy is followed + * + * @param elementNames + * The name(s) of the element to get the trees for. If multiple + * names are given, then the elements are expected to have a + * hierarchical relation + * @return The trees for the given element. If no element with that name is + * found, an empty collection will be returned + */ + @SuppressWarnings("null") + default Collection getTreesForNamed(String... elementNames) { + Collection elements = getElements(); + for (int i = 0; i < elementNames.length; i++) { + String elementName = elementNames[i]; + for (Object element : elements) { + if (String.valueOf(element).equals(elementName)) { + // Found the element at this level. Is this the last? + if (i == elementNames.length - 1) { + return getTreesFor(element); + } + if (element instanceof ITree) { + elements = ((ITree) element).getChildren(); + break; + } + } + } + } + return Collections.emptyList(); + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/Messages.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/Messages.java new file mode 100644 index 0000000000..0fe947d019 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/Messages.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 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.tree; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.osgi.util.NLS; + +/** + * Messages for the weighted tree classes + * + * @author Geneviève Bastien + */ +public class Messages extends NLS { + private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$ + + /** Name of the level group descriptor */ + public static @Nullable String GroupDescriptor_Level; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/WeightedTree.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/WeightedTree.java new file mode 100644 index 0000000000..d07dbb5754 --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/WeightedTree.java @@ -0,0 +1,286 @@ +/******************************************************************************* + * Copyright (c) 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.tree; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.analysis.timing.core.statistics.IStatistics; + +/** + * A Weighted Tree class to describe hierarchical data with a weight. This class + * is a concrete class to describe a simple weighted tree, but it is also meant + * to be extended to support other metrics associated with each tree, apart from + * the weight. + * + * Note that the weight is such that the sum of the weight of the children is + * smaller or equal to the weight of the parent. Failure to comply to this will + * result in undefined behaviors when viewing the results. + * + * Also, if a child is added to the weighted tree for an object that is already + * present in the children of this tree, their data will be merged. + * + * @author Geneviève Bastien + * @param + * The type of objects in this tree + */ +public class WeightedTree<@NonNull T> implements Comparable> { + + private final T fObject; + private final Map> fChildren = new HashMap<>(); + private @Nullable WeightedTree fParent; + private long fWeight = 0; + + /** + * Constructor + * + * @param object + * The object that goes with this tree. + */ + public WeightedTree(T object) { + this(object, 0); + } + + /** + * Constructor + * + * @param object + * The object that goes with this tree. + * @param initialWeight + * The initial length of this object + */ + public WeightedTree(T object, long initialWeight) { + fObject = object; + fParent = null; + fWeight = initialWeight; + } + + /** + * Copy constructor + * + * @param copy + * The tree to copy + */ + protected WeightedTree(WeightedTree copy) { + fObject = copy.fObject; + for (WeightedTree entry : copy.fChildren.values()) { + fChildren.put(entry.getObject(), entry.copyOf()); + } + fParent = copy.fParent; + fWeight = copy.fWeight; + } + + /** + * Get the weight of this tree. The unit of this weight will depend on the + * metric it represents. + * + * @return The weight of this tree + */ + public long getWeight() { + return fWeight; + } + + /** + * Make a copy of this tree, with its statistics. Implementing classes + * should make sure they copy all fields of the tree, including the + * statistics. + * + * This constructor recursively copies all the children. + * + * @return A copy of this weighted tree + */ + public WeightedTree copyOf() { + return new WeightedTree<>(this); + } + + /** + * Get the object associated with this tree + * + * @return The object for this tree + */ + public T getObject() { + return fObject; + } + + /** + * Get the parent of this tree + * + * @return The parent of this tree + */ + protected @Nullable WeightedTree getParent() { + return fParent; + } + + /** + * Sets the parent of this tree + * + * @param parent + * The parent tree + */ + protected void setParent(WeightedTree parent) { + fParent = parent; + } + + /** + * Get the children of this tree + * + * @return A collection of children trees + */ + public Collection> getChildren() { + return fChildren.values(); + } + + /** + * Add value to the weight of this tree + * + * @param weight + * the amount to add to the length + */ + public void addToWeight(long weight) { + fWeight += weight; + } + + /** + * Add a child to this tree. If a child for the same object already exists, + * the data for both children will be merged. + * + * @param child + * the child tree to add + */ + public void addChild(WeightedTree child) { + WeightedTree childTree = fChildren.get(child.getObject()); + if (childTree == null) { + child.setParent(this); + fChildren.put(child.getObject(), child); + return; + } + childTree.merge(child); + } + + /** + * Merge a tree's data with this one. This method will modify the current + * tree. + * + * It will first call {@link #mergeData(WeightedTree)} that needs to be + * implemented for each implementation of this class. + * + * It will then merge the children of both trees by adding the other's + * children to this one. + * + * @param other + * The tree to merge. It has to have the same object as the + * current tree otherwise it will throw an + * {@link IllegalArgumentException} + */ + public final void merge(WeightedTree other) { + if (!other.getObject().equals(getObject())) { + throw new IllegalArgumentException("AggregatedStackTraces: trying to merge stack traces of different symbols"); //$NON-NLS-1$ + } + fWeight += other.fWeight; + mergeData(other); + mergeChildren(other); + } + + /** + * Merge the data of two trees. This should modify the current tree's + * specific data. It is called by {@link #merge(WeightedTree)} and this + * method MUST NOT touch the children of the tree. + * + * @param other + * The tree to merge to this one + */ + protected void mergeData(WeightedTree other) { + // Nothing to do in main class + } + + /** + * Get the statistics for a metric at index. If the index {@literal <} 0, + * then the metric is the main weight. + * + * @param metricIndex + * The index in the list of the metric metric to get. If + * {@literal <} 0, then the metric is the weight. + * @return The statistics for the metric or null if not + * available + */ + public @Nullable IStatistics getStatistics(int metricIndex) { + return null; + } + + /** + * Merge the children trees + * + * @param other + * The tree to merge to this one + */ + private void mergeChildren(WeightedTree other) { + for (WeightedTree otherChildSite : other.fChildren.values()) { + T childObject = otherChildSite.getObject(); + WeightedTree childSite = fChildren.get(childObject); + if (childSite == null) { + fChildren.put(childObject, otherChildSite.copyOf()); + } else { + // combine children + childSite.merge(otherChildSite); + } + } + } + + /** + * Get the maximum depth under and including this tree. A depth of 1 means + * there is one element under and including this element. + * + * @return The maximum depth under and including this tree. The minimal + * value for the depth is 1. + */ + public int getMaxDepth() { + int maxDepth = 0; + for (WeightedTree child : getChildren()) { + maxDepth = Math.max(maxDepth, child.getMaxDepth()); + } + return maxDepth + 1; + } + + /** + * Get other children of this tree that are not its direct descendants. It + * can be used for instance to represent extra data, for example kernel + * statuses for a callstack. + * + * A {@link IWeightedTreeProvider} will advertise those potential children + * data that come with this tree, and consumers can then call this method + * with the index of this extra type, if the tree has more than one extra + * data set + * + * @param index + * The index of this extra children set, as provided by the + * {@link IWeightedTreeProvider#getExtraDataSets()} method. + * + * @return The extra children trees + */ + public Collection> getExtraDataTrees(int index) { + return Collections.emptyList(); + } + + @Override + public String toString() { + return "[" + fObject + "]: " + fWeight; //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + public int compareTo(WeightedTree<@NonNull T> o) { + return Long.compare(fWeight, o.fWeight); + } +} diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/messages.properties b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/messages.properties new file mode 100644 index 0000000000..5c9932b3cb --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/messages.properties @@ -0,0 +1,12 @@ +############################################################################### +# Copyright (c) 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 +############################################################################### + +GroupDescriptor_Level=Level diff --git a/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/package-info.java b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/package-info.java new file mode 100644 index 0000000000..f878e29f2d --- /dev/null +++ b/analysis/org.eclipse.tracecompass.analysis.callstack.core/src/org/eclipse/tracecompass/internal/analysis/callstack/core/tree/package-info.java @@ -0,0 +1,12 @@ +/******************************************************************************* + * Copyright (c) 2023 Ericsson + * + * 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 + *******************************************************************************/ + +@org.eclipse.jdt.annotation.NonNullByDefault +package org.eclipse.tracecompass.internal.analysis.callstack.core.tree;