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