From 6fe4c6c2f430da0c010109bd5276fa1ee62b56ec 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 | 2 + .../plugin.xml | 18 +- .../core/analysis/DpdkEthdevEventLayout.java | 214 +++++++++++++++++ ...tractDpdkEthdevThroughputDataProvider.java | 199 ++++++++++++++++ .../analysis/AbstractPortQueueBuilder.java | 65 ++++++ .../DpdkEthdevThroughputAnalysisModule.java | 61 +++++ .../DpdkEthdevThroughputAttributes.java | 34 +++ .../DpdkEthdevThroughputBpsDataProvider.java | 200 ++++++++++++++++ ...thdevThroughputBpsDataProviderFactory.java | 57 +++++ .../DpdkEthdevThroughputEventHandler.java | 171 ++++++++++++++ .../DpdkEthdevThroughputPpsDataProvider.java | 220 ++++++++++++++++++ ...thdevThroughputPpsDataProviderFactory.java | 56 +++++ .../DpdkEthdevThroughputStateProvider.java | 86 +++++++ .../ethdev/throughput/analysis/Messages.java | 38 +++ .../throughput/analysis/messages.properties | 16 ++ .../throughput/analysis/package-info.java | 13 ++ .../plugin.xml | 30 +++ .../ui/ethdev/throughput/bps/Messages.java | 42 ++++ .../bps/NicQueueThroughputBpsView.java | 68 ++++++ .../ethdev/throughput/bps/messages.properties | 15 ++ .../ethdev/throughput/bps/package-info.java | 11 + .../ui/ethdev/throughput/pps/Messages.java | 42 ++++ .../pps/NicQueueThroughputPpsView.java | 69 ++++++ .../ethdev/throughput/pps/messages.properties | 15 ++ .../ethdev/throughput/pps/package-info.java | 11 + 25 files changed, 1752 insertions(+), 1 deletion(-) create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/analysis/DpdkEthdevEventLayout.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..632b95cb1 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF +++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF @@ -15,8 +15,10 @@ Require-Bundle: org.eclipse.ui, org.eclipse.tracecompass.tmf.core, org.eclipse.tracecompass.tmf.ctf.core, org.eclipse.tracecompass.analysis.os.linux.core, + com.google.guava, 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/analysis/DpdkEthdevEventLayout.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/analysis/DpdkEthdevEventLayout.java new file mode 100644 index 000000000..9e8b8a588 --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/analysis/DpdkEthdevEventLayout.java @@ -0,0 +1,214 @@ +/********************************************************************** + * 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.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_CONFIGURE = "lib.ethdev.configure"; //$NON-NLS-1$ + 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_Q = "nb_rx_q";//$NON-NLS-1$ + private static final String NB_TX_Q = "nb_tx_q";//$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 RC = "rc"; //$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 triggered when the configuration of the Ethernet port is + * completed. + * + * @return The event name. + */ + public static String eventEthdevConfigure() { + return ETH_DEV_CONFIGURE; + } + + /** + * This event is generated when an empty burst of packets is received + * + * @return The event name + */ + public static 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 static 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 static 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 static 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 static String eventProfileEthdevTxBurst() { + return PROFILE_ETH_DEV_TX_BURST; + } + // ------------------------------------------------------------------------ + // Event field names + // ------------------------------------------------------------------------ + + /** + * @return The name of the field specifying the NIC port identifier + */ + public static String fieldPortId() { + return PORT_ID; + } + + /** + * @return The name of the field indicating the id of a queue attached to a + * port + */ + public static String fieldQueueId() { + return QUEUE_ID; + } + + /** + * @return The name of the field indicating the number of RX queues + * supported by a port + */ + public static String fieldNbRxQ() { + return NB_RX_Q; + } + + /** + * @return The name of the field indicating the number of TX queues + * supported by a port + */ + public static String fieldNbTxQ() { + return NB_TX_Q; + } + + /** + * @return The name of the field specifying the number of packets received + * in a burst + */ + public static String fieldNbRxPkts() { + return NB_RX; + } + + /** + * @return The field name specifying the number of packets transmitted as a + * burst + */ + public static String fieldNbPkts() { + return NB_PKTS; + } + + /** + * @return The field name specifying the number of packets transmitted as a + * burst in the profiling event + */ + public static String fieldNbTxPkts() { + return NB_TX; + } + + /** + * @return The field name containing a code value representing the success + * or the failure of the configuration operation + */ + public static String fieldRc() { + return RC; + } + + /** + * @return The name of the field specifying the number of bytes denoting the + * size of the received or transmitted burst + */ + public static String fieldSize() { + return SIZE; + } + + /** + * @return The name of the thread issuing the DPDK event + */ + public static String fieldThreadName() { + return THREAD_NAME; + } + + /** + * @return The identifier of the CPU on which the DPDK event was recorded + */ + public static String fieldCpuId() { + return CPU_ID; + } +} 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..e18ba2329 --- /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,199 @@ +/********************************************************************** + * 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.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.incubator.internal.dpdk.core.analysis.DpdkEthdevEventLayout; +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.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.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.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); + /** Packets and bytes reception label */ + protected static final String RX_LABEL = Objects.requireNonNull(Messages.DpdkEthdev_ThroughputDataProvider_TRAFFIC_RX); + /** Packets and bytes 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(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("A DPDK event (" + DpdkEthdevEventLayout.eventEthdevConfigure() + ") is missing", e); //$NON-NLS-1$ //$NON-NLS-2$ + } + return nodes; + } + + @Override + protected abstract String getTitle(); + + @Override + public abstract String getId(); + + @Override + protected abstract @Nullable Collection getYSeriesModels(ITmfStateSystem ss, Map fetchParameters, + @Nullable IProgressMonitor monitor) throws StateSystemDisposedException; + + /** + * 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..baf7b48e1 --- /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,65 @@ +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..bad972029 --- /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,61 @@ +/********************************************************************** + * 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.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 TmfAbstractAnalysisRequirement REQUIREMENT = new TmfAnalysisEventRequirement(ImmutableList.of( + DpdkEthdevEventLayout.eventEthdevConfigure()), PriorityLevel.AT_LEAST_ONE); + + @Override + protected ITmfStateProvider createStateProvider() { + ITmfTrace trace = checkNotNull(getTrace()); + + if (trace instanceof DpdkTrace) { + return new DpdkEthdevThroughputStateProvider(trace, 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..fec160852 --- /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 */ + 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..3ad1565f2 --- /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,200 @@ +/********************************************************************** + * 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 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; +import com.google.common.collect.Iterables; + +/** + * 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(Iterables.transform(builders, builder -> builder.build(Y_AXIS_DESCRIPTION))); + } + + /** + * 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..9c532faee --- /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,171 @@ +/********************************************************************** + * 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.DpdkEthdevEventLayout; +import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.IDpdkEventHandler; +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 process the DPDK 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); + + DpdkEthdevThroughputEventHandler() { + // do nothing + } + + /** + * Update the count of received or transmitted packets on the state system + * + * @param ts + * Timestamp to use for the state change + */ + static private void createPortQueues(ITmfStateSystemBuilder ssb, int queueSetQuark, int nbQueues, long ts) { + if (nbQueues <= 0) { + return; + } + + for (int i = 0; i < nbQueues; i++) { + ssb.getQuarkRelativeAndAdd(queueSetQuark, String.valueOf(i)); + } + } + + /** + * 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, DpdkEthdevEventLayout.fieldPortId()); + Objects.requireNonNull(portId); + + if (eventName.equals(DpdkEthdevEventLayout.eventEthdevConfigure())) { + handleConfigureEvent(ssb, event, portId, timestamp); + } else if (eventName.equals(DpdkEthdevEventLayout.eventEthdevRxBurstNonEmpty()) || + eventName.equals(DpdkEthdevEventLayout.eventProfileEthdevRxBurst())) { + handleRxBurstEvent(ssb, event, portId, timestamp, eventName); + } else if (eventName.equals(DpdkEthdevEventLayout.eventEthdevTxBurst()) || + eventName.equals(DpdkEthdevEventLayout.eventProfileEthdevTxBurst())) { + handleTxBurstEvent(ssb, event, portId, timestamp, eventName); + } else { + Activator.getInstance().logError("Unknown event (" + eventName + ") !!"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + private static void handleConfigureEvent(ITmfStateSystemBuilder ssb, ITmfEvent event, Integer portId, long timestamp) { + Integer rc = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldRc()); + Integer nbRxQueues = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldNbRxQ()); + Integer nbTxQueues = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldNbTxQ()); + + if (rc != null && rc == 0 && nbRxQueues != null && nbTxQueues != null) { + int portQuark = ssb.getQuarkAbsoluteAndAdd(ETH_NIC_PORTS, portId.toString()); + + int rxQueueQuark = ssb.getQuarkRelativeAndAdd(portQuark, RX_Q); + createPortQueues(ssb, rxQueueQuark, nbRxQueues, timestamp); + + int txQueueQuark = ssb.getQuarkRelativeAndAdd(portQuark, TX_Q); + createPortQueues(ssb, txQueueQuark, nbTxQueues, timestamp); + } else { + Activator.getInstance().logWarning(DpdkEthdevEventLayout.eventEthdevConfigure() + "event has invalid or missing fields"); //$NON-NLS-1$ + } + } + + private void handleRxBurstEvent(ITmfStateSystemBuilder ssb, ITmfEvent event, Integer portId, long timestamp, String eventName) { + Integer queueId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldQueueId()); + Integer nbRxPkts = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldNbRxPkts()); + + Integer size = null; + + boolean isProfileEvent = eventName.equals(DpdkEthdevEventLayout.eventProfileEthdevRxBurst()); + if (isProfileEvent) { + size = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldSize()); + } + + updateCounts(ssb, portId, Objects.requireNonNull(queueId), RX_Q, isProfileEvent, Objects.requireNonNull(nbRxPkts), size, timestamp); + } + + private void handleTxBurstEvent(ITmfStateSystemBuilder ssb, ITmfEvent event, Integer portId, long timestamp, String eventName) { + Integer nbTxPkts; + Integer size = null; + Integer queueId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldQueueId()); + + boolean isProfileEvent = eventName.equals(DpdkEthdevEventLayout.eventProfileEthdevTxBurst()); + if (isProfileEvent) { + nbTxPkts = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldNbTxPkts()); + size = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldSize()); + } else { + nbTxPkts = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldNbPkts()); + } + + updateCounts(ssb, portId, Objects.requireNonNull(queueId), TX_Q, isProfileEvent, Objects.requireNonNull(nbTxPkts), 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..6bb9a69b5 --- /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,220 @@ +/********************************************************************** + * 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 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; +import com.google.common.collect.Iterables; + +/** + * 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 { + + public static final String ID = "org.eclipse.tracecompass.incubator.dpdk.ethdev.throughput.pps.dataprovider"; //$NON-NLS-1$ + protected 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(Iterables.transform(builders, builder -> builder.build(Y_AXIS_DESCRIPTION))); + } + + /** + * 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 = entry.getKey(); + int quark = 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..4f1a0a0ab --- /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,86 @@ +/********************************************************************** + * 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.DpdkEthdevEventLayout; +import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.IDpdkEventHandler; +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; + + /** + * Constructor + * + * @param trace + * trace + * @param id + * id + */ + protected DpdkEthdevThroughputStateProvider(ITmfTrace trace, String id) { + super(trace, id); + } + + /** + * 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(), DpdkEthdevThroughputAnalysisModule.ID); + } + + @Override + protected @Nullable IDpdkEventHandler getEventHandler(String eventName) { + if (fEventNames == null) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + IDpdkEventHandler ethdevEventHandler = new DpdkEthdevThroughputEventHandler(); + builder.put(DpdkEthdevEventLayout.eventEthdevConfigure(), ethdevEventHandler); + builder.put(DpdkEthdevEventLayout.eventEthdevRxBurstNonEmpty(), ethdevEventHandler); + builder.put(DpdkEthdevEventLayout.eventEthdevTxBurst(), ethdevEventHandler); + /* + * The following events are emitted ONLY when the custom profiling + * library is pre-loaded. They allow to compute RX and TX traffic in + * bps + */ + builder.put(DpdkEthdevEventLayout.eventProfileEthdevTxBurst(), ethdevEventHandler); + builder.put(DpdkEthdevEventLayout.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/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..ae856ab11 --- /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,68 @@ +/********************************************************************** + * 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 java.util.List; + +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; + +/** + * 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 () -> { + return List.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..74d89cc5b --- /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,69 @@ +/********************************************************************** + * 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 java.util.List; + +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; + +/** + * 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 () -> { + return List.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 ba765574e7010280eb62bafe1358bc17611fc23a 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 + .../core/ethdev/spin/analysis/Attributes.java | 31 +++ .../spin/analysis/DpdkEthdevEventHandler.java | 79 ++++++ .../DpdkEthdevSpinAnalysisModule.java | 154 +++++++++++ .../analysis/DpdkEthdevSpinDataProvider.java | 240 ++++++++++++++++++ .../DpdkEthdevSpinDataProviderFactory.java | 55 ++++ .../analysis/DpdkEthdevSpinStateProvider.java | 78 ++++++ .../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 | 68 +++++ .../spin/ThreadSpinStatisticsViewer.java | 49 ++++ .../dpdk/ui/ethdev/spin/messages.properties | 16 ++ .../dpdk/ui/ethdev/spin/package-info.java | 11 + 17 files changed, 915 insertions(+) create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/Attributes.java create mode 100644 tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevEventHandler.java 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/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/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 632b95cb1..c8a4ab2fc 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 @@ -18,6 +18,7 @@ Require-Bundle: org.eclipse.ui, com.google.guava, 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/Attributes.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/Attributes.java new file mode 100644 index 000000000..c5352b992 --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/Attributes.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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 Attributes { + + /* First-level attributes */ + + /** Root attribute for DPDK Ethdev Nics */ + String POLL_THREADS = "Threads"; + /** Reception Queues */ + /** */ + String SPIN_STATUS = "Spin"; + /** */ + 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/DpdkEthdevEventHandler.java b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevEventHandler.java new file mode 100644 index 000000000..db0f3b225 --- /dev/null +++ b/tracetypes/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevEventHandler.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.DpdkEthdevEventLayout; +import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.IDpdkEventHandler; +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 DpdkEthdevEventHandler implements IDpdkEventHandler { + + private static final String POLL_THREADS = Objects.requireNonNull(Attributes.POLL_THREADS); + + DpdkEthdevEventHandler() { + // do nothing + } + + /** + * 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 + * time 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 = " + String.valueOf(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, DpdkEthdevEventLayout.fieldPortId()); + Integer queueId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldQueueId()); + String threadName = event.getContent().getFieldValue(String.class, DpdkEthdevEventLayout.fieldThreadName()); + Integer cpuId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldCpuId()); + + int threadQuark = ssb.getQuarkAbsoluteAndAdd(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(DpdkEthdevEventLayout.eventEthdevRxBurstEmpty())) { + ssb.modifyAttribute(ts, Attributes.SPIN_STATUS, queueQark); + } else if (eventName.equals(DpdkEthdevEventLayout.eventEthdevRxBurstNonEmpty())) { + ssb.modifyAttribute(ts, Attributes.ACTIVE_STATUS, queueQark); + } + } +} 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..64828ee80 --- /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,154 @@ +/********************************************************************** + * 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.jdt.annotation.NonNull; +import org.eclipse.tracecompass.incubator.internal.dpdk.core.Activator; +import org.eclipse.tracecompass.incubator.internal.dpdk.core.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.trace.TmfTrace; +import org.eclipse.tracecompass.tmf.core.util.Pair; + +import com.google.common.collect.ImmutableList; + +/** + * This analysis provides an estimation for the percentage of time a PMD thread + * was doing a real work (e.g., fetching and processing packets). It is based on + * two events: the "lib.ethdev.rx.burst.empty" event denotes an empty poll, and + * the "lib.ethdev.rx.burst.nonempty" event denotes a successful poll, where one + * or many packets are retrieved. + * + * @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 TmfAbstractAnalysisRequirement REQUIREMENT = new TmfAnalysisEventRequirement(ImmutableList.of( + DpdkEthdevEventLayout.eventEthdevRxBurstEmpty(), DpdkEthdevEventLayout.eventEthdevRxBurstNonEmpty()), PriorityLevel.AT_LEAST_ONE); + + @Override + protected ITmfStateProvider createStateProvider() { + ITmfTrace trace = checkNotNull(getTrace()); + + if (trace instanceof TmfTrace) { + return new DpdkEthdevSpinStateProvider(trace, ID); + } + + throw new IllegalStateException(); + } + + @Override + public Iterable getAnalysisRequirements() { + return Collections.singleton(REQUIREMENT); + } + + /** + * Calculates thread usage within a specific time interval + * + * @param threads + * Identifiers of the target threads + * @param start + * Start timestamp + * @param end + * End timestamp + * @return A map of Thread names -> time spent in empty or active iterations + */ + public Map> getThreadUsageInRange(Set<@NonNull Integer> 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 threadsNode = threadSs.getQuarkAbsolute(Attributes.POLL_THREADS); + for (int threadNode : threadSs.getSubAttributes(threadsNode, false)) { + if (!threads.contains(threadNode)) { + continue; + } + + String curThreadName = threadSs.getAttributeName(threadNode); + long countActive = 0; + long countSpin = 0; + for (int queueNode : threadSs.getSubAttributes(threadNode, false)) { + long ts = startTime; + do { + ITmfStateInterval stateInterval = threadSs.querySingleState(ts, queueNode); + Object stateValue = stateInterval.getStateValue().unboxValue(); + long stateStart = stateInterval.getStartTime(); + long stateEnd = stateInterval.getEndTime(); + + if (stateValue != null) { + String threadState = (String) stateValue; + if (threadState.equals(Attributes.ACTIVE_STATUS)) { + countActive += interpolateCount(startTime, endTime, stateEnd, stateStart); + } else if (threadState.equals(Attributes.SPIN_STATUS)) { + countSpin += interpolateCount(startTime, endTime, stateEnd, stateStart); + } + } + ts = Math.min(stateEnd, endTime) + 1; + } while (ts < endTime); + } + + map.put(curThreadName, new Pair<>(countActive, countSpin)); + } + + } catch (TimeRangeException | AttributeNotFoundException | StateSystemDisposedException e) { + Activator.getInstance().logError(e.getMessage()); + } + + return map; + } + + private static long interpolateCount(long startTime, long endTime, long spinningEnd, long spinningStart) { + + long count = spinningEnd - spinningStart; + + /* sanity check */ + if (count > 0) { + if (startTime > spinningStart) { + count -= (startTime - spinningStart); + } + + if (endTime < spinningEnd) { + count -= (spinningEnd - endTime); + } + } + return count; + } +} 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..06e506709 --- /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,240 @@ +/********************************************************************** + * 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}. + */ + protected 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(Attributes.POLL_THREADS); + long threadsId = getId(threadsQuark); + nodes.add(new TmfTreeDataModel(threadsId, rootId, Collections.singletonList(THREADS_LABEL), false, null)); + + for (Integer threadQuark : ss.getQuarks(Attributes.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 " + Attributes.POLL_THREADS); //$NON-NLS-1$ + } + return new TmfTreeModel<>(Collections.emptyList(), nodes); + } + + private static long getInitialPrevTime(SelectionTimeQueryFilter filter) { + /* + * Subtract from start time the same interval as the interval from start + * time to next time, ignoring duplicates in the times requested. + */ + 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) { + return null; + } + + if (getSelectedEntries(filter).isEmpty()) { + return null; + } + + Set selectedThreads = new HashSet<>(); + + long[] xValues = filter.getTimesRequested(); + Map selectedThreadValues = new HashMap<>(); + + for (Entry entry : getSelectedEntries(filter).entrySet()) { + int quark = Objects.requireNonNull(entry.getValue()); + + if ((quark == ITmfStateSystem.ROOT_ATTRIBUTE) || + (ss.getParentAttributeQuark(quark) == ITmfStateSystem.ROOT_ATTRIBUTE)) { + continue; + } + selectedThreads.add(quark); + String name = ss.getAttributeName(quark); + selectedThreadValues.put(name, new YModel(entry.getKey(), getTrace().getName() + '/' + name, new double[xValues.length])); + } + + long prevTime = Math.max(getInitialPrevTime(filter), ss.getStartTime()); + long currentEnd = ss.getCurrentEndTime(); + + for (int i = 0; i < xValues.length; i++) { + long time = xValues[i]; + if (time < ss.getStartTime() || time > currentEnd) { + prevTime = time; + continue; + } + if (prevTime < time) { + Map> threadUsageMap = getAnalysisModule().getThreadUsageInRange(selectedThreads, prevTime, time); + for (Entry> entry : threadUsageMap.entrySet()) { + IYModel values = selectedThreadValues.get(entry.getKey()/* + * threadName + */); + if (values != null) { + long countActive = Objects.requireNonNull(entry.getValue()).getFirst(); + long countSpin = Objects.requireNonNull(entry.getValue()).getSecond(); + values.getData()[i] = getPercentageValue(countActive, countSpin); + } + } + } else if (i > 0) { + /* In case of duplicate time xValue copy previous yValues */ + for (IYModel values : selectedThreadValues.values()) { + values.getData()[i] = values.getData()[i - 1]; + } + } + prevTime = time; + if (monitor != null && monitor.isCanceled()) { + return null; + } + } + + ImmutableList.Builder ySeries = ImmutableList.builder(); + for (IYModel entry : selectedThreadValues.values()) { + ySeries.add(entry); + } + + return ySeries.build(); + } + + private static double getPercentageValue(long countActive, long countSpin) { + double value = (countActive + countSpin == 0) ? 0.0 : (double) countActive * 100 / (countActive + countSpin); + return value; + } + + @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/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..6392ad6fc --- /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,78 @@ +/********************************************************************** + * 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.DpdkEthdevEventLayout; +import org.eclipse.tracecompass.incubator.internal.dpdk.core.analysis.IDpdkEventHandler; +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; + + /** + * Constructor + * + * @param trace + * trace + * @param id + * id + */ + protected DpdkEthdevSpinStateProvider(ITmfTrace trace, String id) { + super(trace, id); + } + + /** + * 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(), DpdkEthdevSpinAnalysisModule.ID); + } + + @Override + protected @Nullable IDpdkEventHandler getEventHandler(String eventName) { + if (fEventNames == null) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + IDpdkEventHandler ethdevEventHandler = new DpdkEthdevEventHandler(); + builder.put(DpdkEthdevEventLayout.eventEthdevRxBurstEmpty(), ethdevEventHandler); + builder.put(DpdkEthdevEventLayout.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..c8a9e3306 --- /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; + /** 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..93c8764cf --- /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,68 @@ +/********************************************************************** + * 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 java.util.List; + +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; + +/** + * 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 () -> { + return List.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 e471f9e747c89843b7043c9da212f128ca48a43d 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 | 3 +- .../plugin.xml | 7 + .../DpdkPollDistributionAnalysis.java | 224 ++++++++++++++++++ .../poll/distribution/analysis/Messages.java | 46 ++++ .../distribution/analysis/messages.properties | 14 ++ 5 files changed, 293 insertions(+), 1 deletion(-) 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 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 c8a4ab2fc..b61c61721 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 @@ -16,7 +16,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.tracecompass.tmf.ctf.core, org.eclipse.tracecompass.analysis.os.linux.core, com.google.guava, - org.eclipse.jdt.annotation;bundle-version="2.2.400" + org.eclipse.jdt.annotation;bundle-version="2.2.400", + org.eclipse.tracecompass.analysis.lami.core 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, 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() ? true : false; + } + 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, Messages.getMessage(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(Messages.getMessage(Messages.EthdevPollDistribution_CountLabel), DpdkEthdevEventLayout.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 static 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 static void processEvent(ITmfEvent event, Predicate filterPredicate, + Map> pollAspectCounts) { + + if (event.getName().equals(DpdkEthdevEventLayout.eventEthdevRxBurstNonEmpty()) + && filterPredicate.test(event)) { + Integer nbRxPkts = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldNbRxPkts()); + Integer portId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldPortId()); + Integer queueId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.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, Long::sum); + } + } + } + } + + private @NonNull List convertToLamiTables(TmfTimeRange timeRange, + Map> pollCountPerQueue) { + List results = new ArrayList<>(); + for (Map.Entry> entry : pollCountPerQueue.entrySet()) { + String queueName = entry.getKey(); + Map dataSet = entry.getValue(); + + List tableEntries = dataSet.entrySet().stream() + .map(e -> new LamiTableEntry(Arrays.asList( + new LamiString(e.getKey().toString()), + new LamiLongNumber(e.getValue())))) + .collect(Collectors.toList()); + + List<@NonNull LamiTableEntryAspect> tableAspects = Arrays.asList( + new LamiCategoryAspect(Messages.EthdevPollDistribution_NumberOfPacketsLabel, 0), + new LamiCountAspect(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 @NonNull 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..bd2dde63d --- /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,46 @@ +/******************************************************************************* + * 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.NonNull; +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 @NonNull String getMessage(@Nullable String msg) { + if (msg == null) { + return ""; //$NON-NLS-1$ + } + return msg; + } + + 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 From 7a8e39dea123d38ecb3f95eab764981a3aa9b889 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 | 260 ++++++++++++++++++ .../ethdev/poll/stats/analysis/Messages.java | 52 ++++ .../poll/stats/analysis/messages.properties | 19 ++ 4 files changed, 335 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 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..f2f69632d --- /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,260 @@ +/******************************************************************************* + * 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.math.BigDecimal; +import java.math.RoundingMode; +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.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.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.incubator.dpdk.core.trace.DpdkTrace; +import org.eclipse.tracecompass.incubator.internal.dpdk.core.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; + + /** + * Constructor + */ + public DpdkPollStatsAnalysis() { + super(Messages.getMessage(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() ? true : false; + } + 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<@NonNull String, Map<@NonNull String, List>> 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 static TmfEventRequest createEventRequest(ITmfTrace trace, TmfTimeRange timeRange, Map<@NonNull String, Map<@NonNull String, List>> 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 static void processEvent(ITmfEvent event, Map<@NonNull String, Map<@NonNull String, List>> pollCountsMap) { + if (!event.getName().equals(DpdkEthdevEventLayout.eventEthdevRxBurstNonEmpty())) { + return; + } + + Integer nbRxPkts = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldNbRxPkts()); + Integer portId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldPortId()); + Integer queueId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldQueueId()); + String threadName = event.getContent().getFieldValue(String.class, DpdkEthdevEventLayout.fieldThreadName()); + + if (nbRxPkts == null || portId == null || queueId == null || threadName == null) { + return; + } + + // update the poll count from a queue perspective + String queueName = "P" + portId + "/Q" + queueId; //$NON-NLS-1$ //$NON-NLS-2$ + updatePollCountsMap(pollCountsMap, Messages.getMessage(Messages.EthdevPollStats_QueueLabel), queueName, nbRxPkts); + + // update the poll count from a thread perspective + updatePollCountsMap(pollCountsMap, Messages.getMessage(Messages.EthdevPollStats_ThreadLabel), threadName, nbRxPkts); + } + + private static void updatePollCountsMap(Map<@NonNull String, Map<@NonNull String, List>> pollCountsMap, @NonNull String aspectName, @NonNull String key, Integer nbRxPkts) { + Map<@NonNull String, List> 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 @NonNull List convertToLamiTables(TmfTimeRange timeRange, + Map<@NonNull String, Map<@NonNull String, List>> pollAspectCounts) { + List results = new ArrayList<>(); + for (Entry<@NonNull String, Map<@NonNull String, List>> entry : pollAspectCounts.entrySet()) { + + Map<@NonNull String, List> dataSet = entry.getValue(); + List entries = new ArrayList<>(); + + for (Entry<@NonNull String, List> element : dataSet.entrySet()) { + /* + * Calculate the number of successful polls, along with the + * minimum and maximum polls values + */ + int nbSuccessfulPolls = element.getValue().size(); + int minPollValue = Collections.min(element.getValue()); + int maxPollValue = Collections.max(element.getValue()); + + // calculate the mean and the standard deviation + double avgPollValue = element.getValue().stream().mapToInt(i -> i).average().orElse(0); + double sd = element.getValue().stream().mapToDouble(val -> Math.pow(val - avgPollValue, 2)).sum(); + double std = Math.sqrt(sd / element.getValue().size()); + + BigDecimal bd = new BigDecimal(std).setScale(2, RoundingMode.HALF_UP); + double rounded = bd.doubleValue(); + + List<@NonNull LamiData> data = Arrays.asList( + new LamiString(element.getKey()), + new LamiLongNumber((long) minPollValue), + new LamiLongNumber((long) maxPollValue), + new LamiLongNumber((long) avgPollValue), + new LamiDoubleNumber(rounded), + new LamiLongNumber((long) nbSuccessfulPolls)); + + entries.add(new LamiTableEntry(data)); + } + + List<@NonNull LamiTableEntryAspect> tableAspects = Arrays.asList(new LamiCategoryAspect(entry.getKey(), 0), + new LamiCountAspect(Messages.EthdevPollStats_MinimumValueLabel, 1), + new LamiCountAspect(Messages.EthdevPollStats_MaximumValueLabel, 2), + new LamiCountAspect(Messages.EthdevPollStats_AverageValueLabel, 3), + new LamiCountAspect(Messages.EthdevPollStats_StandardDeviationLabel, 4), + new LamiCountAspect(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(@NonNull String element) { + fElement = element; + } + + @Override + public @NonNull 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..d75543990 --- /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,52 @@ +/******************************************************************************* + * 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.NonNull; +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 @NonNull String getMessage(@Nullable String msg) { + if (msg == null) { + return ""; //$NON-NLS-1$ + } + return msg; + } + + 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