os.linux: Add some util methods to check thread state
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.os.linux.core / src / org / eclipse / tracecompass / analysis / os / linux / core / kernel / KernelThreadInformationProvider.java
CommitLineData
50a47aa6 1/*******************************************************************************
ed902a2b 2 * Copyright (c) 2014, 2015 École Polytechnique de Montréal
50a47aa6
GB
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
12
0f7a12d3 13package org.eclipse.tracecompass.analysis.os.linux.core.kernel;
50a47aa6
GB
14
15import java.util.Collection;
16import java.util.Collections;
3fef638d 17import java.util.Iterator;
50a47aa6
GB
18import java.util.List;
19import java.util.Set;
20import java.util.TreeSet;
3fef638d
AM
21import java.util.function.Predicate;
22import java.util.stream.Collectors;
50a47aa6
GB
23
24import org.eclipse.core.runtime.IProgressMonitor;
4c4e2816 25import org.eclipse.jdt.annotation.NonNull;
50a47aa6 26import org.eclipse.jdt.annotation.Nullable;
f69045e2 27import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes;
50a47aa6
GB
28import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
29import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
30import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
31import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
32import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
33import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
34import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
f20f0966 35import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue.Type;
50a47aa6 36
3fef638d
AM
37import com.google.common.collect.ImmutableSet;
38
50a47aa6
GB
39/**
40 * Information provider utility class that retrieves thread-related information
41 * from a Linux Kernel Analysis
42 *
43 * @author Geneviève Bastien
0f7a12d3 44 * @since 2.0
50a47aa6 45 */
e363eae1 46public final class KernelThreadInformationProvider {
50a47aa6 47
e363eae1 48 private KernelThreadInformationProvider() {
50a47aa6
GB
49 }
50
51 /**
52 * Get the ID of the thread running on the CPU at time ts
53 *
54 * TODO: This method may later be replaced by an aspect, when the aspect can
55 * resolve to something that is not an event
56 *
57 * @param module
1add07ef 58 * The kernel analysis instance to run this method on
50a47aa6
GB
59 * @param cpuId
60 * The CPU number the process is running on
61 * @param ts
62 * The timestamp at which we want the running process
63 * @return The TID of the thread running on CPU cpuId at time ts or
64 * {@code null} if either no thread is running or we do not know.
65 */
6d16f5a9 66 public static @Nullable Integer getThreadOnCpu(KernelAnalysisModule module, long cpuId, long ts) {
50a47aa6
GB
67 ITmfStateSystem ss = module.getStateSystem();
68 if (ss == null) {
69 return null;
70 }
71 try {
72 int cpuQuark = ss.getQuarkAbsolute(Attributes.CPUS, Long.toString(cpuId), Attributes.CURRENT_THREAD);
73 ITmfStateInterval interval = ss.querySingleState(ts, cpuQuark);
74 ITmfStateValue val = interval.getStateValue();
f20f0966 75 if (val.getType().equals(Type.INTEGER)) {
50a47aa6 76 return val.unboxInt();
50a47aa6
GB
77 }
78 } catch (AttributeNotFoundException | StateSystemDisposedException | TimeRangeException e) {
79 }
80 return null;
81 }
82
3fef638d
AM
83 /**
84 * The the threads that have been scheduled on the given CPU(s), for the
85 * given time range. Threads with TID 0 (swapper threads) will never be
86 * included.
87 *
88 * @param module
89 * The kernel analysis module to query
90 * @param cpus
91 * The list of cpus
92 * @param rangeStart
93 * The start of the time range
94 * @param rangeEnd
95 * The end of the time range
96 * @return A set of all the thread IDs that are run on said CPUs on the time
97 * range. Empty set if there is no thread on the CPUs in this time
98 * range. Null if the information is not available.
99 * @since 2.2
100 */
101 public static @Nullable Set<Integer> getThreadsOfCpus(KernelAnalysisModule module, Collection<Long> cpus, long rangeStart, long rangeEnd) {
102 ITmfStateSystem ss = module.getStateSystem();
103 if (ss == null) {
104 return null;
105 }
106
107 Set<Long> uniqueCpus = (cpus instanceof Set ? (Set<Long>) cpus : ImmutableSet.copyOf(cpus));
108
109 int threadsQuark = ss.optQuarkAbsolute(Attributes.THREADS);
110 if (threadsQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
111 return null;
112 }
113
114 List<Integer> threadQuarks = ss.getSubAttributes(threadsQuark, false);
115 return threadQuarks.stream()
116 /*
117 * Keep only the quarks of threads that are on at least one of the
118 * wanted CPUs' run queue.
119 */
120 .filter(threadQuark -> {
121 int threadCurrentCpuQuark = ss.optQuarkRelative(threadQuark, Attributes.CURRENT_CPU_RQ);
122 if (threadCurrentCpuQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
123 return false;
124 }
125
126 Iterator<ITmfStateInterval> it = StateSystemUtils.getIteratorOverQuark(ss, threadCurrentCpuQuark, rangeStart, rangeEnd);
127 while (it.hasNext()) {
128 ITmfStateInterval interval = it.next();
129 ITmfStateValue value = interval.getStateValue();
130 if (!value.isNull() && uniqueCpus.contains(Long.valueOf(value.unboxLong()))) {
131 return true;
132 }
133 }
134 /* The thread was never on any of the requested CPUs. */
135 return false;
136 })
137
138 /* Convert the thread quarks to their corresponding TIDs */
139 .map(ss::getAttributeName)
140 /* Ignore swapper threads */
141 .filter(attribName -> !attribName.startsWith(Attributes.THREAD_0_PREFIX))
142 .map(Integer::parseInt)
143 .collect(Collectors.toSet());
144 }
145
146 /**
147 * Predicate indicating if a thread state value is considered active or not.
148 */
149 private static final Predicate<ITmfStateValue> IS_STATE_VALUE_ACTIVE = stateValue -> {
150 if (stateValue.isNull() ||
151 stateValue.equals(StateValues.PROCESS_STATUS_UNKNOWN_VALUE) ||
152 stateValue.equals(StateValues.PROCESS_STATUS_WAIT_BLOCKED_VALUE) ||
153 stateValue.equals(StateValues.PROCESS_STATUS_WAIT_UNKNOWN_VALUE)) {
154 return false;
155 }
156 return true;
157 };
158
159 /**
160 * Return all the threads that are considered active in the given time
161 * range. Threads with TID 0 (swapper threads) will never be included.
162 *
163 * @param module
164 * The kernel analysis module to query
165 * @param rangeStart
166 * The start of the time range
167 * @param rangeEnd
168 * The end of the time range
169 * @return A set of all the thread IDs that are considered active in the
170 * time range. Empty set if there are none. Null if the information
171 * is not available.
172 * @since 2.2
173 */
174 public static @Nullable Set<Integer> getActiveThreadsForRange(KernelAnalysisModule module, long rangeStart, long rangeEnd) {
175 ITmfStateSystem ss = module.getStateSystem();
176 if (ss == null) {
177 return null;
178 }
179
180 List<ITmfStateInterval> fullQueryAtStart;
181 int threadsQuark;
182 try {
183 fullQueryAtStart = ss.queryFullState(rangeStart);
184 threadsQuark = ss.getQuarkAbsolute(Attributes.THREADS);
185 } catch (AttributeNotFoundException | StateSystemDisposedException e) {
186 return null;
187 }
188
189
190 List<Integer> threadQuarks = ss.getSubAttributes(threadsQuark, false);
191 return threadQuarks.stream()
192 /*
193 * Keep only the quarks of threads that are considered active at
194 * some point in the time range.
195 */
196 .filter(threadQuark -> {
197 /*
198 * If the thread was active at range start, we can already
199 * consider it active.
200 */
201 ITmfStateInterval intervalAtStart = fullQueryAtStart.get(threadQuark);
202 if (IS_STATE_VALUE_ACTIVE.test(intervalAtStart.getStateValue())) {
203 return true;
204 }
205
206 /*
207 * If it was inactive, and it remains in the exact same
208 * state for the whole time range, we can conclude it is
209 * inactive for the whole range.
210 *
211 * Note this will not catch cases where the threads goes
212 * from one inactive state to another, this will be found
213 * with the range query below.
214 */
215 if (intervalAtStart.getEndTime() >= rangeEnd) {
216 return false;
217 }
218
219 Iterator<ITmfStateInterval> it = StateSystemUtils.getIteratorOverQuark(ss, threadQuark, rangeStart, rangeEnd);
220 while (it.hasNext()) {
221 ITmfStateInterval interval = it.next();
222 if (IS_STATE_VALUE_ACTIVE.test(interval.getStateValue())) {
223 return true;
224 }
225 }
226 /* We haven't found an active state value in the whole range. */
227 return false;
228 })
229
230 /* Convert the thread quarks to their corresponding TIDs */
231 .map(ss::getAttributeName)
232 /* Ignore swapper threads */
233 .filter(attribName -> !attribName.startsWith(Attributes.THREAD_0_PREFIX))
234 .map(Integer::parseInt)
235 .collect(Collectors.toSet());
236 }
237
50a47aa6
GB
238 /**
239 * Get the TIDs of the threads from an analysis
240 *
241 * @param module
1add07ef 242 * The kernel analysis instance to run this method on
50a47aa6
GB
243 * @return The set of TIDs corresponding to the threads
244 */
6d16f5a9 245 public static Collection<Integer> getThreadIds(KernelAnalysisModule module) {
50a47aa6
GB
246 ITmfStateSystem ss = module.getStateSystem();
247 if (ss == null) {
aa353506 248 return Collections.EMPTY_SET;
50a47aa6
GB
249 }
250 int threadQuark;
251 try {
252 threadQuark = ss.getQuarkAbsolute(Attributes.THREADS);
4c4e2816 253 Set<@NonNull Integer> tids = new TreeSet<>();
50a47aa6 254 for (Integer quark : ss.getSubAttributes(threadQuark, false)) {
02b08403
MK
255 final @NonNull String attributeName = ss.getAttributeName(quark);
256 tids.add(attributeName.startsWith(Attributes.THREAD_0_PREFIX) ? 0 : Integer.parseInt(attributeName));
50a47aa6
GB
257 }
258 return tids;
259 } catch (AttributeNotFoundException e) {
260 }
aa353506 261 return Collections.EMPTY_SET;
50a47aa6
GB
262 }
263
264 /**
265 * Get the parent process ID of a thread
266 *
267 * @param module
1add07ef 268 * The kernel analysis instance to run this method on
50a47aa6
GB
269 * @param threadId
270 * The thread ID of the process for which to get the parent
271 * @param ts
272 * The timestamp at which to get the parent
273 * @return The parent PID or {@code null} if the PPID is not found.
274 */
6d16f5a9 275 public static @Nullable Integer getParentPid(KernelAnalysisModule module, Integer threadId, long ts) {
50a47aa6
GB
276 ITmfStateSystem ss = module.getStateSystem();
277 if (ss == null) {
f20f0966 278 return null;
50a47aa6
GB
279 }
280 Integer ppidNode;
281 try {
282 ppidNode = ss.getQuarkAbsolute(Attributes.THREADS, threadId.toString(), Attributes.PPID);
283 ITmfStateInterval ppidInterval = ss.querySingleState(ts, ppidNode);
284 ITmfStateValue ppidValue = ppidInterval.getStateValue();
285
f20f0966
MK
286 if (ppidValue.getType().equals(Type.INTEGER)) {
287 return Integer.valueOf(ppidValue.unboxInt());
50a47aa6
GB
288 }
289 } catch (AttributeNotFoundException | StateSystemDisposedException | TimeRangeException e) {
290 }
f20f0966 291 return null;
50a47aa6
GB
292 }
293
294 /**
295 * Get the executable name of the thread ID. If the thread ID was used
296 * multiple time or the name changed in between, it will return the last
297 * name the thread has taken, or {@code null} if no name is found
298 *
299 * @param module
1add07ef 300 * The kernel analysis instance to run this method on
50a47aa6
GB
301 * @param threadId
302 * The thread ID of the process for which to get the name
303 * @return The last executable name of this process, or {@code null} if not
304 * found
305 */
6d16f5a9 306 public static @Nullable String getExecutableName(KernelAnalysisModule module, Integer threadId) {
50a47aa6
GB
307 ITmfStateSystem ss = module.getStateSystem();
308 if (ss == null) {
f20f0966 309 return null;
50a47aa6
GB
310 }
311 Integer execNameNode;
312 try {
313 execNameNode = ss.getQuarkAbsolute(Attributes.THREADS, threadId.toString(), Attributes.EXEC_NAME);
314 List<ITmfStateInterval> execNameIntervals = StateSystemUtils.queryHistoryRange(ss, execNameNode, ss.getStartTime(), ss.getCurrentEndTime());
315
316 ITmfStateValue execNameValue;
f20f0966 317 String execName = null;
50a47aa6
GB
318 for (ITmfStateInterval interval : execNameIntervals) {
319 execNameValue = interval.getStateValue();
f20f0966 320 if (execNameValue.getType().equals(Type.STRING)) {
0e4f957e 321 execName = execNameValue.unboxStr();
50a47aa6
GB
322 }
323 }
f20f0966 324 return execName;
50a47aa6
GB
325 } catch (AttributeNotFoundException | StateSystemDisposedException | TimeRangeException e) {
326 }
f20f0966 327 return null;
50a47aa6
GB
328 }
329
1add07ef
GB
330 /**
331 * Get the priority of this thread at time ts
332 *
333 * @param module
334 * The kernel analysis instance to run this method on
335 * @param threadId
336 * The ID of the thread to query
337 * @param ts
338 * The timestamp at which to query
339 * @return The priority of the thread or <code>-1</code> if not available
340 */
341 public static int getThreadPriority(KernelAnalysisModule module, int threadId, long ts) {
342 ITmfStateSystem ss = module.getStateSystem();
343 if (ss == null) {
344 return -1;
345 }
346 int prioQuark = ss.optQuarkAbsolute(Attributes.THREADS, String.valueOf(threadId), Attributes.PRIO);
347 if (prioQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
348 return -1;
349 }
350 try {
351 return ss.querySingleState(ts, prioQuark).getStateValue().unboxInt();
ed48dc75 352 } catch (StateSystemDisposedException e) {
1add07ef
GB
353 return -1;
354 }
355 }
356
50a47aa6
GB
357 /**
358 * Get the status intervals for a given thread with a resolution
359 *
360 * @param module
1add07ef 361 * The kernel analysis instance to run this method on
50a47aa6
GB
362 * @param threadId
363 * The ID of the thread to get the intervals for
364 * @param start
365 * The start time of the requested range
366 * @param end
367 * The end time of the requested range
368 * @param resolution
369 * The resolution or the minimal time between the requested
370 * intervals. If interval times are smaller than resolution, only
371 * the first interval is returned, the others are ignored.
372 * @param monitor
373 * A progress monitor for this task
374 * @return The list of status intervals for this thread, an empty list is
375 * returned if either the state system is {@code null} or the quark
376 * is not found
377 */
6d16f5a9 378 public static List<ITmfStateInterval> getStatusIntervalsForThread(KernelAnalysisModule module, Integer threadId, long start, long end, long resolution, IProgressMonitor monitor) {
50a47aa6
GB
379 ITmfStateSystem ss = module.getStateSystem();
380 if (ss == null) {
aa353506 381 return Collections.EMPTY_LIST;
50a47aa6
GB
382 }
383
384 try {
385 int threadQuark = ss.getQuarkAbsolute(Attributes.THREADS, threadId.toString());
d3cc952f 386 List<ITmfStateInterval> statusIntervals = StateSystemUtils.queryHistoryRange(ss, threadQuark, Math.max(start, ss.getStartTime()), Math.min(end - 1, ss.getCurrentEndTime()), resolution, monitor);
50a47aa6
GB
387 return statusIntervals;
388 } catch (AttributeNotFoundException | StateSystemDisposedException | TimeRangeException e) {
389 }
aa353506 390 return Collections.EMPTY_LIST;
50a47aa6
GB
391 }
392
393}
This page took 0.073354 seconds and 5 git commands to generate.