diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF
index b35f5aac3..f1c623471 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF
@@ -15,8 +15,11 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.tracecompass.tmf.core,
org.eclipse.tracecompass.tmf.ctf.core,
org.eclipse.tracecompass.analysis.os.linux.core,
+ org.eclipse.tracecompass.analysis.lami.core,
org.eclipse.jdt.annotation;bundle-version="2.2.400"
Export-Package: org.eclipse.tracecompass.incubator.dpdk.core.trace,
+ org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.spin.analysis,
+ org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.throughput.analysis,
org.eclipse.tracecompass.incubator.internal.dpdk.core.lcore.analysis;x-friends:="org.eclipse.tracecompass.incubator.dpdk.core.tests"
Automatic-Module-Name: org.eclipse.tracecompass.incubator.dpdk.core
Import-Package: com.google.common.collect,
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml
index f172cf4d9..f2445ba42 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml
@@ -12,6 +12,35 @@
class="org.eclipse.tracecompass.incubator.dpdk.core.trace.DpdkTrace">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -19,6 +48,18 @@
class="org.eclipse.tracecompass.incubator.internal.dpdk.core.lcore.analysis.DpdkLogicalCoreDataProviderFactory"
id="org.eclipse.tracecompass.incubator.dpdk.lcore.dataprovider">
+
+
+
+
+
+
@@ -31,5 +72,4 @@
trace_type="org.eclipse.tracecompass.incubator.dpdk.core.trace.DpdkTrace">
-
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/analysis/DpdkEthdevEventLayout.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/analysis/DpdkEthdevEventLayout.java
new file mode 100644
index 000000000..83d40337e
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/analysis/DpdkEthdevEventLayout.java
@@ -0,0 +1,177 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.analysis;
+
+/**
+ * This class specifies the names of events required for the analysis of
+ * Ethdev-based applications, and their fields.
+ *
+ * To start using an Ethernet port, a Dpdk application must perform the
+ * following steps:
+ *
+ * 1. **Configure the Ethernet port** by calling the API function
+ * `rte_eth_dev_configure()`. This function requires specifying the number of RX
+ * and TX queues to enable, along with other parameters that determine the
+ * features and capabilities of the port (e.g., RSS).
+ *
+ * 2. **Set up the receive and transmit queues** by calling the API functions
+ * `rte_eth_rx_queue_setup()` and `rte_eth_tx_queue_setup()`. The main
+ * parameters for these functions are the number of descriptors and the memory
+ * pool from which to allocate the `rte_mbuf` network memory buffers.
+ *
+ * 3. **Start the device** by calling the API function `rte_eth_dev_start()`.
+ * From this point onward, the device becomes operational, and enqueue and
+ * dequeue operations can be performed on its queues.
+ *
+ * 4. **Use the port queues** by polling the RX queues for incoming packets
+ * using `rte_eth_rx_burst()` and transmit packets by sending them to the TX
+ * queues using `rte_eth_tx_burst()`.
+ *
+ * 5. **Stop the Ethernet port** by calling `rte_eth_dev_stop()`.
+ *
+ * 6. **Close the Ethernet port** by calling `rte_eth_dev_close()`.
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevEventLayout {
+ /* Event names */
+ private static final String ETH_DEV_RX_BURST_EMPTY = "lib.ethdev.rx.burst.empty"; //$NON-NLS-1$
+ private static final String ETH_DEV_RX_BURST_NON_EMPTY = "lib.ethdev.rx.burst.nonempty"; //$NON-NLS-1$
+ private static final String ETH_DEV_TX_BURST = "lib.ethdev.tx.burst"; //$NON-NLS-1$
+ private static final String PROFILE_ETH_DEV_RX_BURST = "lib.ethdev.rx.burst.extended"; //$NON-NLS-1$
+ private static final String PROFILE_ETH_DEV_TX_BURST = "lib.ethdev.tx.burst.extended"; //$NON-NLS-1$
+
+ /* Event field names */
+ private static final String PORT_ID = "port_id"; //$NON-NLS-1$
+ private static final String QUEUE_ID = "queue_id"; //$NON-NLS-1$
+ private static final String NB_RX = "nb_rx"; //$NON-NLS-1$
+ private static final String NB_TX = "nb_tx"; //$NON-NLS-1$
+ private static final String NB_PKTS = "nb_pkts"; //$NON-NLS-1$
+ private static final String SIZE = "size"; //$NON-NLS-1$
+ private static final String THREAD_NAME = "context.name"; //$NON-NLS-1$
+ private static final String CPU_ID = "context.cpu_id"; //$NON-NLS-1$
+
+ // ------------------------------------------------------------------------
+ // Event names
+ // ------------------------------------------------------------------------
+
+ /**
+ * This event is generated when an empty burst of packets is received
+ *
+ * @return The event name
+ */
+ public String eventEthdevRxBurstEmpty() {
+ return ETH_DEV_RX_BURST_EMPTY;
+ }
+
+ /**
+ * This event is generated when a burst of one or more packets is received
+ *
+ * @return The event name
+ */
+ public String eventEthdevRxBurstNonEmpty() {
+ return ETH_DEV_RX_BURST_NON_EMPTY;
+ }
+
+ /**
+ * This event is generated when a burst of packets is sent
+ *
+ * @return The event name
+ */
+ public String eventEthdevTxBurst() {
+ return ETH_DEV_TX_BURST;
+ }
+
+ /**
+ * This event is emitted by the Ethdev profiling library when a non empty
+ * burst of packets is received
+ *
+ * @return The event name
+ */
+ public String eventProfileEthdevRxBurst() {
+ return PROFILE_ETH_DEV_RX_BURST;
+ }
+
+ /**
+ * This event is emitted by the Ethdev profiling library when a burst of
+ * packets is sent
+ *
+ * @return The event name
+ */
+ public String eventProfileEthdevTxBurst() {
+ return PROFILE_ETH_DEV_TX_BURST;
+ }
+
+ // ------------------------------------------------------------------------
+ // Event field names
+ // ------------------------------------------------------------------------
+
+ /**
+ * @return The name of the field specifying the NIC port identifier
+ */
+ public String fieldPortId() {
+ return PORT_ID;
+ }
+
+ /**
+ * @return The name of the field indicating the id of a queue attached to a
+ * port
+ */
+ public String fieldQueueId() {
+ return QUEUE_ID;
+ }
+
+ /**
+ * @return The name of the field specifying the number of packets received
+ * in a burst
+ */
+ public String fieldNbRxPkts() {
+ return NB_RX;
+ }
+
+ /**
+ * @return The field name specifying the number of packets transmitted as a
+ * burst
+ */
+ public String fieldNbPkts() {
+ return NB_PKTS;
+ }
+
+ /**
+ * @return The field name specifying the number of packets transmitted as a
+ * burst in the profiling event
+ */
+ public String fieldNbTxPkts() {
+ return NB_TX;
+ }
+
+ /**
+ * @return The name of the field specifying the number of bytes denoting the
+ * size of the received or transmitted burst
+ */
+ public String fieldSize() {
+ return SIZE;
+ }
+
+ /**
+ * @return The name of the thread issuing the DPDK event
+ */
+ public String fieldThreadName() {
+ return THREAD_NAME;
+ }
+
+ /**
+ * @return The identifier of the CPU on which the DPDK event was recorded
+ */
+ public String fieldCpuId() {
+ return CPU_ID;
+ }
+}
\ No newline at end of file
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/analysis/package-info.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/analysis/package-info.java
new file mode 100644
index 000000000..fad5420cd
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/analysis/package-info.java
@@ -0,0 +1,12 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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
+ **********************************************************************/
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.analysis;
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/DpdkPollDistributionAnalysis.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/DpdkPollDistributionAnalysis.java
new file mode 100644
index 000000000..dc3f519c9
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/DpdkPollDistributionAnalysis.java
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.poll.distribution.analysis;
+
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.dpdk.core.trace.DpdkTrace;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.analysis.DpdkEthdevEventLayout;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiGenericAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiAnalysis;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableClass;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiLongNumber;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimestamp;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.eclipse.tracecompass.tmf.core.event.aspect.TmfContentFieldAspect;
+import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterMatchesNode;
+import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
+import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+
+
+/**
+ * Dpdk polls distribution analysis is an on-demand analysis that calculates the
+ * number of packets retrieved in a single call to rte_eth_rx_burst(). The poll
+ * distribution is calculated per port queue only.
+ *
+ * @author Adel Belkhiri
+ *
+ */
+public class DpdkPollDistributionAnalysis extends LamiAnalysis {
+
+ private static final long PROGRESS_INTERVAL = (1 << 10) - 1L;
+ private static final int MEMORY_SANITY_LIMIT = 40000;
+ /* Events layout */
+ private final DpdkEthdevEventLayout fLayout = new DpdkEthdevEventLayout();
+
+ /**
+ * Constructor
+ */
+ public DpdkPollDistributionAnalysis() {
+ super(Objects.requireNonNull(Messages.EthdevPollDistribution_AnalysisName), false, trace -> true, Collections.emptyList());
+ }
+
+ @Override
+ protected synchronized void initialize() {
+ // do nothing
+ }
+
+ @Override
+ public boolean canExecute(ITmfTrace trace) {
+ if (trace instanceof DpdkTrace) {
+ return ((DpdkTrace) trace).validate(null, trace.getPath()).isOK();
+ }
+ return false;
+ }
+
+ private static int workRemaining(ITmfTrace trace) {
+ return (int) Math.min(trace.getNbEvents() / (PROGRESS_INTERVAL + 1), Integer.MAX_VALUE);
+ }
+
+ @Override
+ public List execute(ITmfTrace trace, @Nullable TmfTimeRange timeRange, String extraParamsString, IProgressMonitor monitor) throws CoreException {
+ AtomicLong done = new AtomicLong();
+ Map> pollCountPerQueue = new TreeMap<>();
+ TmfTimeRange adjustedTimeRange = timeRange == null ? TmfTimeRange.ETERNITY : timeRange;
+ SubMonitor subMonitor = SubMonitor.convert(monitor, Objects.requireNonNull(Messages.EthdevPollDistribution_AnalysisName), workRemaining(trace));
+
+ /*
+ * Handle the filter in case the user indicates a specific port to
+ * process its events
+ */
+ TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null);
+ filter.setEventAspect(new TmfContentFieldAspect(Objects.requireNonNull(Messages.EthdevPollDistribution_CountLabel), fLayout.fieldPortId()));
+ filter.setRegex(extraParamsString);
+ Predicate filterPred = (event -> extraParamsString.isEmpty() || filter.matches(event));
+
+ // Create and send the event request
+ TmfEventRequest eventRequest = createEventRequest(trace, adjustedTimeRange, filterPred,
+ pollCountPerQueue, subMonitor, done);
+ trace.sendRequest(eventRequest);
+
+ try {
+ eventRequest.waitForCompletion();
+ return convertToLamiTables(adjustedTimeRange, pollCountPerQueue);
+
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return Collections.emptyList();
+ }
+ }
+
+ private TmfEventRequest createEventRequest(ITmfTrace trace, TmfTimeRange timeRange, Predicate filterPredicate, Map> pollAspectCounts, SubMonitor monitor, AtomicLong nbProcessevents) {
+ return new TmfEventRequest(ITmfEvent.class, timeRange, 0, Integer.MAX_VALUE, ExecutionType.BACKGROUND) {
+ @Override
+ public void handleData(ITmfEvent event) {
+ if (monitor.isCanceled()) {
+ cancel();
+ return;
+ }
+
+ // Process events to compute RX polls distribution
+ processEvent(event, filterPredicate, pollAspectCounts);
+
+ if ((nbProcessevents.incrementAndGet() & PROGRESS_INTERVAL) == 0) {
+ monitor.setWorkRemaining(workRemaining(trace));
+ monitor.worked(1);
+ monitor.setTaskName(String.format("DPDK Polls Distribution Analysis (%s events processed)", //$NON-NLS-1$
+ NumberFormat.getInstance().format(nbProcessevents.get())));
+ }
+ }
+ };
+ }
+
+ private void processEvent(ITmfEvent event, Predicate filterPredicate,
+ Map> pollAspectCounts) {
+
+ if (event.getName().equals(fLayout.eventEthdevRxBurstNonEmpty())
+ && filterPredicate.test(event)) {
+ Integer nbRxPkts = event.getContent().getFieldValue(Integer.class, fLayout.fieldNbRxPkts());
+ Integer portId = event.getContent().getFieldValue(Integer.class, fLayout.fieldPortId());
+ Integer queueId = event.getContent().getFieldValue(Integer.class, fLayout.fieldQueueId());
+
+ if (nbRxPkts != null && portId != null && queueId != null) {
+ String queueName = "P" + portId + "/Q" + queueId; //$NON-NLS-1$ //$NON-NLS-2$
+ Map dataSet = pollAspectCounts.computeIfAbsent(queueName, k -> new HashMap<>());
+ if (dataSet.size() < MEMORY_SANITY_LIMIT) {
+ dataSet.merge(nbRxPkts, 1L, (v1, v2) -> v1 + v2);
+ }
+ }
+ }
+ }
+
+ private List convertToLamiTables(TmfTimeRange timeRange,
+ Map> pollCountPerQueue) {
+ List results = new ArrayList<>();
+ for (Map.Entry> entry : pollCountPerQueue.entrySet()) {
+ String queueName = Objects.requireNonNull(entry.getKey());
+ Map dataSet = Objects.requireNonNull(entry.getValue());
+
+ List tableEntries = dataSet.entrySet().stream()
+ .map(e -> new LamiTableEntry(Arrays.asList(
+ new LamiString(Objects.requireNonNull(e.getKey()).toString()),
+ new LamiLongNumber(Objects.requireNonNull(e.getValue())))))
+ .collect(Collectors.toList());
+
+ List tableAspects = Arrays.asList(
+ new LamiCategoryAspect(Objects.requireNonNull(Messages.EthdevPollDistribution_NumberOfPacketsLabel), 0),
+ new LamiCountAspect(Objects.requireNonNull(Messages.EthdevPollDistribution_CountLabel), 1));
+
+ LamiTableClass tableClass = new LamiTableClass(queueName, queueName, tableAspects, Collections.emptySet());
+ results.add(new LamiResultTable(createTimeRange(timeRange), tableClass, tableEntries));
+ }
+ return results;
+ }
+
+ /**
+ * Count aspect, generic
+ *
+ */
+ private final class LamiCountAspect extends LamiGenericAspect {
+ private LamiCountAspect(String name, int column) {
+ super(name, null, column, true, false);
+ }
+ }
+
+ /**
+ * Category aspect, generic
+ *
+ */
+ private final class LamiCategoryAspect extends LamiGenericAspect {
+ private LamiCategoryAspect(String name, int column) {
+ super(name, null, column, false, false);
+ }
+ }
+
+ /**
+ * TODO: move to LAMI
+ */
+ private static LamiTimeRange createTimeRange(TmfTimeRange timeRange) {
+ return new LamiTimeRange(new LamiTimestamp(timeRange.getStartTime().toNanos()), new LamiTimestamp(timeRange.getEndTime().toNanos()));
+ }
+
+ /**
+ * TODO: LamiString in LAMI is private
+ */
+ private final class LamiString extends LamiData {
+ private final String fElement;
+
+ private LamiString(String element) {
+ fElement = element;
+ }
+
+ @Override
+ public @Nullable String toString() {
+ return fElement;
+ }
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/Messages.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/Messages.java
new file mode 100644
index 000000000..73125a8c0
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/Messages.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.poll.distribution.analysis;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Messages for the {@link DpdkPollDistributionAnalysis} on-demand analysis
+ *
+ * @author Adel Belkhiri
+ */
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.poll.distribution.analysis.messages"; //$NON-NLS-1$
+
+ public static @Nullable String EthdevPollDistribution_AnalysisName;
+ public static @Nullable String EthdevPollDistribution_NumberOfPacketsLabel;
+ public static @Nullable String EthdevPollDistribution_CountLabel;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ // do nothing
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/messages.properties b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/messages.properties
new file mode 100644
index 000000000..a9f707ec5
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/messages.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2024 É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
+###############################################################################
+
+EthdevPollDistribution_AnalysisName=DPDK Polls Distribution (ethdev)
+EthdevPollDistribution_NumberOfPacketsLabel=Number of retrieved packets
+EthdevPollDistribution_CountLabel=Count
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/package-info.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/package-info.java
new file mode 100644
index 000000000..7d99fdadf
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/package-info.java
@@ -0,0 +1,12 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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
+ **********************************************************************/
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.poll.distribution.analysis;
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/DpdkPollStatsAnalysis.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/DpdkPollStatsAnalysis.java
new file mode 100644
index 000000000..6c4d6c46c
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/DpdkPollStatsAnalysis.java
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.poll.stats.analysis;
+
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.dpdk.core.trace.DpdkTrace;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.analysis.DpdkEthdevEventLayout;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiGenericAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiAnalysis;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableClass;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiData;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiDoubleNumber;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiLongNumber;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange;
+import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimestamp;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
+import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+
+/**
+ * The DPDK Polls Statistics Analysis is an on-demand analysis that computes
+ * statistics related to the polling of receive queues of Ethernet ports by PMD
+ * (Poll-Mode Driver) threads, through calls to `rte_eth_rx_burst()`. The
+ * statistics include, per queue and per thread, the minimum, maximum, average,
+ * and standard deviation of the number of packets retrieved in a single call to
+ * the `rte_eth_rx_burst()` API function.
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkPollStatsAnalysis extends LamiAnalysis {
+
+ private static final long PROGRESS_INTERVAL = (1 << 10) - 1L;
+ private static final int MEMORY_SANITY_LIMIT = 40000;
+ /* Events layout */
+ private final DpdkEthdevEventLayout fLayout = new DpdkEthdevEventLayout();
+
+ /**
+ * Constructor
+ */
+ public DpdkPollStatsAnalysis() {
+ super(Objects.requireNonNull(Messages.EthdevPollStats_AnalysisName), false, trace -> true, Collections.emptyList());
+ }
+
+ @Override
+ protected synchronized void initialize() {
+ // do nothing
+ }
+
+ @Override
+ public boolean canExecute(ITmfTrace trace) {
+ if (trace instanceof DpdkTrace) {
+ return ((DpdkTrace) trace).validate(null, trace.getPath()).isOK();
+ }
+ return false;
+ }
+
+ private static int workRemaining(ITmfTrace trace) {
+ return (int) Math.min(trace.getNbEvents() / (PROGRESS_INTERVAL + 1), Integer.MAX_VALUE);
+ }
+
+ @Override
+ public List execute(ITmfTrace trace, @Nullable TmfTimeRange timeRange, String extraParamsString, IProgressMonitor monitor) throws CoreException {
+ AtomicLong done = new AtomicLong();
+ Map>> pollCountMap = new HashMap<>();
+ TmfTimeRange adjustedTimeRange = timeRange == null ? TmfTimeRange.ETERNITY : timeRange;
+ SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.EthdevPollStats_AnalysisName, workRemaining(trace));
+
+ // Create and send the event request
+ TmfEventRequest eventRequest = createEventRequest(trace, adjustedTimeRange,
+ pollCountMap, subMonitor, done);
+ trace.sendRequest(eventRequest);
+
+ // Convert the results to LAMI tables
+ try {
+ eventRequest.waitForCompletion();
+ return convertToLamiTables(adjustedTimeRange, pollCountMap);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return Collections.emptyList();
+ }
+ }
+
+ private TmfEventRequest createEventRequest(ITmfTrace trace, TmfTimeRange timeRange, Map>> pollAspectCounts, SubMonitor monitor, AtomicLong nbProcessevents) {
+ return new TmfEventRequest(ITmfEvent.class, timeRange, 0, Integer.MAX_VALUE, ExecutionType.BACKGROUND) {
+ @Override
+ public void handleData(ITmfEvent event) {
+ if (monitor.isCanceled()) {
+ cancel();
+ return;
+ }
+
+ // Process events to compute RX polls statistics
+ processEvent(event, pollAspectCounts);
+
+ if ((nbProcessevents.incrementAndGet() & PROGRESS_INTERVAL) == 0) {
+ monitor.setWorkRemaining(workRemaining(trace));
+ monitor.worked(1);
+ monitor.setTaskName(String.format("Dpdk Polls Statistics Analysis (%s events processed)", //$NON-NLS-1$
+ NumberFormat.getInstance().format(nbProcessevents.get())));
+ }
+ }
+ };
+ }
+
+ private void processEvent(ITmfEvent event, Map>> pollCountsMap) {
+ if (!event.getName().equals(fLayout.eventEthdevRxBurstNonEmpty())) {
+ return;
+ }
+
+ Integer nbRxPkts = event.getContent().getFieldValue(Integer.class, fLayout.fieldNbRxPkts());
+ Integer portId = event.getContent().getFieldValue(Integer.class, fLayout.fieldPortId());
+ Integer queueId = event.getContent().getFieldValue(Integer.class, fLayout.fieldQueueId());
+ String threadName = event.getContent().getFieldValue(String.class, fLayout.fieldThreadName());
+
+ if (nbRxPkts == null || portId == null || queueId == null || threadName == null) {
+ return;
+ }
+
+ // Update the poll count for queues
+ String queueName = "P" + portId + "/Q" + queueId; //$NON-NLS-1$ //$NON-NLS-2$
+ updatePollCountsMap(pollCountsMap, Objects.requireNonNull(Messages.EthdevPollStats_QueueLabel), queueName, nbRxPkts);
+
+ // Update the poll count for threads
+ updatePollCountsMap(pollCountsMap, Objects.requireNonNull(Messages.EthdevPollStats_ThreadLabel), threadName, nbRxPkts);
+ }
+
+ private static void updatePollCountsMap(Map>> pollCountsMap, String aspectName, String key, Integer nbRxPkts) {
+ Map> dataSet = pollCountsMap.computeIfAbsent(aspectName, unused -> new HashMap<>());
+ if (dataSet.size() < MEMORY_SANITY_LIMIT) {
+ List data = dataSet.computeIfAbsent(key, unused -> new ArrayList<>());
+ data.add(nbRxPkts);
+ }
+ }
+
+ private List convertToLamiTables(TmfTimeRange timeRange,
+ Map>> pollAspectCounts) {
+ List results = new ArrayList<>();
+ for (Entry>> entry : pollAspectCounts.entrySet()) {
+
+ Map> dataSet = Objects.requireNonNull(entry.getValue());
+ List entries = new ArrayList<>();
+
+ for (Entry> element : dataSet.entrySet()) {
+
+ List pollValues = Objects.requireNonNull(element.getValue());
+ /*
+ * Calculate the number of successful polls, along with the
+ * minimum and maximum polls values
+ */
+ int nbSuccessfulPolls = pollValues.size();
+ int minPollValue = Collections.min(element.getValue());
+ int maxPollValue = Collections.max(element.getValue());
+
+ /*
+ * Calculate the mean and the standard deviation
+ */
+ double avgPollValue = pollValues.stream().mapToInt(i -> i).average().orElse(0);
+ double sd = pollValues.stream().mapToDouble(val -> Math.pow(val - avgPollValue, 2)).sum();
+ double std = Math.sqrt(sd / pollValues.size());
+ double stdRounded = Math.round(std * 100.0) / 100.0;
+
+ List data = Arrays.asList(
+ new LamiString(element.getKey()),
+ new LamiLongNumber((long) minPollValue),
+ new LamiLongNumber((long) maxPollValue),
+ new LamiLongNumber((long) avgPollValue),
+ new LamiDoubleNumber(stdRounded),
+ new LamiLongNumber((long) nbSuccessfulPolls));
+
+ entries.add(new LamiTableEntry(data));
+ }
+
+ List tableAspects = Arrays.asList(new LamiCategoryAspect(entry.getKey(), 0),
+ new LamiCountAspect(Objects.requireNonNull(Messages.EthdevPollStats_MinimumValueLabel), 1),
+ new LamiCountAspect(Objects.requireNonNull(Messages.EthdevPollStats_MaximumValueLabel), 2),
+ new LamiCountAspect(Objects.requireNonNull(Messages.EthdevPollStats_AverageValueLabel), 3),
+ new LamiCountAspect(Objects.requireNonNull(Messages.EthdevPollStats_StandardDeviationLabel), 4),
+ new LamiCountAspect(Objects.requireNonNull(Messages.EthdevPollStats_CountLabel), 5));
+ LamiTableClass tableClass = new LamiTableClass(entry.getKey(), entry.getKey(), tableAspects, Collections.emptySet());
+ LamiResultTable lrt = new LamiResultTable(createTimeRange(timeRange), tableClass, entries);
+ results.add(lrt);
+ }
+ return results;
+ }
+
+ /**
+ * TODO: move to LAMI
+ */
+ private static LamiTimeRange createTimeRange(TmfTimeRange timeRange) {
+ return new LamiTimeRange(new LamiTimestamp(timeRange.getStartTime().toNanos()), new LamiTimestamp(timeRange.getEndTime().toNanos()));
+ }
+
+ /**
+ * TODO: move to LAMI
+ */
+ private final class LamiString extends LamiData {
+ private final String fElement;
+
+ private LamiString(String element) {
+ fElement = element;
+ }
+
+ @Override
+ public @Nullable String toString() {
+ return fElement;
+ }
+ }
+
+ /**
+ * Count aspect, generic
+ *
+ * TODO: move to LAMI
+ *
+ */
+ private final class LamiCountAspect extends LamiGenericAspect {
+
+ private LamiCountAspect(String name, int column) {
+ super(name, null, column, true, false);
+ }
+ }
+
+ /**
+ * Category aspect, generic
+ *
+ * TODO: move to LAMI
+ *
+ */
+ private final class LamiCategoryAspect extends LamiGenericAspect {
+
+ private LamiCategoryAspect(String name, int column) {
+ super(name, null, column, false, false);
+ }
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/Messages.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/Messages.java
new file mode 100644
index 000000000..95665385b
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/Messages.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.poll.stats.analysis;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Messages for the {@link DpdkPollStatsAnalysis} on-demand analysis
+ *
+ * @author Adel Belkhiri
+ */
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.poll.stats.analysis.messages"; //$NON-NLS-1$
+
+ public static @Nullable String EthdevPollStats_AnalysisName;
+ public static @Nullable String EthdevPollStats_QueueLabel;
+ public static @Nullable String EthdevPollStats_ThreadLabel;
+
+ public static @Nullable String EthdevPollStats_MinimumValueLabel;
+ public static @Nullable String EthdevPollStats_MaximumValueLabel;
+ public static @Nullable String EthdevPollStats_AverageValueLabel;
+ public static @Nullable String EthdevPollStats_StandardDeviationLabel;
+ public static @Nullable String EthdevPollStats_CountLabel;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ // do nothing
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/messages.properties b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/messages.properties
new file mode 100644
index 000000000..7b185ff96
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/messages.properties
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2024 É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
+###############################################################################
+
+EthdevPollStats_AnalysisName=DPDK Polls Statistics (ethdev)
+EthdevPollStats_QueueLabel=Port Queue
+EthdevPollStats_ThreadLabel=PMD Thread
+EthdevPollStats_MinimumValueLabel=Minimum Value
+EthdevPollStats_MaximumValueLabel=Maximum Value
+EthdevPollStats_AverageValueLabel=Average Value
+EthdevPollStats_StandardDeviationLabel=Standard Deviation
+EthdevPollStats_CountLabel=Count
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/package-info.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/package-info.java
new file mode 100644
index 000000000..70e721eae
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/package-info.java
@@ -0,0 +1,12 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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
+ **********************************************************************/
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.poll.stats.analysis;
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAnalysisModule.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAnalysisModule.java
new file mode 100644
index 000000000..670596862
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAnalysisModule.java
@@ -0,0 +1,198 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.spin.analysis;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.tracecompass.incubator.dpdk.core.trace.DpdkTrace;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.Activator;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.analysis.DpdkEthdevEventLayout;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAbstractAnalysisRequirement;
+import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAbstractAnalysisRequirement.PriorityLevel;
+import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAnalysisEventRequirement;
+import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
+import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * This analysis module estimates the percentage of time a PMD thread spends
+ * performing actual work (e.g., fetching and processing packets). The obtained
+ * results can be used to evaluate PMD thread efficiency. The analysis is based
+ * on two events:
+ *
+ * 1- the "lib.ethdev.rx.burst.empty" event indicates an empty poll where no
+ * packets were fetched.
+ *
+ * 2- the "lib.ethdev.rx.burst.nonempty" event indicates a successful poll where
+ * one or more packets were fetched
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevSpinAnalysisModule extends TmfStateSystemAnalysisModule {
+
+ /** The ID of this analysis module */
+ public static final String ID = "org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.spin.analysis"; //$NON-NLS-1$
+ private final DpdkEthdevEventLayout fLayout = new DpdkEthdevEventLayout();
+
+ private final TmfAbstractAnalysisRequirement REQUIREMENT = new TmfAnalysisEventRequirement(ImmutableList.of(
+ fLayout.eventEthdevRxBurstEmpty(), fLayout.eventEthdevRxBurstNonEmpty()), PriorityLevel.AT_LEAST_ONE);
+
+ @Override
+ protected ITmfStateProvider createStateProvider() {
+ ITmfTrace trace = checkNotNull(getTrace());
+
+ if (trace instanceof DpdkTrace) {
+ return new DpdkEthdevSpinStateProvider(trace, fLayout, ID);
+ }
+
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public Iterable getAnalysisRequirements() {
+ return Collections.singleton(REQUIREMENT);
+ }
+
+ /**
+ * Computes the time spent in active and spin states for specified threads
+ * within a given time range.
+ *
+ * @param threads
+ * A set of thread identifiers to analyze
+ * @param start
+ * Start timestamp of the analysis interval
+ * @param end
+ * End timestamp
+ * @return A map where each key is a thread name, and the value is a pair
+ * containing time spent in active and spin states, respectively
+ */
+ public Map> calculateThreadStateDurations(Set threads, long start, long end) {
+ Map> map = new HashMap<>();
+
+ ITmfTrace trace = getTrace();
+ ITmfStateSystem threadSs = getStateSystem();
+ if (trace == null || threadSs == null) {
+ return map;
+ }
+
+ long startTime = Math.max(start, threadSs.getStartTime());
+ long endTime = Math.min(end, threadSs.getCurrentEndTime());
+ if (endTime < startTime) {
+ return map;
+ }
+
+ try {
+ int threadsQuark = threadSs.getQuarkAbsolute(DpdkEthdevSpinAttributes.POLL_THREADS);
+ for (int threadQuark : threadSs.getSubAttributes(threadsQuark, false)) {
+ if (!threads.contains(threadQuark)) {
+ continue;
+ }
+
+ String threadName = threadSs.getAttributeName(threadQuark);
+ long countActive = 0;
+ long countSpin = 0;
+ for (int queueQuark : threadSs.getSubAttributes(threadQuark, false)) {
+ countActive += calculateStateCount(threadSs, queueQuark, startTime, endTime, DpdkEthdevSpinAttributes.ACTIVE_STATUS);
+ countSpin += calculateStateCount(threadSs, queueQuark, startTime, endTime, DpdkEthdevSpinAttributes.SPIN_STATUS);
+ }
+
+ map.put(threadName, new Pair<>(countActive, countSpin));
+ }
+
+ } catch (TimeRangeException | AttributeNotFoundException e) {
+ Activator.getInstance().logError(e.getMessage());
+ }
+
+ return map;
+ }
+
+ /**
+ * Computes the time a thread spent in a specific state within the given
+ * time range.
+ *
+ * @param stateSystem
+ * State system
+ * @param attributeNode
+ * The node representing the thread state.
+ * @param startTime
+ * Start timestamp
+ * @param endTime
+ * End timestamp
+ * @param targetState
+ * The state to analyze (e.g., active or spin)
+ * @return The total time spent in the target state
+ */
+ private static long calculateStateCount(ITmfStateSystem stateSystem, int attributeNode, long startTime, long endTime, String targetState) {
+ long count = 0;
+ long ts = startTime;
+
+ try {
+ while (ts < endTime) {
+ ITmfStateInterval stateInterval = stateSystem.querySingleState(ts, attributeNode);
+ Object stateValue = stateInterval.getStateValue().unboxValue();
+ long stateStart = stateInterval.getStartTime();
+ long stateEnd = stateInterval.getEndTime();
+
+ if (stateValue != null && targetState.equals(stateValue)) {
+ count += interpolateCount(startTime, endTime, stateStart, stateEnd);
+ }
+ ts = Math.min(stateEnd, endTime) + 1;
+ }
+ } catch (TimeRangeException | StateSystemDisposedException e) {
+ Activator.getInstance().logError(e.getMessage());
+ }
+
+ return count;
+ }
+
+ /**
+ * Adjusts the time interval to ensure it fits within the specified range.
+ *
+ * @param startTime
+ * Start timestamp of the analysis interval
+ * @param endTime
+ * End timestamp of the analysis interval
+ * @param startInterval
+ * Start timestamp of the state interval
+ * @param endInterval
+ * End timestamp of the state interval
+ * @return
+ */
+ private static long interpolateCount(long startTime, long endTime, long startInterval, long endInterval) {
+
+ long count = endInterval - startInterval;
+
+ /* Sanity check */
+ if (count > 0) {
+ if (startTime > startInterval) {
+ count -= (startTime - startInterval);
+ }
+
+ if (endTime < endInterval) {
+ count -= (endInterval - endTime);
+ }
+ }
+ return count;
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAttributes.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAttributes.java
new file mode 100644
index 000000000..7da1d92b4
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAttributes.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.spin.analysis;
+
+/**
+ * This interface defines all the attribute names used in the state system.
+ *
+ * @author Adel Belkhiri
+ */
+@SuppressWarnings({ "nls" })
+public interface DpdkEthdevSpinAttributes {
+
+ /** Root attribute for DPDK PMD threads */
+ String POLL_THREADS = "Threads";
+ /** Thread is polling with no packets retrieved */
+ String SPIN_STATUS = "Spin";
+ /** Thread is polling and retrieving at least one packet */
+ String ACTIVE_STATUS = "Active";
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinDataProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinDataProvider.java
new file mode 100644
index 000000000..138692eea
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinDataProvider.java
@@ -0,0 +1,247 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.spin.analysis;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.Activator;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
+import org.eclipse.tracecompass.tmf.core.model.IOutputStyleProvider;
+import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
+import org.eclipse.tracecompass.tmf.core.model.OutputStyleModel;
+import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
+import org.eclipse.tracecompass.tmf.core.model.YModel;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.AbstractTreeCommonXDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.xy.IYModel;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import org.eclipse.tracecompass.tmf.core.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * This data provider will return a XY model, showing the percentage of
+ * occupancy of a PMD thread. The model also shows how much time the thread
+ * spends processing packets versus being idle over a period.
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevSpinDataProvider extends AbstractTreeCommonXDataProvider
+ implements IOutputStyleProvider {
+
+ /**
+ * Extension point ID.
+ */
+ public static final String ID = "org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.spin.dataprovider"; //$NON-NLS-1$
+
+ /**
+ * Title used to create XY models for the
+ * {@link DpdkEthdevSpinDataProvider}.
+ */
+ private static final String PROVIDER_TITLE = Objects.requireNonNull("DPDK Threads Effective CPU Usage"); //$NON-NLS-1$
+
+ private static final String BASE_STYLE = "base"; //$NON-NLS-1$
+
+ private static final String THREADS_LABEL = Objects.requireNonNull(Messages.DpdkEthdevSpin_DataProvider_Threads);
+
+ private static final Map STATE_MAP;
+
+ static {
+ // Create the base style
+ ImmutableMap.Builder builder = new ImmutableMap.Builder<>();
+ builder.put(BASE_STYLE, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.SERIES_TYPE, StyleProperties.SeriesType.SCATTER, StyleProperties.WIDTH, 1.0f)));
+ STATE_MAP = builder.build();
+ }
+
+ /**
+ * Create an instance of {@link DpdkEthdevSpinDataProvider}. Returns a null
+ * instance if the analysis module is not found.
+ *
+ * @param trace
+ * A trace on which we are interested to fetch a model
+ * @return a {@link DpdkEthdevSpinDataProvider} instance. If analysis module
+ * is not found, it returns null
+ */
+ public static @Nullable DpdkEthdevSpinDataProvider create(ITmfTrace trace) {
+ DpdkEthdevSpinAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, DpdkEthdevSpinAnalysisModule.class, DpdkEthdevSpinAnalysisModule.ID);
+ if (module != null) {
+ module.schedule();
+ return new DpdkEthdevSpinDataProvider(trace, module);
+ }
+ return null;
+ }
+
+ /**
+ * Constructor
+ */
+ private DpdkEthdevSpinDataProvider(ITmfTrace trace, DpdkEthdevSpinAnalysisModule module) {
+ super(trace, module);
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ protected TmfTreeModel getTree(ITmfStateSystem ss, Map parameters, @Nullable IProgressMonitor monitor) {
+ List nodes = new ArrayList<>();
+ long rootId = getId(ITmfStateSystem.ROOT_ATTRIBUTE);
+ nodes.add(new TmfTreeDataModel(rootId, -1, Collections.singletonList(Objects.requireNonNull(getTrace().getName())), false, null));
+
+ try {
+ int threadsQuark = ss.getQuarkAbsolute(DpdkEthdevSpinAttributes.POLL_THREADS);
+ long threadsId = getId(threadsQuark);
+ nodes.add(new TmfTreeDataModel(threadsId, rootId, Collections.singletonList(THREADS_LABEL), false, null));
+
+ for (Integer threadQuark : ss.getQuarks(DpdkEthdevSpinAttributes.POLL_THREADS, "*")) { //$NON-NLS-1$
+ String threadName = ss.getAttributeName(threadQuark);
+ long threadId = getId(threadQuark);
+ nodes.add(new TmfTreeDataModel(threadId, threadsId, Collections.singletonList(threadName), false, null));
+ }
+ } catch (AttributeNotFoundException e) {
+ Activator.getInstance().logError("Error getting the root attribute of " + DpdkEthdevSpinAttributes.POLL_THREADS); //$NON-NLS-1$
+ }
+ return new TmfTreeModel<>(Collections.emptyList(), nodes);
+ }
+
+ /**
+ * Subtract from start time the same interval as the interval from start
+ * time to next time, ignoring duplicates in the times requested.
+ */
+ private static long getInitialPrevTime(SelectionTimeQueryFilter filter) {
+ long startTime = filter.getStart();
+ for (long time : filter.getTimesRequested()) {
+ if (time > startTime) {
+ return startTime - (time - startTime);
+ }
+ }
+ return startTime;
+ }
+
+ @Override
+ protected @Nullable Collection getYSeriesModels(ITmfStateSystem ss, Map fetchParameters, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
+ SelectionTimeQueryFilter filter = FetchParametersUtils.createSelectionTimeQuery(fetchParameters);
+ if (filter == null || getSelectedEntries(filter).isEmpty()) {
+ return null;
+ }
+
+ Set threadQuarks = new HashSet<>();
+ Map threadYModels = new HashMap<>();
+
+ // Fetch the quarks of PMD threads for analysis
+ for (Entry entry : getSelectedEntries(filter).entrySet()) {
+ int quark = Objects.requireNonNull(entry.getValue());
+ if ((quark == ITmfStateSystem.ROOT_ATTRIBUTE) ||
+ (ss.getParentAttributeQuark(quark) == ITmfStateSystem.ROOT_ATTRIBUTE)) {
+ continue;
+ }
+ threadQuarks.add(quark);
+ String name = ss.getAttributeName(quark);
+ threadYModels.put(name, new YModel(entry.getKey(), getTrace().getName() + '/' + name, new double[filter.getTimesRequested().length]));
+ }
+
+ calculateThreadStatePercentages(ss, filter, monitor, threadQuarks, threadYModels);
+ return ImmutableList.copyOf(threadYModels.values());
+ }
+
+ /**
+ * Updates the thread YSeries models with the percentage of time each thread spent in active and spinning states
+ *
+ * @param ss
+ * State System
+ * @param filter
+ * Query Filter
+ * @param monitor
+ * Monitor
+ * @param threadQuarks
+ * Set of quarks representing the threads to be analyzed
+ * @param threadYModels
+ * A map of thread names to their corresponding Y-axis data models
+ */
+ private void calculateThreadStatePercentages(ITmfStateSystem ss, SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor, Set threadQuarks, Map threadYModels) {
+ long[] xValues = filter.getTimesRequested();
+ long prevTime = Math.max(getInitialPrevTime(filter), ss.getStartTime());
+ long currentEnd = ss.getCurrentEndTime();
+
+ for (int i = 0; i < xValues.length; i++) {
+ long currentTime = xValues[i];
+ if (currentTime < ss.getStartTime() || currentTime > currentEnd) {
+ prevTime = currentTime;
+ continue;
+ }
+
+ if (prevTime < currentTime) {
+ Map> threadUsageMap = getAnalysisModule().calculateThreadStateDurations(
+ threadQuarks, prevTime, currentTime);
+
+ final int index = i;
+ threadUsageMap.forEach((key, durations) -> {
+ IYModel values = threadYModels.get(key);
+ if (values != null) {
+ values.getData()[index] = getPercentageValue(durations.getFirst(), durations.getSecond());
+ }
+ });
+ } else if (i > 0) {
+ for (IYModel values : threadYModels.values()) {
+ values.getData()[i] = values.getData()[i - 1];
+ }
+ }
+
+ prevTime = currentTime;
+
+ if (monitor != null && monitor.isCanceled()) {
+ return;
+ }
+ }
+ }
+
+ private static double getPercentageValue(long countActive, long countSpin) {
+ return (countActive + countSpin) == 0 ? 0.0 : (double) countActive * 100 / (countActive + countSpin);
+ }
+
+ @Override
+ protected boolean isCacheable() {
+ return true;
+ }
+
+ @Override
+ protected String getTitle() {
+ return PROVIDER_TITLE;
+ }
+
+ @Override
+ public TmfModelResponse fetchStyle(Map fetchParameters, @Nullable IProgressMonitor monitor) {
+ return new TmfModelResponse<>(new OutputStyleModel(STATE_MAP), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinDataProviderFactory.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinDataProviderFactory.java
new file mode 100644
index 000000000..2550304f7
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinDataProviderFactory.java
@@ -0,0 +1,55 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.spin.analysis;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor.ProviderType;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
+import org.eclipse.tracecompass.tmf.core.model.DataProviderDescriptor;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.ITmfTreeXYDataProvider;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+/**
+ * Factory to create instances of the {@link DpdkEthdevSpinDataProvider}.
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevSpinDataProviderFactory implements IDataProviderFactory {
+ private static final IDataProviderDescriptor DESCRIPTOR = new DataProviderDescriptor.Builder()
+ .setId(DpdkEthdevSpinDataProvider.ID)
+ .setName("Dpdk Ethernet RX Spins") //$NON-NLS-1$
+ .setDescription("XY chart showing a rough estimate of PMD threads busyness based on the number of empy and full Rx spins") //$NON-NLS-1$
+ .setProviderType(ProviderType.TREE_TIME_XY)
+ .build();
+
+ @Override
+ public @Nullable ITmfTreeXYDataProvider extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
+ DpdkEthdevSpinAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, DpdkEthdevSpinAnalysisModule.class, DpdkEthdevSpinAnalysisModule.ID);
+ if (module == null) {
+ return null;
+ }
+ module.schedule();
+ return DpdkEthdevSpinDataProvider.create(trace);
+ }
+
+ @Override
+ public Collection getDescriptors(ITmfTrace trace) {
+ DpdkEthdevSpinAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, DpdkEthdevSpinAnalysisModule.class, DpdkEthdevSpinAnalysisModule.ID);
+ return module != null ? Collections.singletonList(DESCRIPTOR) : Collections.emptyList();
+ }
+
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinEventHandler.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinEventHandler.java
new file mode 100644
index 000000000..4cd49595d
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinEventHandler.java
@@ -0,0 +1,79 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.spin.analysis;
+
+import java.util.Objects;
+
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.Activator;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.IDpdkEventHandler;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.analysis.DpdkEthdevEventLayout;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
+import org.eclipse.tracecompass.statesystem.core.StateSystemBuilderUtils;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+
+/**
+ * Event handler for the DPDK events required for the
+ * {@link DpdkEthdevSpinAnalysisModule} analysis
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevSpinEventHandler implements IDpdkEventHandler {
+
+ private final DpdkEthdevEventLayout fLayout;
+
+ DpdkEthdevSpinEventHandler(DpdkEthdevEventLayout layout) {
+ fLayout = layout;
+ }
+
+ /**
+ * Update the count of received or transmitted packets on the state system
+ *
+ * @param ssb
+ * State system builder
+ * @param queueQuark
+ * Quark of the the Ethernet device queue
+ * @param nbPkts
+ * Number of packets received or transmitted
+ * @param ts
+ * Timestamp to use for state change
+ */
+ public void updateCounts(ITmfStateSystemBuilder ssb, int queueQuark, Integer nbPkts, long ts) {
+ if (nbPkts <= 0) {
+ return;
+ }
+ try {
+ StateSystemBuilderUtils.incrementAttributeLong(ssb, ts, queueQuark, nbPkts);
+ } catch (StateValueTypeException e) {
+ Activator.getInstance().logWarning("Problem accessing the state of a NIC queue (Quark = " + queueQuark + ")", e); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ @Override
+ public void handleEvent(ITmfStateSystemBuilder ssb, ITmfEvent event) {
+ long ts = event.getTimestamp().getValue();
+ String eventName = event.getName();
+
+ Integer portId = event.getContent().getFieldValue(Integer.class, fLayout.fieldPortId());
+ Integer queueId = event.getContent().getFieldValue(Integer.class, fLayout.fieldQueueId());
+ String threadName = event.getContent().getFieldValue(String.class, fLayout.fieldThreadName());
+ Integer cpuId = event.getContent().getFieldValue(Integer.class, fLayout.fieldCpuId());
+
+ int threadQuark = ssb.getQuarkAbsoluteAndAdd(DpdkEthdevSpinAttributes.POLL_THREADS, threadName + "/" + cpuId); //$NON-NLS-1$
+ int queueQark = ssb.getQuarkRelativeAndAdd(threadQuark, "P" + Objects.requireNonNull(portId).toString() + "/Q" + Objects.requireNonNull(queueId).toString()); //$NON-NLS-1$ //$NON-NLS-2$
+
+ if (eventName.equals(fLayout.eventEthdevRxBurstEmpty())) {
+ ssb.modifyAttribute(ts, DpdkEthdevSpinAttributes.SPIN_STATUS, queueQark);
+ } else if (eventName.equals(fLayout.eventEthdevRxBurstNonEmpty())) {
+ ssb.modifyAttribute(ts, DpdkEthdevSpinAttributes.ACTIVE_STATUS, queueQark);
+ }
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinStateProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinStateProvider.java
new file mode 100644
index 000000000..2cd17bda9
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinStateProvider.java
@@ -0,0 +1,83 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.spin.analysis;
+
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.AbstractDpdkStateProvider;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.IDpdkEventHandler;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.analysis.DpdkEthdevEventLayout;
+import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * State provider for the {@link DpdkEthdevSpinAnalysisModule} analysis
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevSpinStateProvider extends AbstractDpdkStateProvider {
+
+ private static final int VERSION = 1;
+
+ /** Map events needed for this analysis with their handler functions */
+ private @Nullable Map fEventNames;
+ /** Events layout */
+ private final DpdkEthdevEventLayout fLayout;
+
+ /**
+ * Constructor
+ *
+ * @param trace
+ * trace
+ * @param layout
+ * layout
+ * @param id
+ * id
+ */
+ protected DpdkEthdevSpinStateProvider(ITmfTrace trace, DpdkEthdevEventLayout layout, String id) {
+ super(trace, id);
+ fLayout = layout;
+ }
+
+ /**
+ * Get the version of this state provider
+ */
+ @Override
+ public int getVersion() {
+ return VERSION;
+ }
+
+ /**
+ * Get a new instance
+ */
+ @Override
+ public ITmfStateProvider getNewInstance() {
+ return new DpdkEthdevSpinStateProvider(this.getTrace(), fLayout, DpdkEthdevSpinAnalysisModule.ID);
+ }
+
+ @Override
+ protected @Nullable IDpdkEventHandler getEventHandler(String eventName) {
+ if (fEventNames == null) {
+ ImmutableMap.Builder builder = ImmutableMap.builder();
+ IDpdkEventHandler ethdevEventHandler = new DpdkEthdevSpinEventHandler(fLayout);
+ builder.put(fLayout.eventEthdevRxBurstEmpty(), ethdevEventHandler);
+ builder.put(fLayout.eventEthdevRxBurstNonEmpty(), ethdevEventHandler);
+ fEventNames = builder.build();
+ }
+ if (fEventNames != null) {
+ return fEventNames.get(eventName);
+ }
+ return null;
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/Messages.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/Messages.java
new file mode 100644
index 000000000..db4c9c690
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/Messages.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.spin.analysis;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Messages for {@link DpdkEthdevSpinDataProvider}
+ *
+ * @author Adel Belkhiri
+ */
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.spin.analysis.messages"; //$NON-NLS-1$
+
+ public static @Nullable String DpdkEthdevSpin_DataProvider_Threads;
+ public static @Nullable String DpdkEthdevSpin_DataProvider_YAxis;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ // do nothing
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/messages.properties b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/messages.properties
new file mode 100644
index 000000000..5e0387dc0
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/messages.properties
@@ -0,0 +1,13 @@
+###############################################################################
+# Copyright (c) 2024 É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
+###############################################################################
+
+DpdkEthdevSpin_DataProvider_Threads=Threads
+DpdkEthdevSpin_DataProvider_YAxis=Count
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/package-info.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/package-info.java
new file mode 100644
index 000000000..6054bdc5f
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/package-info.java
@@ -0,0 +1,13 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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
+ **********************************************************************/
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.spin.analysis;
+
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/AbstractDpdkEthdevThroughputDataProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/AbstractDpdkEthdevThroughputDataProvider.java
new file mode 100644
index 000000000..23bf4dfda
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/AbstractDpdkEthdevThroughputDataProvider.java
@@ -0,0 +1,185 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.Activator;
+import org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput.IODataPalette;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
+import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
+import org.eclipse.tracecompass.tmf.core.model.IOutputStyleProvider;
+import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
+import org.eclipse.tracecompass.tmf.core.model.OutputStyleModel;
+import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.AbstractTreeCommonXDataProvider;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Abstract base class for DPDK Ethernet throughput per queue data providers
+ *
+ * @author Adel Belkhiri
+ */
+public abstract class AbstractDpdkEthdevThroughputDataProvider extends AbstractTreeCommonXDataProvider
+ implements IOutputStyleProvider {
+
+ private static final String BASE_STYLE = "base"; //$NON-NLS-1$
+ private static final Map STATE_MAP;
+ private static final List> COLOR_LIST = IODataPalette.getColors();
+ private static final List SUPPORTED_STYLES = ImmutableList.of(
+ StyleProperties.SeriesStyle.SOLID,
+ StyleProperties.SeriesStyle.DASH,
+ StyleProperties.SeriesStyle.DOT,
+ StyleProperties.SeriesStyle.DASHDOT,
+ StyleProperties.SeriesStyle.DASHDOTDOT);
+
+ static {
+ // Create the base style
+ ImmutableMap.Builder builder = ImmutableMap.builder();
+ builder.put(BASE_STYLE, new OutputElementStyle(null, ImmutableMap.of(
+ StyleProperties.SERIES_TYPE, StyleProperties.SeriesType.AREA,
+ StyleProperties.WIDTH, 1.0f)));
+ STATE_MAP = builder.build();
+ }
+
+ /** Ports label */
+ protected static final String PORTS_LABEL = Objects.requireNonNull(Messages.DpdkEthdev_ThroughputDataProvider_PORTS);
+ /** Traffic reception label */
+ protected static final String RX_LABEL = Objects.requireNonNull(Messages.DpdkEthdev_ThroughputDataProvider_TRAFFIC_RX);
+ /** Traffic transmission label */
+ protected static final String TX_LABEL = Objects.requireNonNull(Messages.DpdkEthdev_ThroughputDataProvider_TRAFFIC_TX);
+
+ /**
+ * Constructor
+ *
+ * @param trace
+ * Target trace
+ * @param module
+ * Analysis module
+ */
+ protected AbstractDpdkEthdevThroughputDataProvider(ITmfTrace trace, DpdkEthdevThroughputAnalysisModule module) {
+ super(trace, module);
+ }
+
+ @Override
+ protected boolean isCacheable() {
+ return true;
+ }
+
+ @Override
+ public TmfModelResponse fetchStyle(Map fetchParameters, @Nullable IProgressMonitor monitor) {
+ return new TmfModelResponse<>(new OutputStyleModel(STATE_MAP), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
+ }
+
+ @Override
+ protected TmfTreeModel getTree(ITmfStateSystem ss, Map parameters, @Nullable IProgressMonitor monitor) {
+ List nodes = new ArrayList<>();
+ long rootId = getId(ITmfStateSystem.ROOT_ATTRIBUTE);
+ nodes.add(new TmfTreeDataModel(rootId, -1, Collections.singletonList(Objects.requireNonNull(getTrace().getName())), false, null));
+
+ try {
+ int portsQuark = ss.getQuarkAbsolute(DpdkEthdevThroughputAttributes.PORTS);
+ long portsId = getId(portsQuark);
+ nodes.add(new TmfTreeDataModel(portsId, rootId, Collections.singletonList(PORTS_LABEL), false, null));
+
+ for (Integer portQuark : ss.getQuarks(DpdkEthdevThroughputAttributes.PORTS, "*")) { //$NON-NLS-1$
+ String portName = ss.getAttributeName(portQuark);
+ long portId = getId(portQuark);
+ nodes.add(new TmfTreeDataModel(portId, portsId, Collections.singletonList(portName), false, null));
+
+ int rxQsQuark = ss.optQuarkRelative(portQuark, DpdkEthdevThroughputAttributes.RX_Q);
+ int txQsQuark = ss.optQuarkRelative(portQuark, DpdkEthdevThroughputAttributes.TX_Q);
+
+ nodes.addAll(getQueuesTree(ss, rxQsQuark, portName, portId));
+ nodes.addAll(getQueuesTree(ss, txQsQuark, portName, portId));
+ }
+ } catch (AttributeNotFoundException e) {
+ Activator.getInstance().logError("Error getting the root attribute of " + DpdkEthdevThroughputAttributes.PORTS); //$NON-NLS-1$
+ }
+ return new TmfTreeModel<>(Collections.emptyList(), nodes);
+ }
+
+ /**
+ * Get the XY subtrees related to the RX and TX queues
+ *
+ * @param ss
+ * The state system
+ * @param qsQuark
+ * Quark of the queues list
+ * @param portName
+ * Name of the port to which the queues are attached
+ * @param portId
+ * The ID of the selected port
+ * @return a list of {@link TmfTreeDataModel}
+ */
+ protected List getQueuesTree(ITmfStateSystem ss, int qsQuark, String portName, long portId) {
+ int i = 0;
+ List nodes = new ArrayList<>();
+ boolean isRxQueue = true;
+
+ try {
+ if (DpdkEthdevThroughputAttributes.TX_Q.equals(ss.getAttributeName(qsQuark))) {
+ isRxQueue = false;
+ }
+
+ long qsId = getId(qsQuark);
+ nodes.add(new TmfTreeDataModel(qsId, portId, Collections.singletonList(isRxQueue ? RX_LABEL : TX_LABEL), false, null));
+
+ for (Integer queueQuark : ss.getSubAttributes(qsQuark, false)) {
+ String queueName = ss.getAttributeName(queueQuark);
+ long queueId = getId(queueQuark);
+
+ // Get color and style for the queue
+ Pair colorPair = COLOR_LIST.get(i % COLOR_LIST.size());
+ String seriesStyle = SUPPORTED_STYLES.get((i / COLOR_LIST.size()) % SUPPORTED_STYLES.size());
+
+ nodes.add(new TmfTreeDataModel(queueId, qsId, Collections.singletonList(queueName), true,
+ new OutputElementStyle(BASE_STYLE, ImmutableMap.of(
+ StyleProperties.COLOR, isRxQueue ? colorPair.getFirst() : colorPair.getSecond(),
+ StyleProperties.SERIES_STYLE, seriesStyle,
+ StyleProperties.STYLE_NAME, portName + '/' + (isRxQueue ? RX_LABEL : TX_LABEL) + '/' + queueName))));
+
+ i++;
+ }
+ } catch (IndexOutOfBoundsException e) {
+ Activator.getInstance().logWarning(e.getMessage());
+ }
+ return nodes;
+ }
+
+ /**
+ * Create instances of builders
+ *
+ * @param ss
+ * State System
+ * @param filter
+ * Filter
+ * @return a list of builders
+ */
+ protected abstract List extends AbstractPortQueueBuilder> initBuilders(ITmfStateSystem ss, SelectionTimeQueryFilter filter);
+
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/AbstractPortQueueBuilder.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/AbstractPortQueueBuilder.java
new file mode 100644
index 000000000..348a3c00f
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/AbstractPortQueueBuilder.java
@@ -0,0 +1,75 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import org.eclipse.tracecompass.tmf.core.model.YModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.IYModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.TmfXYAxisDescription;
+
+/**
+ * Abstract base class for constructing data series representing the throughput
+ * of DPDK ports, measured in packets per second (PPS) and bits per second (BPS)
+ */
+abstract class AbstractPortQueueBuilder {
+ private final long fId;
+ private final String fName;
+ protected final int fQueueQuark;
+ protected final double[] fValues;
+ protected double fPrevCount;
+
+ static final double SECONDS_PER_NANOSECOND = 1E-9;
+
+ /**
+ * Constructor
+ *
+ * @param id
+ * The series Id
+ * @param portQueueQuark
+ * The queue's quark
+ * @param name
+ * The name of this series
+ * @param length
+ * The length of the series
+ */
+ AbstractPortQueueBuilder(long id, int portQueueQuark, String name, int length) {
+ fId = id;
+ fQueueQuark = portQueueQuark;
+ fName = name;
+ fValues = new double[length];
+ }
+
+ void setPrevCount(double prevCount) {
+ fPrevCount = prevCount;
+ }
+
+ /**
+ * Update packet counts or packet bytes at the desired index
+ *
+ * @param pos
+ * The index to update
+ * @param newCount
+ * The new count of bytes received or transmitted
+ * @param deltaT
+ * The time difference to the previous value for interpolation
+ */
+ abstract void updateValue(int pos, double newCount, long deltaT);
+
+ /**
+ * Build a data series representing the network traffic throughput
+ *
+ * @param yAxisDescription
+ * Description for the Y axis
+ * @return an IYModel
+ */
+ IYModel build(TmfXYAxisDescription yAxisDescription) {
+ return new YModel(fId, fName, fValues, yAxisDescription);
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputAnalysisModule.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputAnalysisModule.java
new file mode 100644
index 000000000..856f33483
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputAnalysisModule.java
@@ -0,0 +1,64 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.util.Collections;
+
+import org.eclipse.tracecompass.incubator.dpdk.core.trace.DpdkTrace;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.analysis.DpdkEthdevEventLayout;
+import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAbstractAnalysisRequirement;
+import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAbstractAnalysisRequirement.PriorityLevel;
+import org.eclipse.tracecompass.tmf.core.analysis.requirements.TmfAnalysisEventRequirement;
+import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
+import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * An analysis to calculate the traffic reception and transmission throughput
+ * per Ethernet port queue
+ *
+ * Note: To enable the computing of RX and TX throughput in bps, the DPDK
+ * profiling library must be pre-loaded. This custom library emits events that
+ * include the sizes of RX and TX bursts, in terms of number of bytes.
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevThroughputAnalysisModule extends TmfStateSystemAnalysisModule {
+
+ /** The ID of this analysis module */
+ public static final String ID = "org.eclipse.tracecompass.incubator.dpdk.ethdev.throughput.analysis"; //$NON-NLS-1$
+ private final DpdkEthdevEventLayout fLayout = new DpdkEthdevEventLayout();
+
+ private final TmfAbstractAnalysisRequirement REQUIREMENT = new TmfAnalysisEventRequirement(ImmutableList.of(
+ fLayout.eventEthdevRxBurstNonEmpty(), fLayout.eventEthdevTxBurst(),
+ fLayout.eventProfileEthdevRxBurst(), fLayout.eventProfileEthdevTxBurst()),
+ PriorityLevel.AT_LEAST_ONE);
+
+ @Override
+ protected ITmfStateProvider createStateProvider() {
+ ITmfTrace trace = checkNotNull(getTrace());
+
+ if (trace instanceof DpdkTrace) {
+ return new DpdkEthdevThroughputStateProvider(trace, fLayout, ID);
+ }
+
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public Iterable getAnalysisRequirements() {
+ return Collections.singleton(REQUIREMENT);
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputAttributes.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputAttributes.java
new file mode 100644
index 000000000..c1f8a395c
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputAttributes.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+/**
+ * This interface defines all the attribute names used in the state system
+ *
+ * @author Adel Belkhiri
+ */
+public interface DpdkEthdevThroughputAttributes {
+
+ /** Root attribute for DPDK Ethdev NICs */
+ String PORTS = "Ports"; //$NON-NLS-1$
+ /** Reception queues */
+ String RX_Q = "rx_qs"; //$NON-NLS-1$
+ /** Transmission queues */
+ String TX_Q = "tx_qs"; //$NON-NLS-1$
+ /** Packets number */
+ String PKT_COUNT = "pkt_cnt"; //$NON-NLS-1$
+ /** Packets size provided by the profiling library events */
+ String PKT_SIZE_P = "pkt_size_p"; //$NON-NLS-1$
+ /** Packets number provided by the profiling library events */
+ String PKT_COUNT_P = "pkt_cnt_p"; //$NON-NLS-1$
+
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputBpsDataProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputBpsDataProvider.java
new file mode 100644
index 000000000..5c3253ce9
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputBpsDataProvider.java
@@ -0,0 +1,203 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.Activator;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataType;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.xy.IYModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.TmfXYAxisDescription;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * The {@link DpdkEthdevThroughputBpsDataProvider} data provider will return a
+ * XY model showing the Ethernet throughput per port queue, in bits per second
+ * (bps).
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevThroughputBpsDataProvider extends AbstractDpdkEthdevThroughputDataProvider {
+
+ /** The ID of this data provider */
+ public static final String ID = "org.eclipse.tracecompass.incubator.dpdk.ethdev.throughput.bps.dataprovider"; //$NON-NLS-1$
+ private static final String PROVIDER_TITLE = "Dpdk Ethernet Device Throughput - BPS"; //$NON-NLS-1$
+ private static final String BINARY_SPEED_UNIT = "b/s"; //$NON-NLS-1$
+ private static final TmfXYAxisDescription Y_AXIS_DESCRIPTION = new TmfXYAxisDescription(Objects.requireNonNull(Messages.DpdkEthdev_ThroughputBpsDataProvider_YAxis), BINARY_SPEED_UNIT, DataType.NUMBER);
+
+ /**
+ * Class for generating data series representing traffic reception and
+ * transmission throughput
+ */
+ private class PortQueueBuilder extends AbstractPortQueueBuilder {
+ private static final double BITS_PER_BYTE = 8.0;
+
+ protected PortQueueBuilder(long id, int queueQuark, String name, int length) {
+ super(id, queueQuark, name, length);
+ }
+
+ @Override
+ protected void updateValue(int pos, double newCount, long deltaT) {
+ /**
+ * Linear interpolation between current and previous times to
+ * compute packets throughput in bits per second
+ */
+ fValues[pos] = (newCount - fPrevCount) * BITS_PER_BYTE / (SECONDS_PER_NANOSECOND * deltaT);
+ fPrevCount = newCount;
+ }
+ }
+
+ /**
+ * Create an instance of {@link DpdkEthdevThroughputBpsDataProvider}.
+ * Returns a null instance if the analysis module is not found.
+ *
+ * @param trace
+ * A trace on which we are interested to fetch a model
+ * @return A {@link DpdkEthdevThroughputBpsDataProvider} instance. If
+ * analysis module is not found, then returns null
+ */
+ public static @Nullable DpdkEthdevThroughputBpsDataProvider create(ITmfTrace trace) {
+ DpdkEthdevThroughputAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, DpdkEthdevThroughputAnalysisModule.class, DpdkEthdevThroughputAnalysisModule.ID);
+ return module != null ? new DpdkEthdevThroughputBpsDataProvider(trace, module) : null;
+ }
+
+ /**
+ * Constructor
+ */
+ private DpdkEthdevThroughputBpsDataProvider(ITmfTrace trace, DpdkEthdevThroughputAnalysisModule module) {
+ super(trace, module);
+ }
+
+ @Override
+ protected String getTitle() {
+ return PROVIDER_TITLE;
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ protected @Nullable Collection getYSeriesModels(ITmfStateSystem ss, Map fetchParameters,
+ @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
+ SelectionTimeQueryFilter filter = FetchParametersUtils.createSelectionTimeQuery(fetchParameters);
+ if (filter == null) {
+ return null;
+ }
+
+ long[] xValues = filter.getTimesRequested();
+ List builders = initBuilders(ss, filter);
+ if (builders.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ long currentEnd = ss.getCurrentEndTime();
+ long prevTime = filter.getStart();
+
+ if (prevTime >= ss.getStartTime() && prevTime <= currentEnd) {
+ List states = ss.queryFullState(prevTime);
+
+ for (PortQueueBuilder builder : builders) {
+ builder.setPrevCount(extractCount(ss, states, builder.fQueueQuark));
+ }
+ }
+
+ for (int i = 1; i < xValues.length; i++) {
+ if (monitor != null && monitor.isCanceled()) {
+ return null;
+ }
+ long time = xValues[i];
+ if (time > currentEnd) {
+ break;
+ } else if (time >= ss.getStartTime()) {
+ List states = ss.queryFullState(time);
+
+ for (PortQueueBuilder builder : builders) {
+ double count = extractCount(ss, states, builder.fQueueQuark);
+ builder.updateValue(i, count, time - prevTime);
+ }
+ }
+ prevTime = time;
+ }
+
+ return ImmutableList.copyOf(
+ builders.stream()
+ .map(builder -> builder.build(Y_AXIS_DESCRIPTION))
+ .collect(Collectors.toList()));
+ }
+
+ /**
+ * Extract packet burst size
+ *
+ * @param ss
+ * State System
+ * @param states
+ * ITmfStateInterval values
+ * @param queueQuark
+ * Port queue quark
+ * @return The number of bytes received or sent from a queue
+ */
+ private static double extractCount(ITmfStateSystem ss, List states, int queueQuark) {
+ try {
+ int metricQuark = ss.optQuarkRelative(queueQuark, DpdkEthdevThroughputAttributes.PKT_SIZE_P);
+ if (metricQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
+ Object stateValue = states.get(metricQuark).getValue();
+ return stateValue instanceof Number ? ((Number) stateValue).doubleValue() : 0.0;
+ }
+ } catch (IndexOutOfBoundsException e) {
+ Activator.getInstance().logError(e.getMessage());
+ }
+ return Double.NaN;
+ }
+
+ @Override
+ protected List initBuilders(ITmfStateSystem ss, SelectionTimeQueryFilter filter) {
+ int length = filter.getTimesRequested().length;
+ List builders = new ArrayList<>();
+
+ for (Entry entry : getSelectedEntries(filter).entrySet()) {
+ long id = Objects.requireNonNull(entry.getKey());
+ int quark = Objects.requireNonNull(entry.getValue());
+
+ if (quark == ITmfStateSystem.ROOT_ATTRIBUTE || ss.getParentAttributeQuark(quark) == ITmfStateSystem.ROOT_ATTRIBUTE) {
+ continue;
+ }
+
+ int parentQuark = ss.getParentAttributeQuark(quark);
+ if (parentQuark != ITmfStateSystem.INVALID_ATTRIBUTE &&
+ (DpdkEthdevThroughputAttributes.RX_Q.equals(ss.getAttributeName(parentQuark)) ||
+ DpdkEthdevThroughputAttributes.TX_Q.equals(ss.getAttributeName(parentQuark)))) {
+ int portQuark = ss.getParentAttributeQuark(parentQuark);
+ String name = getTrace().getName() + '/' + ss.getAttributeName(portQuark) + '/' + ss.getAttributeName(parentQuark) + '/' + ss.getAttributeName(quark);
+ builders.add(new PortQueueBuilder(id, quark, name, length));
+ }
+ }
+ return builders;
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputBpsDataProviderFactory.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputBpsDataProviderFactory.java
new file mode 100644
index 000000000..74ff4075f
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputBpsDataProviderFactory.java
@@ -0,0 +1,57 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor.ProviderType;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
+import org.eclipse.tracecompass.tmf.core.model.DataProviderDescriptor;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.ITmfTreeXYDataProvider;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+/**
+ * Factory to create instances of the
+ * {@link DpdkEthdevThroughputBpsDataProvider}.
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevThroughputBpsDataProviderFactory implements IDataProviderFactory {
+ private static final IDataProviderDescriptor DESCRIPTOR = new DataProviderDescriptor.Builder()
+ .setId(DpdkEthdevThroughputBpsDataProvider.ID)
+ .setName("Dpdk Ethernet Throughput BPS") //$NON-NLS-1$
+ .setDescription("XY chart illustrating the throughput of DPDK Ethernet NIC queues in bits per second") //$NON-NLS-1$
+ .setProviderType(ProviderType.TREE_TIME_XY)
+ .build();
+
+ @Override
+ public @Nullable ITmfTreeXYDataProvider extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
+ DpdkEthdevThroughputAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, DpdkEthdevThroughputAnalysisModule.class, DpdkEthdevThroughputAnalysisModule.ID);
+ if (module == null) {
+ return null;
+ }
+ module.schedule();
+ return DpdkEthdevThroughputBpsDataProvider.create(trace);
+ }
+
+ @Override
+ public Collection getDescriptors(ITmfTrace trace) {
+ DpdkEthdevThroughputAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, DpdkEthdevThroughputAnalysisModule.class, DpdkEthdevThroughputAnalysisModule.ID);
+ return module != null ? Collections.singletonList(DESCRIPTOR) : Collections.emptyList();
+ }
+
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputEventHandler.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputEventHandler.java
new file mode 100644
index 000000000..d3bdec117
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputEventHandler.java
@@ -0,0 +1,123 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.Activator;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.IDpdkEventHandler;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.analysis.DpdkEthdevEventLayout;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
+import org.eclipse.tracecompass.statesystem.core.StateSystemBuilderUtils;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+
+/**
+ * Event handler to handle the events required for the
+ * {@link DpdkEthdevThroughputAnalysisModule} analysis
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevThroughputEventHandler implements IDpdkEventHandler {
+
+ /* Attribute names */
+ private static final String ETH_NIC_PORTS = Objects.requireNonNull(DpdkEthdevThroughputAttributes.PORTS);
+ private static final String RX_Q = Objects.requireNonNull(DpdkEthdevThroughputAttributes.RX_Q);
+ private static final String TX_Q = Objects.requireNonNull(DpdkEthdevThroughputAttributes.TX_Q);
+ private static final String PKT_NB = Objects.requireNonNull(DpdkEthdevThroughputAttributes.PKT_COUNT);
+ private static final String PKT_NB_P = Objects.requireNonNull(DpdkEthdevThroughputAttributes.PKT_COUNT_P);
+ private static final String PKT_SIZE_P = Objects.requireNonNull(DpdkEthdevThroughputAttributes.PKT_SIZE_P);
+
+ /* Events layout */
+ private final DpdkEthdevEventLayout fLayout;
+
+ DpdkEthdevThroughputEventHandler(DpdkEthdevEventLayout layout) {
+ fLayout = layout;
+ }
+
+ /**
+ * Update the count of bytes received or transmitted on the state system
+ *
+ * @param ssb
+ * State system builder
+ * @param portId
+ * Port identifier
+ * @param queueId
+ * Queue identifier
+ * @param queueCategoryAttribute
+ * Category of the target queue (RX or TX)
+ * @param isProfileEvent
+ * The event is emitted through pre-loading the custom profiling
+ * library
+ * @param nbPkts
+ * Number of packets received or transmitted
+ * @param size
+ * Size of the burst in bytes
+ * @param timestamp
+ * Time to use for the state change
+ */
+ public void updateCounts(ITmfStateSystemBuilder ssb, Integer portId, Integer queueId, String queueCategoryAttribute,
+ boolean isProfileEvent, Integer nbPkts, @Nullable Integer size, long timestamp) {
+ int portQuark = ssb.getQuarkAbsoluteAndAdd(ETH_NIC_PORTS, portId.toString());
+ int queuesQuark = ssb.getQuarkRelativeAndAdd(portQuark, queueCategoryAttribute);
+ int queueQuark = ssb.getQuarkRelativeAndAdd(queuesQuark, queueId.toString());
+
+ try {
+ if (isProfileEvent) {
+ int pktSizeQuark = ssb.getQuarkRelativeAndAdd(queueQuark, PKT_SIZE_P);
+ StateSystemBuilderUtils.incrementAttributeLong(ssb, timestamp, pktSizeQuark, Objects.requireNonNull(size));
+
+ int pktNumberQuark = ssb.getQuarkRelativeAndAdd(queueQuark, PKT_NB_P);
+ StateSystemBuilderUtils.incrementAttributeLong(ssb, timestamp, pktNumberQuark, nbPkts);
+ } else {
+ int pktNumberQuark = ssb.getQuarkRelativeAndAdd(queueQuark, PKT_NB);
+ StateSystemBuilderUtils.incrementAttributeLong(ssb, timestamp, pktNumberQuark, nbPkts);
+ }
+ } catch (StateValueTypeException e) {
+ Activator.getInstance().logWarning("Problem accessing the state of a port queue (Quark = " + queueQuark + ")", e); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ @Override
+ public void handleEvent(ITmfStateSystemBuilder ssb, ITmfEvent event) {
+ String eventName = event.getName();
+ long timestamp = event.getTimestamp().getValue();
+ Integer portId = event.getContent().getFieldValue(Integer.class, fLayout.fieldPortId());
+
+ if (eventName.equals(fLayout.eventEthdevRxBurstNonEmpty()) ||
+ eventName.equals(fLayout.eventProfileEthdevRxBurst())) {
+ handleBurstEvent(ssb, event, Objects.requireNonNull(portId), RX_Q, timestamp);
+ } else if (eventName.equals(fLayout.eventEthdevTxBurst()) ||
+ eventName.equals(fLayout.eventProfileEthdevTxBurst())) {
+ handleBurstEvent(ssb, event, Objects.requireNonNull(portId), TX_Q, timestamp);
+ } else {
+ Activator.getInstance().logError("Unknown event (" + eventName + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ private void handleBurstEvent(ITmfStateSystemBuilder ssb, ITmfEvent event, Integer portId, String queueCategory, long timestamp) {
+ Integer size = null;
+ boolean isRxEvent = queueCategory.equals(RX_Q);
+ Integer nbPkts = event.getContent().getFieldValue(Integer.class, isRxEvent ? fLayout.fieldNbRxPkts() : fLayout.fieldNbPkts());
+ boolean isProfileEvent = event.getName().equals(fLayout.eventProfileEthdevTxBurst()) || event.getName().equals(fLayout.eventProfileEthdevRxBurst());
+
+ if (isProfileEvent) {
+ size = event.getContent().getFieldValue(Integer.class, fLayout.fieldSize());
+ if (!isRxEvent) {
+ nbPkts = event.getContent().getFieldValue(Integer.class, fLayout.fieldNbTxPkts());
+ }
+ }
+
+ Integer queueId = event.getContent().getFieldValue(Integer.class, fLayout.fieldQueueId());
+ updateCounts(ssb, portId, Objects.requireNonNull(queueId), queueCategory, isProfileEvent, Objects.requireNonNull(nbPkts), size, timestamp);
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputPpsDataProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputPpsDataProvider.java
new file mode 100644
index 000000000..1ea56d653
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputPpsDataProvider.java
@@ -0,0 +1,224 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.Activator;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataType;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.xy.IYModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.TmfXYAxisDescription;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * This data provider returns an XY model representing the throughput of
+ * transmitted or received network traffic in packets per second (pps). This
+ * model can be used to generate time-series charts that visualize the network's
+ * bandwidth usage over time.
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevThroughputPpsDataProvider extends AbstractDpdkEthdevThroughputDataProvider {
+
+ /** ID of the Data provider to use in plugin extensions */
+ public static final String ID = "org.eclipse.tracecompass.incubator.dpdk.ethdev.throughput.pps.dataprovider"; //$NON-NLS-1$
+ private static final String PROVIDER_TITLE = "Dpdk Ethernet Device Throughput - PPS"; //$NON-NLS-1$
+ private static final String BINARY_SPEED_UNIT = "/s"; //$NON-NLS-1$
+ private static final TmfXYAxisDescription Y_AXIS_DESCRIPTION = new TmfXYAxisDescription(
+ Objects.requireNonNull(Messages.DpdkEthdev_ThroughputPpsDataProvider_YAxis), BINARY_SPEED_UNIT, DataType.NUMBER);
+
+ /**
+ * Class for generating data series representing packets reception and
+ * transmission rates
+ */
+ protected class PortQueueBuilder extends AbstractPortQueueBuilder {
+ private final boolean fIsProfileMetric;
+
+ /**
+ * Constructor
+ *
+ * @param id
+ * The unique identifier for this data series
+ * @param queueQuark
+ * Quark representing the target port queue in the state
+ * system
+ * @param isProfileMetric
+ * {@code true} if the values are generated from profiling
+ * events; {@code false} otherwise.
+ * @param name
+ * The name of this data series
+ * @param length
+ * The number of data points in this series
+ */
+ protected PortQueueBuilder(long id, int queueQuark, boolean isProfileMetric, String name, int length) {
+ super(id, queueQuark, name, length);
+ fIsProfileMetric = isProfileMetric;
+ }
+
+ @Override
+ protected void updateValue(int pos, double newCount, long deltaT) {
+ fValues[pos] = (newCount - fPrevCount) / (SECONDS_PER_NANOSECOND * deltaT);
+ fPrevCount = newCount;
+ }
+ }
+
+ /**
+ * Create an instance of {@link DpdkEthdevThroughputPpsDataProvider}.
+ * Returns a null instance if the analysis module is not found.
+ *
+ * @param trace
+ * A trace on which we are interested to fetch a model
+ * @return A {@link DpdkEthdevThroughputPpsDataProvider} instance. If
+ * analysis module is not found, it returns null
+ */
+ public static @Nullable DpdkEthdevThroughputPpsDataProvider create(ITmfTrace trace) {
+ DpdkEthdevThroughputAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace,
+ DpdkEthdevThroughputAnalysisModule.class, DpdkEthdevThroughputAnalysisModule.ID);
+ return module != null ? new DpdkEthdevThroughputPpsDataProvider(trace, module) : null;
+ }
+
+ private DpdkEthdevThroughputPpsDataProvider(ITmfTrace trace, DpdkEthdevThroughputAnalysisModule module) {
+ super(trace, module);
+ }
+
+ @Override
+ protected String getTitle() {
+ return PROVIDER_TITLE;
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ protected @Nullable Collection getYSeriesModels(ITmfStateSystem ss, Map fetchParameters,
+ @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
+ SelectionTimeQueryFilter filter = FetchParametersUtils.createSelectionTimeQuery(fetchParameters);
+ if (filter == null) {
+ return null;
+ }
+
+ long[] xValues = filter.getTimesRequested();
+ List builders = initBuilders(ss, filter);
+ if (builders.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ long currentEnd = ss.getCurrentEndTime();
+ long prevTime = filter.getStart();
+
+ if (prevTime >= ss.getStartTime() && prevTime <= currentEnd) {
+ List states = ss.queryFullState(prevTime);
+
+ for (PortQueueBuilder builder : builders) {
+ builder.setPrevCount(extractCount(ss, states, builder.fQueueQuark, builder.fIsProfileMetric));
+ }
+ }
+
+ for (int i = 1; i < xValues.length; i++) {
+ if (monitor != null && monitor.isCanceled()) {
+ return null;
+ }
+ long time = xValues[i];
+ if (time > currentEnd) {
+ break;
+ } else if (time >= ss.getStartTime()) {
+ List states = ss.queryFullState(time);
+
+ for (PortQueueBuilder builder : builders) {
+ double count = extractCount(ss, states, builder.fQueueQuark, builder.fIsProfileMetric);
+ builder.updateValue(i, count, time - prevTime);
+ }
+ }
+ prevTime = time;
+ }
+
+ return ImmutableList.copyOf(
+ builders.stream()
+ .map(builder -> builder.build(Y_AXIS_DESCRIPTION))
+ .collect(Collectors.toList()));
+ }
+
+ /**
+ * Extract a packets counts
+ *
+ * @param ss
+ * State system
+ * @param queueQuark
+ * Port queue quark
+ * @param states
+ * ITmfStateInterval state values
+ * @param isProfileMetric
+ * Whether the metric attribute was generated via the profiling
+ * library or not
+ * @return number of packets received or sent from the RX/TX queue
+ */
+ private static double extractCount(ITmfStateSystem ss, List states, int queueQuark, boolean isProfileMetric) {
+ try {
+ int metricQuark = isProfileMetric
+ ? ss.optQuarkRelative(queueQuark, DpdkEthdevThroughputAttributes.PKT_COUNT_P)
+ : ss.optQuarkRelative(queueQuark, DpdkEthdevThroughputAttributes.PKT_COUNT);
+
+ if (metricQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
+ Object stateValue = states.get(metricQuark).getValue();
+ return stateValue instanceof Number ? ((Number) stateValue).doubleValue() : 0.0;
+ }
+ } catch (IndexOutOfBoundsException e) {
+ Activator.getInstance().logError(e.getMessage());
+ }
+ return Double.NaN;
+ }
+
+ @Override
+ protected List initBuilders(ITmfStateSystem ss, SelectionTimeQueryFilter filter) {
+ int length = filter.getTimesRequested().length;
+ List builders = new ArrayList<>();
+
+ for (Entry entry : getSelectedEntries(filter).entrySet()) {
+ long id = Objects.requireNonNull(entry.getKey());
+ int quark = Objects.requireNonNull(entry.getValue());
+
+ if (quark == ITmfStateSystem.ROOT_ATTRIBUTE || ss.getParentAttributeQuark(quark) == ITmfStateSystem.ROOT_ATTRIBUTE) {
+ continue;
+ }
+
+ int parentQuark = ss.getParentAttributeQuark(quark);
+ if (parentQuark != ITmfStateSystem.INVALID_ATTRIBUTE &&
+ (DpdkEthdevThroughputAttributes.RX_Q.equals(ss.getAttributeName(parentQuark)) ||
+ DpdkEthdevThroughputAttributes.TX_Q.equals(ss.getAttributeName(parentQuark)))) {
+ int portQuark = ss.getParentAttributeQuark(parentQuark);
+ String name = getTrace().getName() + '/' + ss.getAttributeName(portQuark) + '/' + ss.getAttributeName(parentQuark) + '/' + ss.getAttributeName(quark);
+
+ boolean isProfileMetric = ss.getAttributeName(quark).equals(DpdkEthdevThroughputAttributes.PKT_COUNT_P);
+ builders.add(new PortQueueBuilder(id, quark, isProfileMetric, name, length));
+ }
+ }
+ return builders;
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputPpsDataProviderFactory.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputPpsDataProviderFactory.java
new file mode 100644
index 000000000..98b239eed
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputPpsDataProviderFactory.java
@@ -0,0 +1,56 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor.ProviderType;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
+import org.eclipse.tracecompass.tmf.core.model.DataProviderDescriptor;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.ITmfTreeXYDataProvider;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+/**
+ * Factory to create instances of the
+ * {@link DpdkEthdevThroughputPpsDataProvider}.
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevThroughputPpsDataProviderFactory implements IDataProviderFactory {
+ private static final IDataProviderDescriptor DESCRIPTOR = new DataProviderDescriptor.Builder()
+ .setId(DpdkEthdevThroughputPpsDataProvider.ID)
+ .setName("Dpdk Ethernet Throughput PPS") //$NON-NLS-1$
+ .setDescription("XY chart illustrating the throughput of DPDK Ethernet NIC queues in packets per second") //$NON-NLS-1$
+ .setProviderType(ProviderType.TREE_TIME_XY)
+ .build();
+
+ @Override
+ public @Nullable ITmfTreeXYDataProvider extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
+ DpdkEthdevThroughputAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, DpdkEthdevThroughputAnalysisModule.class, DpdkEthdevThroughputAnalysisModule.ID);
+ if (module == null) {
+ return null;
+ }
+ module.schedule();
+ return DpdkEthdevThroughputPpsDataProvider.create(trace);
+ }
+
+ @Override
+ public Collection getDescriptors(ITmfTrace trace) {
+ DpdkEthdevThroughputAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, DpdkEthdevThroughputAnalysisModule.class, DpdkEthdevThroughputAnalysisModule.ID);
+ return module != null ? Collections.singletonList(DESCRIPTOR) : Collections.emptyList();
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputStateProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputStateProvider.java
new file mode 100644
index 000000000..6ec890db1
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputStateProvider.java
@@ -0,0 +1,92 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.AbstractDpdkStateProvider;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.IDpdkEventHandler;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.analysis.DpdkEthdevEventLayout;
+import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * State provider for the {@link DpdkEthdevThroughputAnalysisModule} analysis
+ *
+ * @author Adel Belkhiri
+ */
+public class DpdkEthdevThroughputStateProvider extends AbstractDpdkStateProvider {
+
+ private static final int VERSION = 1;
+
+ /** Map events needed for this analysis with their handler functions */
+ private @Nullable Map fEventNames;
+ /** Events layout */
+ private final DpdkEthdevEventLayout fLayout;
+
+ /**
+ * Constructor
+ *
+ * @param trace
+ * trace
+ * @param layout
+ * events layout
+ * @param id
+ * id
+ */
+ protected DpdkEthdevThroughputStateProvider(ITmfTrace trace, DpdkEthdevEventLayout layout, String id) {
+ super(trace, id);
+ fLayout = layout;
+ }
+
+ /**
+ * Get the version of this state provider
+ */
+ @Override
+ public int getVersion() {
+ return VERSION;
+ }
+
+ /**
+ * Get a new instance
+ */
+ @Override
+ public ITmfStateProvider getNewInstance() {
+ return new DpdkEthdevThroughputStateProvider(this.getTrace(), this.fLayout, DpdkEthdevThroughputAnalysisModule.ID);
+ }
+
+ @Override
+ protected @Nullable IDpdkEventHandler getEventHandler(String eventName) {
+ if (fEventNames == null) {
+ ImmutableMap.Builder builder = ImmutableMap.builder();
+ IDpdkEventHandler ethdevEventHandler = new DpdkEthdevThroughputEventHandler(fLayout);
+
+ builder.put(fLayout.eventEthdevRxBurstNonEmpty(), ethdevEventHandler);
+ builder.put(fLayout.eventEthdevTxBurst(), ethdevEventHandler);
+ /*
+ * The following events are emitted ONLY when the custom profiling
+ * library is pre-loaded. They allow to compute RX and TX traffic
+ * throughput in bps
+ */
+ builder.put(fLayout.eventProfileEthdevTxBurst(), ethdevEventHandler);
+ builder.put(fLayout.eventProfileEthdevRxBurst(), ethdevEventHandler);
+
+ fEventNames = builder.build();
+ }
+ if (fEventNames != null) {
+ return fEventNames.get(eventName);
+ }
+ return null;
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/Messages.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/Messages.java
new file mode 100644
index 000000000..1448beda7
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/Messages.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Messages for {@link DpdkEthdevThroughputBpsDataProvider}
+ *
+ * @author Adel Belkhiri
+ */
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.throughput.analysis.messages"; //$NON-NLS-1$
+ public static @Nullable String DpdkEthdev_ThroughputDataProvider_PORTS;
+ public static @Nullable String DpdkEthdev_ThroughputDataProvider_TRAFFIC_RX;
+ public static @Nullable String DpdkEthdev_ThroughputDataProvider_TRAFFIC_TX;
+ public static @Nullable String DpdkEthdev_ThroughputBpsDataProvider_YAxis;
+ public static @Nullable String DpdkEthdev_ThroughputPpsDataProvider_YAxis;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ // do nothing
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/messages.properties b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/messages.properties
new file mode 100644
index 000000000..0d760c5ca
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/messages.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2024 É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
+###############################################################################
+
+DpdkEthdev_ThroughputDataProvider_PORTS=NIC Port
+DpdkEthdev_ThroughputDataProvider_TRAFFIC_RX=RX
+DpdkEthdev_ThroughputDataProvider_TRAFFIC_TX=TX
+DpdkEthdev_ThroughputBpsDataProvider_YAxis=Throughput (bps)
+DpdkEthdev_ThroughputPpsDataProvider_YAxis=Throughput (pps)
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/package-info.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/package-info.java
new file mode 100644
index 000000000..55642fee3
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/package-info.java
@@ -0,0 +1,13 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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
+ **********************************************************************/
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.throughput.analysis;
+
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/META-INF/MANIFEST.MF b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/META-INF/MANIFEST.MF
index 597d6748d..c87ccf60e 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/META-INF/MANIFEST.MF
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/META-INF/MANIFEST.MF
@@ -14,11 +14,10 @@ Require-Bundle: org.eclipse.ui.ide,
org.eclipse.tracecompass.incubator.dpdk.core,
org.eclipse.tracecompass.common.core,
org.eclipse.core.resources,
- org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional,
- com.google.guava;bundle-version="27.1.0",
org.eclipse.tracecompass.tmf.ui,
org.eclipse.tracecompass.tmf.chart.ui;bundle-version="1.0.10",
- org.eclipse.tracecompass.tmf.core;bundle-version="6.0.0"
+ org.eclipse.tracecompass.tmf.core;bundle-version="6.0.0",
+ org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional
Automatic-Module-Name: org.eclipse.tracecompass.incubator.dpdk.ui
Import-Package: com.google.common.collect;version="21.0.0",
org.eclipse.swtchart
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/plugin.xml b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/plugin.xml
index db2b450f6..ea45828e4 100644
--- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/plugin.xml
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/plugin.xml
@@ -2,6 +2,7 @@
+
+
+
@@ -19,11 +41,33 @@
parentCategory="org.eclipse.linuxtools.tmf.ui.views.category">
+
+
+
+
+
+
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/Messages.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/Messages.java
new file mode 100644
index 000000000..87e787605
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/Messages.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.spin;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Messages for the {@link ThreadSpinStatisticsViewer} view
+ *
+ * @author Adel Belkhiri
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.tracecompass.incubator.internal.dpdk.ui.ethdev.spin.messages"; //$NON-NLS-1$
+ /** Title of the view */
+ public static @Nullable String EthdevThreadSpinStatsView_Title;
+ /** Title of the viewer */
+ public static @Nullable String EthdevThreadSpinStatsViewer_Title;
+ /** X axis caption */
+ public static @Nullable String EthdevThreadSpinStatsViewer_XAxis;
+ /** Y axis caption */
+ public static @Nullable String EthdevThreadSpinStatsViewer_YAxis;
+ /** Thread name column */
+ public static @Nullable String EthdevThreadSpinStatsTreeViewer_ThreadName;
+ /** Legend column*/
+ public static @Nullable String EthdevThreadSpinStatsTreeViewer_Legend;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ // do nothing
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/ThreadSpinStatisticsView.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/ThreadSpinStatisticsView.java
new file mode 100644
index 000000000..336d5d617
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/ThreadSpinStatisticsView.java
@@ -0,0 +1,67 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.spin;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.spin.analysis.DpdkEthdevSpinDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.AbstractSelectTreeViewer2;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfGenericTreeEntry;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeColumnData;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.TmfXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart.TmfXYChartSettings;
+import org.eclipse.tracecompass.tmf.ui.views.xychart.TmfChartView;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Showing the PMD thread real occupancy across the trace duration
+ *
+ * @author Adel Belkhiri
+ */
+public class ThreadSpinStatisticsView extends TmfChartView {
+
+ /**
+ * Identifier of this view
+ */
+ public static final String ID = "org.eclipse.tracecompass.incubator.internal.dpdk.ui.ethdev.spin.statistics.view"; //$NON-NLS-1$
+ private static final double RESOLUTION = 0.4;
+
+ /**
+ * Default constructor
+ */
+ public ThreadSpinStatisticsView() {
+ super(Messages.EthdevThreadSpinStatsView_Title);
+ }
+
+ @Override
+ protected TmfXYChartViewer createChartViewer(Composite parent) {
+ TmfXYChartSettings settings = new TmfXYChartSettings(Messages.EthdevThreadSpinStatsViewer_Title, Messages.EthdevThreadSpinStatsViewer_XAxis, Messages.EthdevThreadSpinStatsViewer_YAxis, RESOLUTION);
+ return new ThreadSpinStatisticsViewer(parent, settings, DpdkEthdevSpinDataProvider.ID);
+ }
+
+ @Override
+ protected @NonNull TmfViewer createLeftChildViewer(@Nullable Composite parent) {
+ return new AbstractSelectTreeViewer2(parent, 1, DpdkEthdevSpinDataProvider.ID) {
+ @Override
+ protected ITmfTreeColumnDataProvider getColumnDataProvider() {
+ return () -> ImmutableList.of(
+ createColumn(Messages.EthdevThreadSpinStatsTreeViewer_ThreadName, Comparator.comparing(TmfGenericTreeEntry::getName)),
+ new TmfTreeColumnData(Messages.EthdevThreadSpinStatsTreeViewer_Legend));
+ }
+ };
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/ThreadSpinStatisticsViewer.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/ThreadSpinStatisticsViewer.java
new file mode 100644
index 000000000..493373f0e
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/ThreadSpinStatisticsViewer.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.spin;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
+import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart.TmfFilteredXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart.TmfXYChartSettings;
+
+/**
+ * Viewer for the {@link ThreadSpinStatisticsView} view
+ *
+ * @author Adel Belkhiri
+ */
+public class ThreadSpinStatisticsViewer extends TmfFilteredXYChartViewer {
+
+ private static final int DEFAULT_SERIES_WIDTH = 1;
+
+ /**
+ * Constructor
+ *
+ * @param parent
+ * Parent composite
+ * @param settings
+ * Chart settings
+ * @param providerId
+ * Data provider ID
+ */
+ public ThreadSpinStatisticsViewer(Composite parent, TmfXYChartSettings settings, String providerId) {
+ super(parent, settings, providerId);
+ }
+
+ @Override
+ public @NonNull OutputElementStyle getSeriesStyle(@NonNull Long seriesId) {
+ return getPresentationProvider().getSeriesStyle(seriesId, StyleProperties.SeriesType.LINE, DEFAULT_SERIES_WIDTH);
+ }
+
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/messages.properties b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/messages.properties
new file mode 100644
index 000000000..b5e4a0336
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/messages.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2024 É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
+###############################################################################
+EthdevThreadSpinStatsView_Title=PMD Effective Busyness View
+EthdevThreadSpinStatsViewer_Title=PMD Effective Busyness View
+EthdevThreadSpinStatsViewer_XAxis=Time
+EthdevThreadSpinStatsViewer_YAxis=% BUSY
+EthdevThreadSpinStatsTreeViewer_ThreadName=Threads
+EthdevThreadSpinStatsTreeViewer_Legend=Legend
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/package-info.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/package-info.java
new file mode 100644
index 000000000..79b47420f
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/package-info.java
@@ -0,0 +1,11 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.spin;
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/Messages.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/Messages.java
new file mode 100644
index 000000000..32a6e9eeb
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/Messages.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.throughput.bps;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Translatable strings for the {@link NicQueueThroughputBpsView} view
+ *
+ * @author Adel Belkhiri
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.tracecompass.incubator.internal.dpdk.ui.ethdev.throughput.bps.messages"; //$NON-NLS-1$
+ /** Title of the DPDK Ethernet throughput view */
+ public static @Nullable String EthdevThroughputBpsView_Title;
+ /** Title of the DPDK Ethernet throughput viewer */
+ public static @Nullable String EthdevThroughputBpsViewer_Title;
+ /** X axis caption */
+ public static @Nullable String EthdevThroughputBpsViewer_XAxis;
+ /** Port ID column */
+ public static @Nullable String EthdevThroughputBpsTreeViewer_PortName;
+ /** Legend Column */
+ public static @Nullable String EthdevThroughputBpsTreeViewer_Legend;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ // do nothing
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/NicQueueThroughputBpsView.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/NicQueueThroughputBpsView.java
new file mode 100644
index 000000000..1e5064f76
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/NicQueueThroughputBpsView.java
@@ -0,0 +1,67 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.throughput.bps;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.throughput.analysis.DpdkEthdevThroughputBpsDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.AbstractSelectTreeViewer2;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfGenericTreeEntry;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeColumnData;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.TmfXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart.TmfFilteredXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart.TmfXYChartSettings;
+import org.eclipse.tracecompass.tmf.ui.views.xychart.TmfChartView;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * This view shows packet reception and transmission throughput per port queue
+ *
+ * @author Adel Belkhiri
+ */
+public class NicQueueThroughputBpsView extends TmfChartView {
+
+ /**
+ * Identifier of this view
+ */
+ public static final String ID = "org.eclipse.tracecompass.incubator.dpdk.ethdev.throughput.bps.view"; //$NON-NLS-1$
+ private static final double RESOLUTION = 0.2;
+
+ /**
+ * Default constructor
+ */
+ public NicQueueThroughputBpsView() {
+ super(Messages.EthdevThroughputBpsView_Title);
+ }
+
+ @Override
+ protected TmfXYChartViewer createChartViewer(Composite parent) {
+ TmfXYChartSettings settings = new TmfXYChartSettings(Messages.EthdevThroughputBpsViewer_Title, Messages.EthdevThroughputBpsViewer_XAxis, null, RESOLUTION);
+ return new TmfFilteredXYChartViewer(parent, settings, DpdkEthdevThroughputBpsDataProvider.ID);
+ }
+
+ @Override
+ protected @NonNull TmfViewer createLeftChildViewer(@Nullable Composite parent) {
+ return new AbstractSelectTreeViewer2(parent, 1, DpdkEthdevThroughputBpsDataProvider.ID) {
+ @Override
+ protected ITmfTreeColumnDataProvider getColumnDataProvider() {
+ return () -> ImmutableList.of(createColumn(Messages.EthdevThroughputBpsTreeViewer_PortName, Comparator.comparing(TmfGenericTreeEntry::getName)),
+ new TmfTreeColumnData(Messages.EthdevThroughputBpsTreeViewer_Legend));
+ }
+ };
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/messages.properties b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/messages.properties
new file mode 100644
index 000000000..f0fc93c5e
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/messages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2024 É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
+###############################################################################
+EthdevThroughputBpsView_Title=NIC Throughput View
+EthdevThroughputBpsViewer_Title=NIC Throughput View
+EthdevThroughputBpsViewer_XAxis=Time
+EthdevThroughputBpsTreeViewer_PortName=Port ID
+EthdevThroughputBpsTreeViewer_Legend=Legend
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/package-info.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/package-info.java
new file mode 100644
index 000000000..878397b03
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/package-info.java
@@ -0,0 +1,11 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.throughput.bps;
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/Messages.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/Messages.java
new file mode 100644
index 000000000..71a716af9
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/Messages.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.throughput.pps;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Translatable strings for the {@link NicQueueThroughputPpsView} View
+ *
+ * @author Adel Belkhiri
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.tracecompass.incubator.internal.dpdk.ui.ethdev.throughput.pps.messages"; //$NON-NLS-1$
+ /** Title of the DPDK Ethernet rate view */
+ public static @Nullable String EthdevThroughputPpsView_Title;
+ /** Title of the DPDK Ethernet rate viewer */
+ public static @Nullable String EthdevThroughputPpsViewer_Title;
+ /** X axis caption */
+ public static @Nullable String EthdevThroughputPpsViewer_XAxis;
+ /** Port ID column */
+ public static @Nullable String EthdevThroughputPpsTreeViewer_PortName;
+ /** Legend Column */
+ public static @Nullable String EthdevThroughputPpsTreeViewer_Legend;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ // do nothing
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/NicQueueThroughputPpsView.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/NicQueueThroughputPpsView.java
new file mode 100644
index 000000000..590da2355
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/NicQueueThroughputPpsView.java
@@ -0,0 +1,67 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.throughput.pps;
+
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.throughput.analysis.DpdkEthdevThroughputPpsDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.AbstractSelectTreeViewer2;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfGenericTreeEntry;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeColumnData;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.TmfXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart.TmfFilteredXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart.TmfXYChartSettings;
+import org.eclipse.tracecompass.tmf.ui.views.xychart.TmfChartView;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * This view shows the packet reception and transmission rate per queue
+ *
+ * @author Adel Belkhiri
+ */
+public class NicQueueThroughputPpsView extends TmfChartView {
+
+ /**
+ * Identifier of this view
+ */
+ public static final String ID = "org.eclipse.tracecompass.incubator.dpdk.ethdev.throughput.pps.view"; //$NON-NLS-1$
+ private static final double RESOLUTION = 0.2;
+
+ /**
+ * Default constructor
+ */
+ public NicQueueThroughputPpsView() {
+ super(Messages.EthdevThroughputPpsView_Title);
+ }
+
+ @Override
+ protected TmfXYChartViewer createChartViewer(Composite parent) {
+ TmfXYChartSettings settings = new TmfXYChartSettings(Messages.EthdevThroughputPpsViewer_Title, Messages.EthdevThroughputPpsViewer_XAxis, null, RESOLUTION);
+ return new TmfFilteredXYChartViewer(parent, settings, DpdkEthdevThroughputPpsDataProvider.ID);
+ }
+
+ @Override
+ protected @NonNull TmfViewer createLeftChildViewer(@Nullable Composite parent) {
+ return new AbstractSelectTreeViewer2(parent, 1, DpdkEthdevThroughputPpsDataProvider.ID) {
+ @Override
+ protected ITmfTreeColumnDataProvider getColumnDataProvider() {
+ return () -> ImmutableList.of(createColumn(Messages.EthdevThroughputPpsTreeViewer_PortName, Comparator.comparing(TmfGenericTreeEntry::getName)),
+ new TmfTreeColumnData(Messages.EthdevThroughputPpsTreeViewer_Legend));
+ }
+ };
+ }
+}
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/messages.properties b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/messages.properties
new file mode 100644
index 000000000..58c197711
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/messages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2024 É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
+###############################################################################
+EthdevThroughputPpsView_Title=NIC Rate View
+EthdevThroughputPpsViewer_Title=NIC Rate View
+EthdevThroughputPpsViewer_XAxis=Time
+EthdevThroughputPpsTreeViewer_PortName=Port ID
+EthdevThroughputPpsTreeViewer_Legend=Legend
diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/package-info.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/package-info.java
new file mode 100644
index 000000000..963a8f374
--- /dev/null
+++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/package-info.java
@@ -0,0 +1,11 @@
+/**********************************************************************
+ * Copyright (c) 2024 É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.incubator.internal.dpdk.ui.ethdev.throughput.pps;