Skip to content

Commit c36911f

Browse files
author
Siwei Zhang
committed
tmf: Add bar chart data provider type with feature to fetch bars
This commit implements the feature to fetch bars in bar chart data provider. All features for bar chart are implemented after this commit. CallStackAnalysis is used as the initial example input to demonstrate this functionality through a function density view. [Added] The feature to fetch bars in bar chart data provider Signed-off-by: Siwei Zhang <siwei.zhang@ericsson.com>
1 parent c0d40cc commit c36911f

14 files changed

Lines changed: 862 additions & 25 deletions

File tree

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,35 @@
1111
package org.eclipse.tracecompass.internal.analysis.profiling.core.callstack.provider;
1212

1313
import java.util.ArrayList;
14+
import java.util.Collection;
1415
import java.util.Collections;
1516
import java.util.HashMap;
1617
import java.util.List;
1718
import java.util.Map;
19+
import java.util.Map.Entry;
20+
import java.util.Objects;
1821

1922
import org.eclipse.core.runtime.IProgressMonitor;
2023
import org.eclipse.jdt.annotation.NonNull;
2124
import org.eclipse.jdt.annotation.Nullable;
2225
import org.eclipse.tracecompass.analysis.profiling.core.callstack.CallStackAnalysis;
2326
import org.eclipse.tracecompass.analysis.profiling.core.callstack.CallStackSeries;
2427
import org.eclipse.tracecompass.common.core.NonNullUtils;
28+
import org.eclipse.tracecompass.internal.analysis.profiling.core.callgraph.ICalledFunction;
29+
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
2530
import org.eclipse.tracecompass.segmentstore.core.ISegment;
2631
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
2732
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
2833
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
2934
import org.eclipse.tracecompass.tmf.core.dataprovider.DataType;
35+
import org.eclipse.tracecompass.tmf.core.model.YModel;
3036
import org.eclipse.tracecompass.tmf.core.model.barchart.AbstractTreeBarChartDataProvider;
3137
import org.eclipse.tracecompass.tmf.core.model.barchart.AxisDomain;
38+
import org.eclipse.tracecompass.tmf.core.model.barchart.Sampling;
39+
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionSamplingQueryFilter;
3240
import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
3341
import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
42+
import org.eclipse.tracecompass.tmf.core.model.xy.IYModel;
3443
import org.eclipse.tracecompass.tmf.core.model.xy.TmfXYAxisDescription;
3544
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
3645

@@ -162,4 +171,77 @@ private static int getThreadProcessId(String name, @Nullable Object value) {
162171
return UNKNOWN_PID;
163172
}
164173
}
174+
175+
@Override
176+
protected @Nullable Collection<@NonNull IYModel> getYSeriesModels(
177+
@NonNull ITmfStateSystem ss,
178+
@NonNull Map<@NonNull String, @NonNull Object> fetchParameters,
179+
@Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
180+
181+
SelectionSamplingQueryFilter filter = FetchParametersUtils.createSelectionSamplingQuery(fetchParameters);
182+
if (filter == null) {
183+
return null;
184+
}
185+
186+
Sampling sampling = filter.getSampling();
187+
if (!(sampling instanceof Sampling.Ranges)) {
188+
return null;
189+
}
190+
191+
Sampling.Ranges timeRanges = (Sampling.Ranges) sampling;
192+
Map<@NonNull Long, @NonNull Integer> selectedEntries = getSelectedEntries(filter);
193+
194+
CallStackSeries series = fModule.getCallStackSeries();
195+
if (series == null) {
196+
return null;
197+
}
198+
199+
List<Sampling.@NonNull Range<@NonNull Long>> ranges = timeRanges.ranges();
200+
Map<Integer, double[]> pidsToBins = new HashMap<>();
201+
202+
// Initialize bins only for selected entries
203+
for (Entry<Integer, Long> pidToEntryId : fPidsToEntryIds.entrySet()) {
204+
int pid = pidToEntryId.getKey();
205+
long entryId = pidToEntryId.getValue();
206+
if (selectedEntries.containsKey(entryId)) {
207+
pidsToBins.put(pid, new double[ranges.size()]);
208+
}
209+
}
210+
211+
// Count function durations falling in each bin, assume the ranges can
212+
// overlap
213+
for (ISegment segment : series.getIntersectingElements(ss.getStartTime(), ss.getCurrentEndTime())) {
214+
if (segment instanceof ICalledFunction function) {
215+
int pid = function.getProcessId();
216+
double[] bins = pidsToBins.get(pid);
217+
if (bins == null) {
218+
continue;
219+
}
220+
long duration = segment.getLength();
221+
for (int i = 0; i < ranges.size(); i++) {
222+
Sampling.Range<@NonNull Long> range = ranges.get(i);
223+
if (duration >= range.start() && duration <= range.end()) {
224+
bins[i]++;
225+
}
226+
}
227+
}
228+
}
229+
230+
// Build final Y models
231+
List<@NonNull IYModel> yModels = new ArrayList<>();
232+
for (Entry<Integer, double[]> entry : pidsToBins.entrySet()) {
233+
int pid = entry.getKey();
234+
double[] bins = entry.getValue();
235+
long entryId = fPidsToEntryIds.getOrDefault(pid, -1L);
236+
yModels.add(new YModel(entryId, getNameFromPID(pid), bins));
237+
}
238+
239+
return yModels;
240+
}
241+
242+
@Override
243+
protected String getTitle() {
244+
String title = Objects.requireNonNull(Messages.CallStackBarChartDataProviderFactory_title);
245+
return title;
246+
}
165247
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ public class CallStackBarChartDataProviderFactory implements IDataProviderFactor
6363
return providers.get(0);
6464
}
6565
if (!providers.isEmpty()) {
66-
return new TmfTreeBarChartCompositeDataProvider<>(providers, CallStackBarChartDataProvider.ID);
66+
String title = Objects.requireNonNull(Messages.CallStackBarChartDataProviderFactory_title);
67+
return new TmfTreeBarChartCompositeDataProvider<>(providers, title, CallStackBarChartDataProvider.ID);
6768
}
6869
return null;
6970
}

tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/TmfXyResponseFactory.java

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**********************************************************************
2-
* Copyright (c) 2017 Ericsson
2+
* Copyright (c) 2017, 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
@@ -21,7 +21,9 @@
2121
import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
2222
import org.eclipse.tracecompass.tmf.core.model.SeriesModel.SeriesModelBuilder;
2323
import org.eclipse.tracecompass.tmf.core.model.TmfXyModel;
24+
import org.eclipse.tracecompass.tmf.core.model.barchart.Sampling;
2425
import org.eclipse.tracecompass.tmf.core.model.xy.ISeriesModel;
26+
import org.eclipse.tracecompass.tmf.core.model.xy.ISeriesModel.DisplayType;
2527
import org.eclipse.tracecompass.tmf.core.model.xy.ITmfXyModel;
2628
import org.eclipse.tracecompass.tmf.core.model.xy.IYModel;
2729
import org.eclipse.tracecompass.tmf.core.model.xy.TmfXYAxisDescription;
@@ -60,7 +62,9 @@ private TmfXyResponseFactory() {
6062
* Tells whether the computed model is complete or partial
6163
* @return A {@link TmfModelResponse} with either a running status or a
6264
* completed status
65+
* @deprecated Use {@link Sampling} for xValues instead.
6366
*/
67+
@Deprecated(since = "10.2", forRemoval = true)
6468
public static TmfModelResponse<ITmfXyModel> create(String title, long[] xValues, Collection<IYModel> yModels, boolean isComplete) {
6569
List<ISeriesModel> series = Lists.transform(new ArrayList<>(yModels), model -> {
6670
SeriesModelBuilder builder = new SeriesModelBuilder(model.getId(), model.getName(), xValues, model.getData());
@@ -78,6 +82,75 @@ public static TmfModelResponse<ITmfXyModel> create(String title, long[] xValues,
7882
return new TmfModelResponse<>(model, ITmfResponse.Status.RUNNING, Objects.requireNonNull(CommonStatusMessage.RUNNING));
7983
}
8084

85+
/**
86+
* Create a {@link TmfModelResponse} for values with common sampling values,
87+
* with a either RUNNING or COMPLETED status. Model is not null, it's either
88+
* partial or full.
89+
*
90+
* @param title
91+
* Chart title
92+
* @param samples
93+
* The samples requested by the viewer
94+
* @param yModels
95+
* Collection of IYModel
96+
* @param isComplete
97+
* Tells whether the computed model is complete or partial
98+
* @return A {@link TmfModelResponse} with either a running status or a
99+
* completed status
100+
*/
101+
public static TmfModelResponse<ITmfXyModel> create(String title, Sampling samples, Collection<IYModel> yModels, boolean isComplete) {
102+
List<ISeriesModel> series = Lists.transform(new ArrayList<>(yModels), model -> {
103+
SeriesModelBuilder builder = new SeriesModelBuilder(model.getId(), model.getName(), samples, model.getData());
104+
TmfXYAxisDescription yAxis = model.getYAxisDescription();
105+
if (yAxis != null) {
106+
builder.yAxisDescription(yAxis);
107+
}
108+
return builder.build();
109+
});
110+
ITmfXyModel model = new TmfXyModel(title, series);
111+
112+
if (isComplete) {
113+
return new TmfModelResponse<>(model, ITmfResponse.Status.COMPLETED, Objects.requireNonNull(CommonStatusMessage.COMPLETED));
114+
}
115+
return new TmfModelResponse<>(model, ITmfResponse.Status.RUNNING, Objects.requireNonNull(CommonStatusMessage.RUNNING));
116+
}
117+
118+
/**
119+
* Create a {@link TmfModelResponse} for values with common sampling values,
120+
* with a either RUNNING or COMPLETED status. Model is not null, it's either
121+
* partial or full.
122+
*
123+
* @param title
124+
* Chart title
125+
* @param samples
126+
* The samples requested by the viewer
127+
* @param yModels
128+
* Collection of IYModel
129+
* @param displayType
130+
* The type of display, see {@link DisplayType}
131+
* @param isComplete
132+
* Tells whether the computed model is complete or partial
133+
* @return A {@link TmfModelResponse} with either a running status or a
134+
* completed status
135+
*/
136+
public static TmfModelResponse<ITmfXyModel> create(String title, Sampling samples, Collection<IYModel> yModels, DisplayType displayType, boolean isComplete) {
137+
List<ISeriesModel> series = Lists.transform(new ArrayList<>(yModels), model -> {
138+
SeriesModelBuilder builder = new SeriesModelBuilder(model.getId(), model.getName(), samples, model.getData());
139+
TmfXYAxisDescription yAxis = model.getYAxisDescription();
140+
if (yAxis != null) {
141+
builder.yAxisDescription(yAxis);
142+
}
143+
builder.seriesDisplayType(displayType);
144+
return builder.build();
145+
});
146+
ITmfXyModel model = new TmfXyModel(title, series);
147+
148+
if (isComplete) {
149+
return new TmfModelResponse<>(model, ITmfResponse.Status.COMPLETED, Objects.requireNonNull(CommonStatusMessage.COMPLETED));
150+
}
151+
return new TmfModelResponse<>(model, ITmfResponse.Status.RUNNING, Objects.requireNonNull(CommonStatusMessage.RUNNING));
152+
}
153+
81154
/**
82155
* Create a {@link TmfModelResponse} with a either RUNNING or COMPLETED status.
83156
* Model is not null, it's either partial or full.

tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/filters/FetchParametersUtils.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**********************************************************************
2-
* Copyright (c) 2019 Ericsson
2+
* Copyright (c) 2019, 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
@@ -20,7 +20,9 @@
2020
import org.eclipse.jdt.annotation.Nullable;
2121
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.VirtualTableQueryFilter;
2222
import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
23+
import org.eclipse.tracecompass.tmf.core.model.barchart.Sampling;
2324
import org.eclipse.tracecompass.tmf.core.model.filters.FilterTimeQueryFilter;
25+
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionSamplingQueryFilter;
2426
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
2527
import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
2628

@@ -61,6 +63,19 @@ private FetchParametersUtils() {
6163
return (timeRequested == null || selectedItems == null) ? null : new SelectionTimeQueryFilter(timeRequested, selectedItems);
6264
}
6365

66+
/**
67+
* Create a {@link SelectionSamplingQueryFilter} with the given map of parameters
68+
*
69+
* @param parameters
70+
* Map of parameters
71+
* @return A {@link SelectionSamplingQueryFilter} or null if the parameters are invalid
72+
*/
73+
public static @Nullable SelectionSamplingQueryFilter createSelectionSamplingQuery(Map<String, Object> parameters) {
74+
Sampling samplingRequested = DataProviderParameterUtils.extractSamplingRequested(parameters);
75+
List<Long> selectedItems = DataProviderParameterUtils.extractSelectedItems(parameters);
76+
return (samplingRequested == null || selectedItems == null) ? null : new SelectionSamplingQueryFilter(samplingRequested, selectedItems);
77+
}
78+
6479
/**
6580
* Create a {@link VirtualTableQueryFilter} with the given map of parameters
6681
*

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

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**********************************************************************
2-
* Copyright (c) 2019 Ericsson
2+
* Copyright (c) 2019, 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
@@ -22,6 +22,7 @@
2222
import org.eclipse.jdt.annotation.NonNullByDefault;
2323
import org.eclipse.jdt.annotation.Nullable;
2424
import org.eclipse.tracecompass.internal.tmf.core.Activator;
25+
import org.eclipse.tracecompass.tmf.core.model.barchart.Sampling;
2526

2627
import com.google.common.collect.HashMultimap;
2728
import com.google.common.collect.Multimap;
@@ -41,6 +42,12 @@ public class DataProviderParameterUtils {
4142
*/
4243
public static final String REQUESTED_TIME_KEY = "requested_times"; //$NON-NLS-1$
4344

45+
/**
46+
* Sampling requested key
47+
* @since 10.2
48+
*/
49+
public static final String REQUESTED_SAMPLING_KEY = "requested_sampling"; //$NON-NLS-1$
50+
4451
/**
4552
* Selected items key
4653
*/
@@ -178,6 +185,62 @@ private DataProviderParameterUtils() {
178185
return null;
179186
}
180187

188+
/**
189+
* Extract a {@link Sampling} object from a map of parameters.
190+
*
191+
* Supports the following formats:
192+
* <ul>
193+
* <li>{@code [1, 2, 3]} for {@link Sampling.Timestamps}</li>
194+
* <li>{@code ["Read", "Write", "Idle"]} for {@link Sampling.Categories}</li>
195+
* <li>{@code [[1, 2], [3, 4]]} for {@link Sampling.Ranges}</li>
196+
* </ul>
197+
*
198+
* @param parameters
199+
* Map of parameters
200+
* @param key
201+
* Parameter key for the value to extract
202+
* @return a {@link Sampling} instance, or null if extraction fails
203+
* @since 10.2
204+
*/
205+
@SuppressWarnings("null")
206+
public static @Nullable Sampling extractSampling(Map<String, Object> parameters, String key) {
207+
Object value = parameters.get(key);
208+
if (!(value instanceof Collection<?> collection)) {
209+
return null;
210+
}
211+
212+
// Check for [1, 2, 3] -> Timestamps
213+
if (collection.stream().allMatch(o -> o instanceof Number)) {
214+
long[] timestamps = collection.stream()
215+
.mapToLong(o -> ((Number) o).longValue())
216+
.toArray();
217+
return new Sampling.Timestamps(timestamps);
218+
}
219+
220+
// Check for ["a", "b", "c"] -> Categories
221+
if (collection.stream().allMatch(o -> o instanceof String)) {
222+
List<String> categories = (List<String>) collection.stream().map(Object::toString).toList();
223+
return new Sampling.Categories(categories);
224+
}
225+
226+
// Check for [[1,2],[3,4]] -> TimeRanges
227+
List<Sampling.Range<Long>> ranges = new ArrayList<>();
228+
for (Object item : collection) {
229+
if (!(item instanceof List<?> pair) || pair.size() != 2) {
230+
return null; // Invalid range entry
231+
}
232+
Object first = pair.get(0);
233+
Object second = pair.get(1);
234+
if (!(first instanceof Number) || !(second instanceof Number)) {
235+
return null; // Invalid types in range
236+
}
237+
long start = ((Number) first).longValue();
238+
long end = ((Number) second).longValue();
239+
ranges.add(new Sampling.Range<>(start, end));
240+
}
241+
return ranges.isEmpty() ? null : new Sampling.Ranges(ranges);
242+
}
243+
181244
/**
182245
* Helper to extract time requested from a map of parameters
183246
*
@@ -189,6 +252,18 @@ private DataProviderParameterUtils() {
189252
return extractLongList(parameters, REQUESTED_TIME_KEY);
190253
}
191254

255+
/**
256+
* Helper to extract time requested from a map of parameters
257+
*
258+
* @param parameters
259+
* Map of parameters
260+
* @return List of times or null if no time requested in the map
261+
* @since 10.2
262+
*/
263+
public static @Nullable Sampling extractSamplingRequested(Map<String, Object> parameters) {
264+
return extractSampling(parameters, REQUESTED_SAMPLING_KEY);
265+
}
266+
192267
/**
193268
* Helper to extract selected items from a map of parameters
194269
*

0 commit comments

Comments
 (0)