From 1a11873388fd28e34b5c600d55ec9ab9890e886c Mon Sep 17 00:00:00 2001 From: Adel Belkhiri Date: Tue, 15 Oct 2024 18:06:07 -0400 Subject: [PATCH] dpdk: add packet distribution analysis for ethdev Introduce packet distribution analysis for PMD threads based on ethdev library. This analysis computes the distribution of packets retrieved in a single rte_eth_rx_burst() call, on a per-queue basis. Signed-off-by: Adel Belkhiri --- .../META-INF/MANIFEST.MF | 3 +- .../plugin.xml | 7 + .../DpdkPollDistributionAnalysis.java | 224 ++++++++++++++++++ .../poll/distribution/analysis/Messages.java | 45 ++++ .../distribution/analysis/messages.properties | 14 ++ 5 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 analyses/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/DpdkPollDistributionAnalysis.java create mode 100644 analyses/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/Messages.java create mode 100644 analyses/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/messages.properties diff --git a/analyses/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF b/analyses/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF index c6952c0fb..e998a62b5 100644 --- a/analyses/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF +++ b/analyses/org.eclipse.tracecompass.incubator.dpdk.core/META-INF/MANIFEST.MF @@ -16,7 +16,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.tracecompass.tmf.ctf.core, org.eclipse.tracecompass.analysis.os.linux.core, org.eclipse.jdt.annotation;bundle-version="2.2.400", - com.google.guava + com.google.guava, + org.eclipse.tracecompass.analysis.lami.core Export-Package: org.eclipse.tracecompass.incubator.dpdk.core.trace, org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.spin.analysis, org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.throughput.analysis, diff --git a/analyses/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml b/analyses/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml index 694e32c56..560ab3317 100644 --- a/analyses/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml +++ b/analyses/org.eclipse.tracecompass.incubator.dpdk.core/plugin.xml @@ -31,6 +31,13 @@ + + + + true, Collections.emptyList()); + } + + @Override + protected synchronized void initialize() { + // do nothing + } + + @Override + public boolean canExecute(ITmfTrace trace) { + if (trace instanceof DpdkTrace) { + return ((DpdkTrace) trace).validate(null, trace.getPath()).isOK() ? true : false; + } + return false; + } + + private static int workRemaining(ITmfTrace trace) { + return (int) Math.min(trace.getNbEvents() / (PROGRESS_INTERVAL + 1), Integer.MAX_VALUE); + } + + @Override + public List execute(ITmfTrace trace, @Nullable TmfTimeRange timeRange, String extraParamsString, IProgressMonitor monitor) throws CoreException { + AtomicLong done = new AtomicLong(); + Map> pollCountPerQueue = new TreeMap<>(); + TmfTimeRange adjustedTimeRange = timeRange == null ? TmfTimeRange.ETERNITY : timeRange; + SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.getMessage(Messages.EthdevPollDistribution_AnalysisName), workRemaining(trace)); + + /* + * Handle the filter in case the user indicates a specific port to + * process its events + */ + TmfFilterMatchesNode filter = new TmfFilterMatchesNode(null); + filter.setEventAspect(new TmfContentFieldAspect(Messages.getMessage(Messages.EthdevPollDistribution_CountLabel), DpdkEthdevEventLayout.fieldPortId())); + filter.setRegex(extraParamsString); + Predicate filterPred = (event -> extraParamsString.isEmpty() || filter.matches(event)); + + // create and send the event request + TmfEventRequest eventRequest = createEventRequest(trace, adjustedTimeRange, filterPred, + pollCountPerQueue, subMonitor, done); + trace.sendRequest(eventRequest); + + try { + eventRequest.waitForCompletion(); + return convertToLamiTables(adjustedTimeRange, pollCountPerQueue); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return Collections.emptyList(); + } + } + + private static TmfEventRequest createEventRequest(ITmfTrace trace, TmfTimeRange timeRange, Predicate filterPredicate, Map> pollAspectCounts, SubMonitor monitor, AtomicLong nbProcessevents) { + return new TmfEventRequest(ITmfEvent.class, timeRange, 0, Integer.MAX_VALUE, ExecutionType.BACKGROUND) { + @Override + public void handleData(ITmfEvent event) { + if (monitor.isCanceled()) { + cancel(); + return; + } + + // process events to compute RX polls distribution + processEvent(event, filterPredicate, pollAspectCounts); + + if ((nbProcessevents.incrementAndGet() & PROGRESS_INTERVAL) == 0) { + monitor.setWorkRemaining(workRemaining(trace)); + monitor.worked(1); + monitor.setTaskName(String.format("DPDK Polls Distribution Analysis (%s events processed)", //$NON-NLS-1$ + NumberFormat.getInstance().format(nbProcessevents.get()))); + } + } + }; + } + + private static void processEvent(ITmfEvent event, Predicate filterPredicate, + Map> pollAspectCounts) { + + if (event.getName().equals(DpdkEthdevEventLayout.eventEthdevRxBurstNonEmpty()) + && filterPredicate.test(event)) { + Integer nbRxPkts = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldNbRxPkts()); + Integer portId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldPortId()); + Integer queueId = event.getContent().getFieldValue(Integer.class, DpdkEthdevEventLayout.fieldQueueId()); + + if (nbRxPkts != null && portId != null && queueId != null) { + String queueName = "P" + portId + "/Q" + queueId; //$NON-NLS-1$ //$NON-NLS-2$ + Map dataSet = pollAspectCounts.computeIfAbsent(queueName, k -> new HashMap<>()); + if (dataSet.size() < MEMORY_SANITY_LIMIT) { + dataSet.merge(nbRxPkts, 1L, Long::sum); + } + } + } + } + + private @NonNull List convertToLamiTables(TmfTimeRange timeRange, + Map> pollCountPerQueue) { + List results = new ArrayList<>(); + for (Map.Entry> entry : pollCountPerQueue.entrySet()) { + String queueName = entry.getKey(); + Map dataSet = entry.getValue(); + + List tableEntries = dataSet.entrySet().stream() + .map(e -> new LamiTableEntry(Arrays.asList( + new LamiString(e.getKey().toString()), + new LamiLongNumber(e.getValue())))) + .collect(Collectors.toList()); + + List<@NonNull LamiTableEntryAspect> tableAspects = Arrays.asList( + new LamiCategoryAspect(Messages.EthdevPollDistribution_NumberOfPacketsLabel, 0), + new LamiCountAspect(Messages.EthdevPollDistribution_CountLabel, 1)); + + LamiTableClass tableClass = new LamiTableClass(queueName, queueName, tableAspects, Collections.emptySet()); + results.add(new LamiResultTable(createTimeRange(timeRange), tableClass, tableEntries)); + } + return results; + } + + /** + * Count aspect, generic + * + */ + private final class LamiCountAspect extends LamiGenericAspect { + private LamiCountAspect(String name, int column) { + super(name, null, column, true, false); + } + } + + /** + * Category aspect, generic + * + */ + private final class LamiCategoryAspect extends LamiGenericAspect { + private LamiCategoryAspect(String name, int column) { + super(name, null, column, false, false); + } + } + + /** + * TODO: move to LAMI + */ + private static LamiTimeRange createTimeRange(TmfTimeRange timeRange) { + return new LamiTimeRange(new LamiTimestamp(timeRange.getStartTime().toNanos()), new LamiTimestamp(timeRange.getEndTime().toNanos())); + } + + /** + * TODO: LamiString in LAMI is private + */ + private final class LamiString extends LamiData { + private final String fElement; + + private LamiString(String element) { + fElement = element; + } + + @Override + public @NonNull String toString() { + return fElement; + } + } +} diff --git a/analyses/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/Messages.java b/analyses/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..4ea7eba01 --- /dev/null +++ b/analyses/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/Messages.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2024 École Polytechnique de Montréal + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License 2.0 which + * accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.poll.distribution.analysis; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.osgi.util.NLS; + +/** + * Messages for the {@link DpdkPollDistributionAnalysis} on-demand analysis + * + * @author Adel Belkhiri + */ +@SuppressWarnings("javadoc") +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.tracecompass.incubator.internal.dpdk.core.ethdev.poll.distribution.analysis.messages"; //$NON-NLS-1$ + + public static @Nullable String EthdevPollDistribution_AnalysisName; + public static @Nullable String EthdevPollDistribution_NumberOfPacketsLabel; + public static @Nullable String EthdevPollDistribution_CountLabel; + + static @NonNull String getMessage(@Nullable String msg) { + if (msg == null) { + return ""; //$NON-NLS-1$ + } + return msg; + } + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/analyses/org.eclipse.tracecompass.incubator.dpdk.core/src/org/eclipse/tracecompass/incubator/internal/dpdk/core/ethdev/poll/distribution/analysis/messages.properties b/analyses/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/analyses/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