-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dpdk: add busyness analysis for ethdev PMD threads
PMD threads continuously poll NIC queues, leading to constant 100% CPU usage. This analysis provides a rough estimation of how busy PMD threads are with actual work, by comparing the times they successfully retrieve packets from the NIC queue versus the times they are merely spinning without processing any packets. Signed-off-by: Adel Belkhiri <[email protected]>
- Loading branch information
1 parent
d7e345a
commit 454e2a5
Showing
17 changed files
with
967 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
198 changes: 198 additions & 0 deletions
198
...mpass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAnalysisModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<TmfAbstractAnalysisRequirement> 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<String, Pair<Long, Long>> calculateThreadStateDurations(Set<Integer> threads, long start, long end) { | ||
Map<String, Pair<Long, Long>> 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; | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...cecompass/incubator/internal/dpdk/core/ethdev/spin/analysis/DpdkEthdevSpinAttributes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"; | ||
} |
Oops, something went wrong.