Skip to content

Commit a40472f

Browse files
author
Siwei Zhang
committed
tmf: Add bar chart data provider type with feature to fetch tree
This commit introduces a new data provider type to support "bar chart" views. It focuses on implementing the feature to fetch tree. CallStackAnalysis is used as the initial example input to demonstrate this functionality through a function density view. [Added] A new data provider type called ITmfBarChartDataProvider, only the feature to fetch tree is implemented in this commit. [Added] CallStackBarChartDataProvider, showcasing a function density view as an example implementation. Signed-off-by: Siwei Zhang <siwei.zhang@ericsson.com>
1 parent 80179eb commit a40472f

17 files changed

Lines changed: 852 additions & 8 deletions

File tree

analysis/org.eclipse.tracecompass.analysis.profiling.core/plugin.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
class="org.eclipse.tracecompass.internal.analysis.profiling.core.instrumented.FlameChartDataProviderFactory"
2020
id="org.eclipse.tracecompass.analysis.profiling.core.flamechart">
2121
</dataProviderFactory>
22+
<dataProviderFactory
23+
class="org.eclipse.tracecompass.internal.analysis.profiling.core.callstack.provider.CallStackBarChartDataProviderFactory"
24+
id="org.eclipse.tracecompass.analysis.profiling.core.callstack.barchart.provider">
25+
</dataProviderFactory>
2226
</extension>
2327
<extension
2428
point="org.eclipse.linuxtools.tmf.core.analysis">

analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2018 École Polytechnique de Montréal
2+
* Copyright (c) 2025 École Polytechnique de Montréal
33
*
44
* All rights reserved. This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**********************************************************************
2+
* Copyright (c) 2025 Ericsson
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License 2.0 which
6+
* accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
**********************************************************************/
11+
package org.eclipse.tracecompass.internal.analysis.profiling.core.callstack.provider;
12+
13+
import java.util.ArrayList;
14+
import java.util.Collections;
15+
import java.util.HashMap;
16+
import java.util.List;
17+
import java.util.Map;
18+
19+
import org.eclipse.core.runtime.IProgressMonitor;
20+
import org.eclipse.jdt.annotation.NonNull;
21+
import org.eclipse.jdt.annotation.Nullable;
22+
import org.eclipse.tracecompass.analysis.profiling.core.callstack.CallStackAnalysis;
23+
import org.eclipse.tracecompass.analysis.profiling.core.callstack.CallStackSeries;
24+
import org.eclipse.tracecompass.common.core.NonNullUtils;
25+
import org.eclipse.tracecompass.segmentstore.core.ISegment;
26+
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
27+
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
28+
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
29+
import org.eclipse.tracecompass.tmf.core.dataprovider.DataType;
30+
import org.eclipse.tracecompass.tmf.core.model.barchart.AbstractTreeBarChartDataProvider;
31+
import org.eclipse.tracecompass.tmf.core.model.barchart.AxisDomain;
32+
import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
33+
import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
34+
import org.eclipse.tracecompass.tmf.core.model.xy.TmfXYAxisDescription;
35+
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
36+
37+
/**
38+
* Bar chart provider to show function duration distributions in the call stack.
39+
*
40+
* @author Siwei Zhang
41+
* @since 2.6
42+
*/
43+
public class CallStackBarChartDataProvider extends AbstractTreeBarChartDataProvider<@NonNull CallStackAnalysis, @NonNull ITmfTreeDataModel> {
44+
45+
/**
46+
* Provider id.
47+
*/
48+
public static final String ID = "org.eclipse.tracecompass.analysis.profiling.core.callstack.barchart.provider"; //$NON-NLS-1$
49+
50+
private static final String EXECUTION_TIME = "Execution Time"; //$NON-NLS-1$
51+
private static final String NUMBER_OF_EXECUTIONS = "Number of Executions"; //$NON-NLS-1$
52+
private static final String UNIT_NS = "ns"; //$NON-NLS-1$
53+
private static final String UNIT_EMPTY = ""; //$NON-NLS-1$
54+
private final Map<Integer, Long> fPidsToEntryIds = new HashMap<>();
55+
private static final int UNKNOWN_PID = -1;
56+
57+
private CallStackAnalysis fModule;
58+
59+
/**
60+
* Constructor.
61+
*
62+
* @param trace
63+
* The trace associated with this data provider
64+
* @param analysisModule
65+
* The analysis module used to compute data
66+
*/
67+
public CallStackBarChartDataProvider(@NonNull ITmfTrace trace, @NonNull CallStackAnalysis analysisModule) {
68+
super(trace, analysisModule);
69+
fModule = analysisModule;
70+
}
71+
72+
@Override
73+
public TmfXYAxisDescription getXAxisDescription() {
74+
ITmfStateSystem ss = fModule.getStateSystem();
75+
if (ss == null) {
76+
return createUnknownDurationAxis();
77+
}
78+
79+
CallStackSeries callstackSeries = getAnalysisModule().getCallStackSeries();
80+
if (callstackSeries == null) {
81+
return createUnknownDurationAxis();
82+
}
83+
84+
long minDuration = Long.MAX_VALUE;
85+
long maxDuration = 0;
86+
87+
for (ISegment segment : callstackSeries.getIntersectingElements(ss.getStartTime(), ss.getCurrentEndTime())) {
88+
long length = segment.getLength();
89+
minDuration = Math.min(minDuration, length);
90+
maxDuration = Math.max(maxDuration, length);
91+
}
92+
93+
if (minDuration == Long.MAX_VALUE) {
94+
// No segments found, return unknown range
95+
return createUnknownDurationAxis();
96+
}
97+
98+
return new TmfXYAxisDescription(
99+
EXECUTION_TIME, UNIT_NS, DataType.DURATION, new AxisDomain.Range(minDuration, maxDuration));
100+
}
101+
102+
private static @NonNull TmfXYAxisDescription createUnknownDurationAxis() {
103+
return new TmfXYAxisDescription(
104+
EXECUTION_TIME, UNIT_NS, DataType.DURATION, new AxisDomain.Range(-1, -1));
105+
}
106+
107+
@Override
108+
public TmfXYAxisDescription getYAxisDescription() {
109+
return new TmfXYAxisDescription(
110+
NUMBER_OF_EXECUTIONS, UNIT_EMPTY, DataType.NUMBER);
111+
}
112+
113+
@Override
114+
public String getId() {
115+
return ID;
116+
}
117+
118+
@Override
119+
protected boolean isCacheable() {
120+
return false;
121+
}
122+
123+
@Override
124+
protected @NonNull List<@NonNull ITmfTreeDataModel> getEntries(@NonNull ITmfStateSystem ss, @NonNull Map<@NonNull String, @NonNull Object> fetchParameters, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
125+
CallStackSeries series = fModule.getCallStackSeries();
126+
if (series == null) {
127+
return Collections.EMPTY_LIST;
128+
}
129+
130+
List<@NonNull ITmfTreeDataModel> entries = new ArrayList<>();
131+
long traceId = getId(ITmfStateSystem.ROOT_ATTRIBUTE);
132+
entries.add(new TmfTreeDataModel(traceId, -1, NonNullUtils.nullToEmptyString(getTrace().getName())));
133+
134+
List<@NonNull Integer> processQuarks = ss.getQuarks(getAnalysisModule().getProcessesPattern());
135+
long end = ss.getCurrentEndTime();
136+
List<@NonNull ITmfStateInterval> fullEnd = ss.queryFullState(end);
137+
for(@NonNull Integer processQuark : processQuarks) {
138+
int pid = UNKNOWN_PID;
139+
if (processQuark != ITmfStateSystem.ROOT_ATTRIBUTE) {
140+
String processName = ss.getAttributeName(processQuark);
141+
Object processValue = fullEnd.get(processQuark).getValue();
142+
pid = getThreadProcessId(processName, processValue);
143+
}
144+
long entryId = getId(processQuark);
145+
fPidsToEntryIds.put(pid, entryId);
146+
entries.add(new TmfTreeDataModel(entryId, traceId, getNameFromPID(pid)));
147+
}
148+
return entries;
149+
}
150+
151+
private static @NonNull String getNameFromPID(int pid) {
152+
return pid == UNKNOWN_PID ? "UNKNOWN_PID" : String.valueOf(pid); //$NON-NLS-1$
153+
}
154+
155+
private static int getThreadProcessId(String name, @Nullable Object value) {
156+
if (value instanceof Number) {
157+
return ((Number) value).intValue();
158+
}
159+
try {
160+
return Integer.parseInt(name);
161+
} catch (NumberFormatException e) {
162+
return UNKNOWN_PID;
163+
}
164+
}
165+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**********************************************************************
2+
* Copyright (c) 2025 Ericsson
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License 2.0 which
6+
* accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
**********************************************************************/
11+
package org.eclipse.tracecompass.internal.analysis.profiling.core.callstack.provider;
12+
13+
import java.util.ArrayList;
14+
import java.util.Collection;
15+
import java.util.Collections;
16+
import java.util.Iterator;
17+
import java.util.List;
18+
import java.util.Objects;
19+
20+
import org.eclipse.jdt.annotation.NonNull;
21+
import org.eclipse.jdt.annotation.Nullable;
22+
import org.eclipse.tracecompass.analysis.profiling.core.callstack.CallStackAnalysis;
23+
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
24+
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor.ProviderType;
25+
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
26+
import org.eclipse.tracecompass.tmf.core.model.DataProviderDescriptor;
27+
import org.eclipse.tracecompass.tmf.core.model.barchart.TmfTreeBarChartCompositeDataProvider;
28+
import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
29+
import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
30+
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
31+
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
32+
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
33+
import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
34+
35+
import com.google.common.collect.Iterables;
36+
37+
/**
38+
* {@link CallStackBarChartDataProvider} factory.
39+
*
40+
* @author Siwei Zhang
41+
* @since 2.6
42+
*/
43+
public class CallStackBarChartDataProviderFactory implements IDataProviderFactory {
44+
45+
private static final IDataProviderDescriptor DESCRIPTOR = new DataProviderDescriptor.Builder()
46+
.setId(CallStackBarChartDataProvider.ID)
47+
.setName(Objects.requireNonNull(Messages.CallStackBarChartDataProviderFactory_title))
48+
.setDescription(Objects.requireNonNull(Messages.CallStackBarChartDataProviderFactory_descriptionText))
49+
.setProviderType(ProviderType.BAR_CHART)
50+
.build();
51+
52+
@Override
53+
public @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
54+
if (trace instanceof TmfExperiment) {
55+
@NonNull List<@NonNull CallStackBarChartDataProvider> providers = new ArrayList<>();
56+
for (ITmfTrace child : TmfTraceManager.getTraceSet(trace)) {
57+
CallStackBarChartDataProvider provider = createProviderLocal(child);
58+
if (provider != null) {
59+
providers.add(provider);
60+
}
61+
}
62+
if (providers.size() == 1) {
63+
return providers.get(0);
64+
}
65+
if (!providers.isEmpty()) {
66+
return new TmfTreeBarChartCompositeDataProvider<>(providers, CallStackBarChartDataProvider.ID);
67+
}
68+
return null;
69+
}
70+
return createProviderLocal(trace);
71+
}
72+
73+
private static @Nullable CallStackBarChartDataProvider createProviderLocal(@NonNull ITmfTrace trace) {
74+
Iterator<CallStackAnalysis> modules = TmfTraceUtils.getAnalysisModulesOfClass(trace, CallStackAnalysis.class).iterator();
75+
while (modules.hasNext()) {
76+
CallStackAnalysis first = modules.next();
77+
first.schedule();
78+
return new CallStackBarChartDataProvider(trace, first);
79+
}
80+
return null;
81+
}
82+
83+
@Override
84+
public Collection<IDataProviderDescriptor> getDescriptors(@NonNull ITmfTrace trace) {
85+
Iterable<@NonNull CallStackAnalysis> modules = TmfTraceUtils.getAnalysisModulesOfClass(trace, CallStackAnalysis.class);
86+
return !Iterables.isEmpty(modules) ? Collections.singletonList(DESCRIPTOR) : Collections.emptyList();
87+
}
88+
}

analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/Messages.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2013, 2014 Ericsson
2+
* Copyright (c) 2013, 2025 Ericsson
33
*
44
* All rights reserved. This program and the accompanying materials are
55
* made available under the terms of the Eclipse Public License 2.0 which
@@ -48,6 +48,15 @@ public class Messages extends NLS {
4848
*/
4949
public static @Nullable String CallStackDataProviderFactory_descriptionText;
5050

51+
/**
52+
* Name of the data provider shown to the user for bar chart
53+
*/
54+
public static @Nullable String CallStackBarChartDataProviderFactory_title;
55+
/**
56+
* Help text for the data descriptor for bar chart
57+
*/
58+
public static @Nullable String CallStackBarChartDataProviderFactory_descriptionText;
59+
5160
static {
5261
// initialize resource bundle
5362
NLS.initializeMessages(BUNDLE_NAME, Messages.class);

analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/messages.properties

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
###############################################################################
2-
# Copyright (c) 2013, 2014 Ericsson
2+
# Copyright (c) 2013, 2025 Ericsson
33
#
44
# All rights reserved. This program and the accompanying materials
55
# are made available under the terms of the Eclipse Public License 2.0
@@ -17,4 +17,6 @@ CallStackStateProvider_IncoherentCallstack=Incoherent callstack found on ({0}) o
1717
CallStackDataProvider_toolTipAddress=Address
1818
CallStackDataProvider_toolTipState=State
1919
CallStackDataProviderFactory_title=Flame Chart
20-
CallStackDataProviderFactory_descriptionText=Show a call stack over time
20+
CallStackDataProviderFactory_descriptionText=Show a call stack over time
21+
CallStackBarChartDataProviderFactory_title=Function Density View
22+
CallStackBarChartDataProviderFactory_descriptionText=Shows the distribution of function execution time

releng/org.eclipse.tracecompass.integration.core.tests/src/org/eclipse/tracecompass/integration/core/tests/dataproviders/DataProviderManagerTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,12 @@ public class DataProviderManagerTest {
273273
.setId("org.eclipse.tracecompass.internal.analysis.profiling.callstack.provider.CallStackDataProvider");
274274
EXPECTED_UST_DP_DESCRIPTORS.add(builder.build());
275275
builder = new DataProviderDescriptor.Builder();
276+
builder.setName("Function Density View")
277+
.setDescription("Shows the distribution of function execution time")
278+
.setProviderType(ProviderType.BAR_CHART)
279+
.setId("org.eclipse.tracecompass.analysis.profiling.core.callstack.barchart.provider");
280+
EXPECTED_UST_DP_DESCRIPTORS.add(builder.build());
281+
builder = new DataProviderDescriptor.Builder();
276282
builder.setName("Function Duration Statistics")
277283
.setDescription("Show the function duration statistics")
278284
.setProviderType(ProviderType.DATA_TREE)

tmf/org.eclipse.tracecompass.tmf.core/META-INF/MANIFEST.MF

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: %Bundle-Name
44
Bundle-Vendor: %Bundle-Vendor
5-
Bundle-Version: 10.1.0.qualifier
5+
Bundle-Version: 10.2.0.qualifier
66
Bundle-Localization: plugin
77
Bundle-SymbolicName: org.eclipse.tracecompass.tmf.core;singleton:=true
88
Bundle-Activator: org.eclipse.tracecompass.internal.tmf.core.Activator
@@ -107,6 +107,7 @@ Export-Package: org.eclipse.tracecompass.internal.provisional.tmf.core.model,
107107
org.eclipse.tracecompass.tmf.core.markers,
108108
org.eclipse.tracecompass.tmf.core.model,
109109
org.eclipse.tracecompass.tmf.core.model.annotations,
110+
org.eclipse.tracecompass.tmf.core.model.barchart,
110111
org.eclipse.tracecompass.tmf.core.model.filters,
111112
org.eclipse.tracecompass.tmf.core.model.timegraph,
112113
org.eclipse.tracecompass.tmf.core.model.tree,

tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/dataprovider/IDataProviderDescriptor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ public enum ProviderType {
6161
* @since 6.1
6262
*/
6363
DATA_TREE,
64+
/**
65+
* A provider for bar chart.
66+
* @since 10.2
67+
*/
68+
BAR_CHART,
6469
/**
6570
* A provider with no data. Can be used for grouping purposes and/or as data provider configurator.
6671
* @since 9.5

0 commit comments

Comments
 (0)