From: Alexandre Montplaisir Date: Wed, 16 Nov 2016 20:52:35 +0000 (-0500) Subject: os.linux: Add some util methods to check thread state X-Git-Url: http://git.efficios.com/?a=commitdiff_plain;ds=sidebyside;h=3fef638debb47795b0eea5c98e2db0a93a4b229c;p=deliverable%2Ftracecompass.git os.linux: Add some util methods to check thread state This can be used by view filters to show either only active threads, or threads on a given subset of CPUs. Change-Id: I3887868d3c6c97009f772c1088274eda2bfbdd65 Signed-off-by: Alexandre Montplaisir --- diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.core.tests/src/org/eclipse/tracecompass/analysis/os/linux/core/tests/kernel/KernelThreadInformationProviderTest.java b/analysis/org.eclipse.tracecompass.analysis.os.linux.core.tests/src/org/eclipse/tracecompass/analysis/os/linux/core/tests/kernel/KernelThreadInformationProviderTest.java index b22ab40bc2..f355dc7856 100644 --- a/analysis/org.eclipse.tracecompass.analysis.os.linux.core.tests/src/org/eclipse/tracecompass/analysis/os/linux/core/tests/kernel/KernelThreadInformationProviderTest.java +++ b/analysis/org.eclipse.tracecompass.analysis.os.linux.core.tests/src/org/eclipse/tracecompass/analysis/os/linux/core/tests/kernel/KernelThreadInformationProviderTest.java @@ -20,7 +20,9 @@ import static org.junit.Assert.assertTrue; import java.io.File; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; @@ -158,6 +160,55 @@ public class KernelThreadInformationProviderTest { } + /** + * Test the {@link KernelThreadInformationProvider#getThreadsOfCpus} method. + */ + @Test + public void testGetThreadsOfCpus() { + KernelAnalysisModule module = checkNotNull(fModule); + final long start = 45L; + final long end = 65L; + + /* + * The test trace considers all scheduled out processes to remain in the + * "wait for cpu" state. In this particular case, the util method means + * "has ever run on CPU X". + */ + + Set tids = KernelThreadInformationProvider.getThreadsOfCpus(module, Collections.singleton(0L), start, end); + assertNotNull(tids); + /* + * Only threads 11 and 100 should be present, due to the sched_switch at + * t=35. + */ + assertEquals(ImmutableSet.of(11, 100), tids); + + tids = KernelThreadInformationProvider.getThreadsOfCpus(module, Collections.singleton(1L), start, end); + assertNotNull(tids); + /* Only threads 10, 20, 21, 30 get scheduled on CPU 1. */ + assertEquals(ImmutableSet.of(10, 20, 21, 30), tids); + } + + /** + * Test the {@link KernelThreadInformationProvider#getActiveThreadsForRange} + * method. + */ + @Test + public void testIsThreadActiveRange() { + KernelAnalysisModule module = checkNotNull(fModule); + final long start = 45L; + final long end = 65L; + + /* Thread 30 should be active in the range */ + Set tids = KernelThreadInformationProvider.getActiveThreadsForRange(module, start, end); + assertNotNull(tids); + assertTrue(tids.contains(30)); + + // TODO Check for non-active states too. Unfortunately the test trace + // wrongly considers all processes to be in a active (value=5) state all + // the time. + } + /** * Test the * {@link KernelThreadInformationProvider#getParentPid(KernelAnalysisModule, Integer, long)} diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/META-INF/MANIFEST.MF b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/META-INF/MANIFEST.MF index 4945e61630..8655f4095d 100644 --- a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/META-INF/MANIFEST.MF +++ b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-Vendor: %Bundle-Vendor -Bundle-Version: 2.1.0.qualifier +Bundle-Version: 2.2.0.qualifier Bundle-Localization: plugin Bundle-SymbolicName: org.eclipse.tracecompass.analysis.os.linux.core;singleton:=true Bundle-Activator: org.eclipse.tracecompass.internal.analysis.os.linux.core.Activator diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/analysis/os/linux/core/kernel/KernelThreadInformationProvider.java b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/analysis/os/linux/core/kernel/KernelThreadInformationProvider.java index dd4e20f691..b1b80e3aa9 100644 --- a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/analysis/os/linux/core/kernel/KernelThreadInformationProvider.java +++ b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/analysis/os/linux/core/kernel/KernelThreadInformationProvider.java @@ -14,9 +14,12 @@ package org.eclipse.tracecompass.analysis.os.linux.core.kernel; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; +import java.util.function.Predicate; +import java.util.stream.Collectors; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.annotation.NonNull; @@ -31,6 +34,8 @@ import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue.Type; +import com.google.common.collect.ImmutableSet; + /** * Information provider utility class that retrieves thread-related information * from a Linux Kernel Analysis @@ -75,6 +80,161 @@ public final class KernelThreadInformationProvider { return null; } + /** + * The the threads that have been scheduled on the given CPU(s), for the + * given time range. Threads with TID 0 (swapper threads) will never be + * included. + * + * @param module + * The kernel analysis module to query + * @param cpus + * The list of cpus + * @param rangeStart + * The start of the time range + * @param rangeEnd + * The end of the time range + * @return A set of all the thread IDs that are run on said CPUs on the time + * range. Empty set if there is no thread on the CPUs in this time + * range. Null if the information is not available. + * @since 2.2 + */ + public static @Nullable Set getThreadsOfCpus(KernelAnalysisModule module, Collection cpus, long rangeStart, long rangeEnd) { + ITmfStateSystem ss = module.getStateSystem(); + if (ss == null) { + return null; + } + + Set uniqueCpus = (cpus instanceof Set ? (Set) cpus : ImmutableSet.copyOf(cpus)); + + int threadsQuark = ss.optQuarkAbsolute(Attributes.THREADS); + if (threadsQuark == ITmfStateSystem.INVALID_ATTRIBUTE) { + return null; + } + + List threadQuarks = ss.getSubAttributes(threadsQuark, false); + return threadQuarks.stream() + /* + * Keep only the quarks of threads that are on at least one of the + * wanted CPUs' run queue. + */ + .filter(threadQuark -> { + int threadCurrentCpuQuark = ss.optQuarkRelative(threadQuark, Attributes.CURRENT_CPU_RQ); + if (threadCurrentCpuQuark == ITmfStateSystem.INVALID_ATTRIBUTE) { + return false; + } + + Iterator it = StateSystemUtils.getIteratorOverQuark(ss, threadCurrentCpuQuark, rangeStart, rangeEnd); + while (it.hasNext()) { + ITmfStateInterval interval = it.next(); + ITmfStateValue value = interval.getStateValue(); + if (!value.isNull() && uniqueCpus.contains(Long.valueOf(value.unboxLong()))) { + return true; + } + } + /* The thread was never on any of the requested CPUs. */ + return false; + }) + + /* Convert the thread quarks to their corresponding TIDs */ + .map(ss::getAttributeName) + /* Ignore swapper threads */ + .filter(attribName -> !attribName.startsWith(Attributes.THREAD_0_PREFIX)) + .map(Integer::parseInt) + .collect(Collectors.toSet()); + } + + /** + * Predicate indicating if a thread state value is considered active or not. + */ + private static final Predicate IS_STATE_VALUE_ACTIVE = stateValue -> { + if (stateValue.isNull() || + stateValue.equals(StateValues.PROCESS_STATUS_UNKNOWN_VALUE) || + stateValue.equals(StateValues.PROCESS_STATUS_WAIT_BLOCKED_VALUE) || + stateValue.equals(StateValues.PROCESS_STATUS_WAIT_UNKNOWN_VALUE)) { + return false; + } + return true; + }; + + /** + * Return all the threads that are considered active in the given time + * range. Threads with TID 0 (swapper threads) will never be included. + * + * @param module + * The kernel analysis module to query + * @param rangeStart + * The start of the time range + * @param rangeEnd + * The end of the time range + * @return A set of all the thread IDs that are considered active in the + * time range. Empty set if there are none. Null if the information + * is not available. + * @since 2.2 + */ + public static @Nullable Set getActiveThreadsForRange(KernelAnalysisModule module, long rangeStart, long rangeEnd) { + ITmfStateSystem ss = module.getStateSystem(); + if (ss == null) { + return null; + } + + List fullQueryAtStart; + int threadsQuark; + try { + fullQueryAtStart = ss.queryFullState(rangeStart); + threadsQuark = ss.getQuarkAbsolute(Attributes.THREADS); + } catch (AttributeNotFoundException | StateSystemDisposedException e) { + return null; + } + + + List threadQuarks = ss.getSubAttributes(threadsQuark, false); + return threadQuarks.stream() + /* + * Keep only the quarks of threads that are considered active at + * some point in the time range. + */ + .filter(threadQuark -> { + /* + * If the thread was active at range start, we can already + * consider it active. + */ + ITmfStateInterval intervalAtStart = fullQueryAtStart.get(threadQuark); + if (IS_STATE_VALUE_ACTIVE.test(intervalAtStart.getStateValue())) { + return true; + } + + /* + * If it was inactive, and it remains in the exact same + * state for the whole time range, we can conclude it is + * inactive for the whole range. + * + * Note this will not catch cases where the threads goes + * from one inactive state to another, this will be found + * with the range query below. + */ + if (intervalAtStart.getEndTime() >= rangeEnd) { + return false; + } + + Iterator it = StateSystemUtils.getIteratorOverQuark(ss, threadQuark, rangeStart, rangeEnd); + while (it.hasNext()) { + ITmfStateInterval interval = it.next(); + if (IS_STATE_VALUE_ACTIVE.test(interval.getStateValue())) { + return true; + } + } + /* We haven't found an active state value in the whole range. */ + return false; + }) + + /* Convert the thread quarks to their corresponding TIDs */ + .map(ss::getAttributeName) + /* Ignore swapper threads */ + .filter(attribName -> !attribName.startsWith(Attributes.THREAD_0_PREFIX)) + .map(Integer::parseInt) + .collect(Collectors.toSet()); + } + /** * Get the TIDs of the threads from an analysis *