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;
}
+ /**
+ * 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<Integer> 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<Integer> 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)}
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;
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
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<Integer> getThreadsOfCpus(KernelAnalysisModule module, Collection<Long> cpus, long rangeStart, long rangeEnd) {
+ ITmfStateSystem ss = module.getStateSystem();
+ if (ss == null) {
+ return null;
+ }
+
+ Set<Long> uniqueCpus = (cpus instanceof Set ? (Set<Long>) cpus : ImmutableSet.copyOf(cpus));
+
+ int threadsQuark = ss.optQuarkAbsolute(Attributes.THREADS);
+ if (threadsQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
+ return null;
+ }
+
+ List<Integer> 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<ITmfStateInterval> 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<ITmfStateValue> 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<Integer> getActiveThreadsForRange(KernelAnalysisModule module, long rangeStart, long rangeEnd) {
+ ITmfStateSystem ss = module.getStateSystem();
+ if (ss == null) {
+ return null;
+ }
+
+ List<ITmfStateInterval> fullQueryAtStart;
+ int threadsQuark;
+ try {
+ fullQueryAtStart = ss.queryFullState(rangeStart);
+ threadsQuark = ss.getQuarkAbsolute(Attributes.THREADS);
+ } catch (AttributeNotFoundException | StateSystemDisposedException e) {
+ return null;
+ }
+
+
+ List<Integer> 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<ITmfStateInterval> 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
*