From ded760b6b191537394c63e48e657da1c0b600e55 Mon Sep 17 00:00:00 2001 From: Adel Belkhiri Date: Tue, 15 Oct 2024 17:35:47 -0400 Subject: [PATCH 1/4] dpdk: add packet throughput analysis for ethdev Introduces packet throughput analysis for DPDK applications using the ethdev library. The analysis calculates packet throughput in both bits per second (bps) and packets per second (pps) for transmitted and received Ethernet traffic on a per-queue basis. Note that the calculation of throughput in bps requires the existence of custom profiling events in the trace Signed-off-by: Adel Belkhiri --- .../META-INF/MANIFEST.MF | 1 + .../plugin.xml | 18 +- .../analysis/DpdkEthdevEventLayout.java | 177 ++++++++++++++ .../core/ethdev/analysis/package-info.java | 12 + ...tractDpdkEthdevThroughputDataProvider.java | 185 +++++++++++++++ .../analysis/AbstractPortQueueBuilder.java | 75 ++++++ .../DpdkEthdevThroughputAnalysisModule.java | 64 +++++ .../DpdkEthdevThroughputAttributes.java | 34 +++ .../DpdkEthdevThroughputBpsDataProvider.java | 203 ++++++++++++++++ ...thdevThroughputBpsDataProviderFactory.java | 57 +++++ .../DpdkEthdevThroughputEventHandler.java | 123 ++++++++++ .../DpdkEthdevThroughputPpsDataProvider.java | 224 ++++++++++++++++++ ...thdevThroughputPpsDataProviderFactory.java | 56 +++++ .../DpdkEthdevThroughputStateProvider.java | 92 +++++++ .../ethdev/throughput/analysis/Messages.java | 38 +++ .../throughput/analysis/messages.properties | 16 ++ .../throughput/analysis/package-info.java | 13 + .../META-INF/MANIFEST.MF | 5 +- .../plugin.xml | 30 +++ .../ui/ethdev/throughput/bps/Messages.java | 42 ++++ .../bps/NicQueueThroughputBpsView.java | 67 ++++++ .../ethdev/throughput/bps/messages.properties | 15 ++ .../ethdev/throughput/bps/package-info.java | 11 + .../ui/ethdev/throughput/pps/Messages.java | 42 ++++ .../pps/NicQueueThroughputPpsView.java | 67 ++++++ .../ethdev/throughput/pps/messages.properties | 15 ++ .../ethdev/throughput/pps/package-info.java | 11 + 27 files changed, 1689 insertions(+), 4 deletions(-) create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/analysis/DpdkEthdevEventLayout.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/analysis/package-info.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/AbstractDpdkEthdevThroughputDataProvider.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/AbstractPortQueueBuilder.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputAnalysisModule.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputAttributes.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputBpsDataProvider.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputBpsDataProviderFactory.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputEventHandler.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputPpsDataProvider.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputPpsDataProviderFactory.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/DpdkEthdevThroughputStateProvider.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/Messages.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/messages.properties create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/throughput/analysis/package-info.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/Messages.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/NicQueueThroughputBpsView.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/messages.properties create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/bps/package-info.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/Messages.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/NicQueueThroughputPpsView.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/messages.properties create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/throughput/pps/package-info.java 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..7eef4387c 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 @@ -17,6 +17,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.tracecompass.analysis.os.linux.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.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..28d16bc68 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml +++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml @@ -12,6 +12,15 @@ class="org.eclipse.tracecompass.incubator.dpdk.core.trace.DpdkTrace"> + + + + @@ -19,6 +28,14 @@ class="org.eclipse.tracecompass.incubator.internal.dpdk.core.lcore.analysis.DpdkLogicalCoreDataProviderFactory" id="org.eclipse.tracecompass.incubator.dpdk.lcore.dataprovider"> + + + + @@ -31,5 +48,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/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 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 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 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..535db5d2f 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 +34,26 @@ 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/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; From fe057bb55e729b085f64c2f19bfdb23bdda4541d Mon Sep 17 00:00:00 2001 From: Adel Belkhiri Date: Tue, 15 Oct 2024 17:56:39 -0400 Subject: [PATCH 2/4] dpdk: add busyness analysis for ethdev PMD threads PMD threads continuously poll NIC queues, leading to constant 100% CPU usage. This analysis provides a rough estimation of how busy PMD threads are with actual work, by comparing the times they successfully retrieve packets from the NIC queue versus the times they are merely spinning without processing any packets. Signed-off-by: Adel Belkhiri --- .../META-INF/MANIFEST.MF | 1 + .../plugin.xml | 13 + .../DpdkEthdevSpinAnalysisModule.java | 198 ++++++++++++++ .../analysis/DpdkEthdevSpinAttributes.java | 28 ++ .../analysis/DpdkEthdevSpinDataProvider.java | 247 ++++++++++++++++++ .../DpdkEthdevSpinDataProviderFactory.java | 55 ++++ .../analysis/DpdkEthdevSpinEventHandler.java | 79 ++++++ .../analysis/DpdkEthdevSpinStateProvider.java | 83 ++++++ .../core/ethdev/spin/analysis/Messages.java | 36 +++ .../ethdev/spin/analysis/messages.properties | 13 + .../ethdev/spin/analysis/package-info.java | 13 + .../plugin.xml | 14 + .../dpdk/ui/ethdev/spin/Messages.java | 44 ++++ .../ethdev/spin/ThreadSpinStatisticsView.java | 67 +++++ .../spin/ThreadSpinStatisticsViewer.java | 49 ++++ .../dpdk/ui/ethdev/spin/messages.properties | 16 ++ .../dpdk/ui/ethdev/spin/package-info.java | 11 + 17 files changed, 967 insertions(+) create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAnalysisModule.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAttributes.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinDataProvider.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinDataProviderFactory.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinEventHandler.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinStateProvider.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/Messages.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/messages.properties create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/package-info.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/Messages.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/ThreadSpinStatisticsView.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/ThreadSpinStatisticsViewer.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/messages.properties create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/src/org/eclipse/tracecompass/incubator/internal/dpdk/ui/ethdev/spin/package-info.java 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 7eef4387c..21c661002 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 @@ -17,6 +17,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.tracecompass.analysis.os.linux.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 diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml index 28d16bc68..e28a4faa3 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml +++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml @@ -21,6 +21,15 @@ class="org.eclipse.tracecompass.incubator.dpdk.core.trace.DpdkTrace"> + + + + @@ -36,6 +45,10 @@ class="org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.throughput.analysis.DpdkEthdevThroughputPpsDataProviderFactory" id="org.eclipse.tracecompass.incubator.dpdk.ethdev.throughput.pps.dataprovider"> + + 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..97363b664 --- /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 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.ui/plugin.xml b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/plugin.xml index 535db5d2f..ea45828e4 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/plugin.xml +++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.ui/plugin.xml @@ -25,6 +25,13 @@ class="org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.throughput.analysis.DpdkEthdevThroughputAnalysisModule"> + + + + @@ -54,6 +61,13 @@ name="Ethernet Throughput View (BPS)" restorable="true"> + + 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; From a0f04c313a84bc72b66ff68ac328fa40f8f86fef Mon Sep 17 00:00:00 2001 From: Adel Belkhiri Date: Tue, 15 Oct 2024 18:06:07 -0400 Subject: [PATCH 3/4] dpdk: add packet distribution analysis for ethdev Introduce packet distribution analysis for PMD threads based on ethdev library. This analysis computes the distribution of packets retrieved in a single rte_eth_rx_burst() call, on a per-queue basis. Signed-off-by: Adel Belkhiri --- .../META-INF/MANIFEST.MF | 1 + .../plugin.xml | 7 + .../DpdkPollDistributionAnalysis.java | 227 ++++++++++++++++++ .../poll/distribution/analysis/Messages.java | 38 +++ .../distribution/analysis/messages.properties | 14 ++ .../distribution/analysis/package-info.java | 12 + 6 files changed, 299 insertions(+) create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/DpdkPollDistributionAnalysis.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/Messages.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/messages.properties create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/package-info.java 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 21c661002..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,6 +15,7 @@ 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, diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml index e28a4faa3..276c5b453 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml +++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml @@ -31,6 +31,13 @@ + + + + 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; From d21e82bcd43a37ccfcbcefb7002abfa70ade9b1a Mon Sep 17 00:00:00 2001 From: Adel Belkhiri Date: Tue, 15 Oct 2024 18:18:51 -0400 Subject: [PATCH 4/4] dpdk: add packet distribution statistics analysis Introduce packet distribution statistics analysis for PMD threads in the ethdev library. This analysis calculates various statistics related to the distribution of packets retrieved in a single rte_eth_rx_burst() call, on a per-thread and per-queue basis. The computed statistics include the minimum, maximum, average number of packets retrieved, as well as the standard deviation. Signed-off-by: Adel Belkhiri --- .../plugin.xml | 4 + .../stats/analysis/DpdkPollStatsAnalysis.java | 262 ++++++++++++++++++ .../ethdev/poll/stats/analysis/Messages.java | 44 +++ .../poll/stats/analysis/messages.properties | 19 ++ .../poll/stats/analysis/package-info.java | 12 + 5 files changed, 341 insertions(+) create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/DpdkPollStatsAnalysis.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/Messages.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/messages.properties create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/stats/analysis/package-info.java diff --git a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml index 276c5b453..f2445ba42 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml +++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml @@ -37,6 +37,10 @@ class="org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.poll.distribution.analysis.DpdkPollDistributionAnalysis" id="org.eclipse.tracecompass.incubator.dpdk.core.ethdev.poll.distribution"> + + 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;