From d7e0272edad4167846f29497780d7d2cdd8b3f25 Mon Sep 17 00:00:00 2001 From: Matthew Khouzam Date: Fri, 3 Oct 2025 11:41:41 -0400 Subject: [PATCH] tmf.core: Add event type children to histogram data provider - Add individual event type histogram support to HistogramDataProvider - Track event type IDs in fEventTypeIds map - Extend fetchTree() to include event type children from statistics - Extend fetchXY() to handle event type histogram requests - Add getEventTypeHistogram() method for per-type density data - Change EventDensityViewer to use AREA chart style This allows users to view histogram density per event type in addition to total and lost events. note: this commit was made with the assistance of Claude Sonnet 4 [Added] lines in histogram Change-Id: I142886fe9fa8e154ef2825a5a01c4f51a48385ea Signed-off-by: Matthew Khouzam --- .../HistogramDataProviderTest.java | 11 ++- .../core/histogram/HistogramDataProvider.java | 84 +++++++++++++++++++ .../eventdensity/EventDensityViewTest.java | 4 +- .../eventdensity/EventDensityViewer.java | 2 +- 4 files changed, 96 insertions(+), 5 deletions(-) diff --git a/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/histogram/dataprovider/HistogramDataProviderTest.java b/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/histogram/dataprovider/HistogramDataProviderTest.java index b343d0a309..15012ab8f5 100644 --- a/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/histogram/dataprovider/HistogramDataProviderTest.java +++ b/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/histogram/dataprovider/HistogramDataProviderTest.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; +import java.util.Map.Entry; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.annotation.NonNull; @@ -108,7 +109,10 @@ public void testHelloLost() throws TmfAnalysisException { ITmfResponse.Status.COMPLETED, treeResponse.getStatus()); TmfTreeModel<@NonNull TmfTreeDataModel> treeModel = treeResponse.getModel(); assertNotNull(treeModel); - assertEquals(EXPECTED_FULL_PATHS, getFullPaths(treeModel.getEntries())); + List entries = getFullPaths(treeModel.getEntries()); + for (String path : EXPECTED_FULL_PATHS) { + assertTrue(entries.contains(path)); + } for (TmfTreeDataModel entry : treeModel.getEntries()) { if (!entry.getName().equals(TRACE_NAME)) { @@ -124,7 +128,10 @@ public void testHelloLost() throws TmfAnalysisException { ITmfResponse.Status.COMPLETED, xyResponse.getStatus()); ITmfXyModel xyModel = xyResponse.getModel(); assertNotNull(xyModel); - assertEquals(EXPECTED_YDATA, Maps.uniqueIndex(xyModel.getSeriesData(), ISeriesModel::getId)); + Map data = Maps.uniqueIndex(xyModel.getSeriesData(), ISeriesModel::getId); + for (Entry yEntry : EXPECTED_YDATA.entrySet()) { + assertEquals(data.get(yEntry.getKey()), yEntry.getValue()); + } } finally { module.dispose(); CtfTmfTestTraceUtils.dispose(CtfTestTrace.HELLO_LOST); diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/histogram/HistogramDataProvider.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/histogram/HistogramDataProvider.java index 66cb426c51..a84c1d9d62 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/histogram/HistogramDataProvider.java +++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/histogram/HistogramDataProvider.java @@ -16,6 +16,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -74,6 +75,7 @@ public class HistogramDataProvider extends AbstractTmfTraceDataProvider implemen private final long fTraceId = TRACE_IDS.getAndIncrement(); private final long fTotalId = TRACE_IDS.getAndIncrement(); private final long fLostId = TRACE_IDS.getAndIncrement(); + private final Map fEventTypeIds = new HashMap<>(); /** * Constructor @@ -101,6 +103,21 @@ public TmfModelResponse> fetchTree(Map eventTypeQuarks = eventsSs.getSubAttributes(eventTypesQuark, false); + for (Integer quark : eventTypeQuarks) { + String eventTypeName = eventsSs.getAttributeName(quark); + if (!"Lost event".equals(eventTypeName)) { //$NON-NLS-1$ + long eventTypeId = TRACE_IDS.getAndIncrement(); + fEventTypeIds.put(eventTypeName, eventTypeId); + builder.add(new TmfXyTreeDataModel(eventTypeId, fTraceId, Collections.singletonList(eventTypeName), true, null, true)); + } + } + } + if (eventsSs.waitUntilBuilt(0)) { TmfModelResponse> response = new TmfModelResponse<>(new TmfTreeModel<>(Collections.emptyList(), builder.build()), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); fCached = response; @@ -145,6 +162,21 @@ public TmfModelResponse> fetchTree(Map entry : fEventTypeIds.entrySet()) { + if (selected.contains(entry.getValue())) { + try { + YModel series = getEventTypeHistogram(eventsSs, entry.getKey(), entry.getValue(), xValues); + builder.add(series); + } catch (StateSystemDisposedException e) { + return TmfXyResponseFactory.createFailedResponse(CommonStatusMessage.STATE_SYSTEM_FAILED); + } + } + } + } + boolean completed = eventsSs != null ? eventsSs.waitUntilBuilt(0) || eventsSs.getCurrentEndTime() >= filter.getEnd() : false; return TmfXyResponseFactory.create(TITLE, xValues, builder.build(), completed); @@ -212,6 +244,58 @@ private YModel getLostEvents(ITmfStateSystem ss, long[] times) throws StateSyste return new YModel(fLostId, lostName, leY); } + private YModel getEventTypeHistogram(ITmfStateSystem eventsSs, String eventTypeName, long eventTypeId, long[] times) throws StateSystemDisposedException { + int eventTypeQuark = eventsSs.optQuarkAbsolute(Attributes.EVENT_TYPES, eventTypeName); + if (eventTypeQuark == ITmfStateSystem.INVALID_ATTRIBUTE) { + return new YModel(eventTypeId, getTrace().getName() + '/' + eventTypeName, new double[times.length]); + } + + List timesList = new ArrayList<>(); + for (long time : times) { + timesList.add(time); + } + + List values = new ArrayList<>(); + try { + List rawValues = new ArrayList<>(); + + Iterable intervals = eventsSs.query2D(Collections.singletonList(eventTypeQuark), timesList); + for (ITmfStateInterval interval : intervals) { + rawValues.add(interval); + } + rawValues.sort(new Comparator() { + @Override + public int compare(ITmfStateInterval arg0, ITmfStateInterval arg1) { + return Long.compare(arg0.getStartTime(), arg1.getStartTime()); + } + }); + long prevValue = rawValues.get(0).getValueLong(); + for (ITmfStateInterval interval : rawValues) { + Object value = interval.getValue(); + long currentValue = (value instanceof Number) ? ((Number) value).longValue() : 0; + values.add(currentValue - prevValue); + prevValue = currentValue; + } + } catch (StateSystemDisposedException e) { + // Return empty data if state system is disposed + for (int i = 0; i < times.length; i++) { + values.add(0L); + } + } + + // Pad with zeros if needed + while (values.size() < times.length) { + values.add(0L); + } + + double[] y = new double[times.length]; + for (int i = 0; i < Math.min(times.length, values.size()); i++) { + y[i] = values.get(i).doubleValue(); + } + + return new YModel(eventTypeId, getTrace().getName() + '/' + eventTypeName, y); + } + private class LostEventInterval { private final long fStartTime; private final long fEndTime; diff --git a/tmf/org.eclipse.tracecompass.tmf.ui.swtbot.tests/src/org/eclipse/tracecompass/tmf/ui/swtbot/tests/views/eventdensity/EventDensityViewTest.java b/tmf/org.eclipse.tracecompass.tmf.ui.swtbot.tests/src/org/eclipse/tracecompass/tmf/ui/swtbot/tests/views/eventdensity/EventDensityViewTest.java index 7a9d69069f..c8668d514c 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui.swtbot.tests/src/org/eclipse/tracecompass/tmf/ui/swtbot/tests/views/eventdensity/EventDensityViewTest.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui.swtbot.tests/src/org/eclipse/tracecompass/tmf/ui/swtbot/tests/views/eventdensity/EventDensityViewTest.java @@ -66,9 +66,9 @@ public void testManipulatingTreeViewer() { // Uncheck a leaf of the tree totalItem.uncheck(); - assertFalse(root.isChecked()); + assertTrue(root.isChecked()); assertFalse(totalItem.isChecked()); - WaitUtils.waitUntil(c -> c.getSeriesSet().getSeries().length == 0, chart, + WaitUtils.waitUntil(c -> c.getSeriesSet().getSeries().length >= 1, chart, "A data series has not been removed."); } diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/viewers/eventdensity/EventDensityViewer.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/viewers/eventdensity/EventDensityViewer.java index 025a85db61..d9d0a04832 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/viewers/eventdensity/EventDensityViewer.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/viewers/eventdensity/EventDensityViewer.java @@ -56,6 +56,6 @@ public EventDensityViewer(Composite parent, TmfXYChartSettings settings) { @Override public @NonNull OutputElementStyle getSeriesStyle(@NonNull Long seriesId) { - return getPresentationProvider().getSeriesStyle(seriesId, StyleProperties.SeriesType.BAR, DEFAULT_SERIES_WIDTH); + return getPresentationProvider().getSeriesStyle(seriesId, StyleProperties.SeriesType.AREA, DEFAULT_SERIES_WIDTH); } }