1 /*
2 * Copyright 2020-2024 The OSHI Project Contributors
3 * SPDX-License-Identifier: MIT
4 */
5 package oshi.driver.linux.proc;
6
7 import static oshi.software.os.OSProcess.State.OTHER;
8 import static oshi.software.os.OSProcess.State.RUNNING;
9 import static oshi.software.os.OSProcess.State.SLEEPING;
10 import static oshi.software.os.OSProcess.State.STOPPED;
11 import static oshi.software.os.OSProcess.State.WAITING;
12 import static oshi.software.os.OSProcess.State.ZOMBIE;
13
14 import java.io.File;
15 import java.util.Arrays;
16 import java.util.EnumMap;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Locale;
20 import java.util.Map;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23 import java.util.stream.Collectors;
24
25 import oshi.annotation.concurrent.ThreadSafe;
26 import oshi.software.os.OSProcess;
27 import oshi.util.Constants;
28 import oshi.util.FileUtil;
29 import oshi.util.ParseUtil;
30 import oshi.util.platform.linux.ProcPath;
31 import oshi.util.tuples.Triplet;
32
33 /**
34 * Utility to read process statistics from {@code /proc/[pid]/stat}
35 */
36 @ThreadSafe
37 public final class ProcessStat {
38
39 private static final Pattern SOCKET = Pattern.compile("socket:\\[(\\d+)\\]");
40
41 /**
42 * Enum corresponding to the fields in the output of {@code /proc/[pid]/stat}
43 */
44 public enum PidStat {
45 /**
46 * The process ID.
47 */
48 PID,
49 /**
50 * The filename of the executable.
51 */
52 COMM,
53 /**
54 * One of the following characters, indicating process state:
55 * <p>
56 * R Running
57 * <p>
58 * S Sleeping in an interruptible wait
59 * <p>
60 * D Waiting in uninterruptible disk sleep
61 * <p>
62 * Z Zombie
63 * <p>
64 * T Stopped (on a signal) or (before Linux 2.6.33) trace stopped
65 * <p>
66 * t Tracing stop (Linux 2.6.33 onward)
67 * <p>
68 * W Paging (only before Linux 2.6.0)
69 * <p>
70 * X Dead (from Linux 2.6.0 onward)
71 * <p>
72 * x Dead (Linux 2.6.33 to 3.13 only)
73 * <p>
74 * K Wakekill (Linux 2.6.33 to 3.13 only)
75 * <p>
76 * W Waking (Linux 2.6.33 to 3.13 only)
77 * <p>
78 * P Parked (Linux 3.9 to 3.13 only)
79 */
80 STATE,
81 /**
82 * The PID of the parent of this process.
83 */
84 PPID,
85 /**
86 * The process group ID of the process.
87 */
88 PGRP,
89 /**
90 * The session ID of the process.
91 */
92 SESSION,
93 /**
94 * The controlling terminal of the process. (The minor device number is contained in the combination of bits 31
95 * to 20 and 7 to 0; the major device number is in bits 15 to 8.)
96 */
97 TTY_NR,
98 /**
99 * The ID of the foreground process group of the controlling terminal of the process.
100 */
101 PTGID,
102 /**
103 * The kernel flags word of the process. For bit meanings, see the PF_* defines in the Linux kernel source file
104 * include/linux/sched.h. Details depend on the kernel version.
105 */
106 FLAGS,
107 /**
108 * The number of minor faults the process has made which have not required loading a memory page from disk.
109 */
110 MINFLT,
111 /**
112 * The number of minor faults that the process's waited-for children have made.
113 */
114 CMINFLT,
115 /**
116 * The number of major faults the process has made which have required loading a memory page from disk.
117 */
118 MAJFLT,
119 /**
120 * The number of major faults that the process's waited-for children have made.
121 */
122 CMAJFLT,
123 /**
124 * Amount of time that this process has been scheduled in user mode, measured in clock ticks. This includes
125 * guest time, cguest_time (time spent running a virtual CPU), so that applications that are not aware of the
126 * guest time field do not lose that time from their calculations.
127 */
128 UTIME,
129 /**
130 * Amount of time that this process has been scheduled in kernel mode, measured in clock ticks.
131 */
132 STIME,
133 /**
134 * Amount of time that this process's waited-for children have been scheduled in user mode, measured in clock
135 * ticks. This includes guest time, cguest_time (time spent running a virtual CPU).
136 */
137 CUTIME,
138 /**
139 * Amount of time that this process's waited-for children have been scheduled in kernel mode, measured in clock
140 * ticks.
141 */
142 CSTIME,
143 /**
144 * For processes running a real-time scheduling policy (policy below; see sched_setscheduler(2)), this is the
145 * negated scheduling priority, minus one; that is, a number in the range -2 to -100, corresponding to real-time
146 * priorities 1 to 99. For processes running under a non-real-time scheduling policy, this is the raw nice value
147 * (setpriority(2)) as represented in the kernel. The kernel stores nice values as numbers in the range 0 (high)
148 * to 39 (low), corresponding to the user-visible nice range of -20 to 19.
149 */
150 PRIORITY,
151 /**
152 * The nice value (see setpriority(2)), a value in the range 19 (low priority) to -20 (high priority).
153 */
154 NICE,
155 /**
156 * Number of threads in this process.
157 */
158 NUM_THREADS,
159 /**
160 * The time in jiffies before the next SIGALRM is sent to the process due to an interval timer. Since ker‐nel
161 * 2.6.17, this field is no longer maintained, and is hard coded as 0.
162 */
163 ITREALVALUE,
164 /**
165 * The time the process started after system boot, in clock ticks.
166 */
167 STARTTIME,
168 /**
169 * Virtual memory size in bytes.
170 */
171 VSIZE,
172 /**
173 * Resident Set Size: number of pages the process has in real memory. This is just the pages which count toward
174 * text, data, or stack space. This does not include pages which have not been demand-loaded in, or which are
175 * swapped out.
176 */
177 RSS,
178 /**
179 * Current soft limit in bytes on the rss of the process; see the description of RLIMIT_RSS in getrlimit(2).
180 */
181 RSSLIM,
182 /**
183 * The address above which program text can run.
184 */
185 STARTCODE,
186
187 /**
188 * The address below which program text can run.
189 */
190 ENDCODE,
191 /**
192 * The address of the start (i.e., bottom) of the stack.
193 */
194 STARTSTACK,
195 /**
196 * The current value of ESP (stack pointer), as found in the kernel stack page for the process.
197 */
198 KSTKESP,
199 /**
200 * The current EIP (instruction pointer).
201 */
202 KSTKEIP,
203 /**
204 * The bitmap of pending signals, displayed as a decimal number. Obsolete, because it does not provide
205 * information on real-time signals; use /proc/[pid]/status instead.
206 */
207 SIGNAL,
208 /**
209 * The bitmap of blocked signals, displayed as a decimal number. Obsolete, because it does not provide
210 * information on real-time signals; use /proc/[pid]/status instead.
211 */
212 BLOCKED,
213 /**
214 * The bitmap of ignored signals, displayed as a decimal number. Obsolete, because it does not provide
215 * information on real-time signals; use /proc/[pid]/status instead.
216 */
217 SIGIGNORE,
218 /**
219 * The bitmap of caught signals, displayed as a decimal number. Obsolete, because it does not provide
220 * information on real-time signals; use /proc/[pid]/status instead.
221 */
222 SIGCATCH,
223 /**
224 * This is the "channel" in which the process is waiting. It is the address of a location in the kernel where
225 * the process is sleeping. The corresponding symbolic name can be found in /proc/[pid]/wchan.
226 */
227 WCHAN,
228 /**
229 * Number of pages swapped (not maintained).
230 */
231 NSWAP,
232 /**
233 * Cumulative nswap for child processes (not maintained).
234 */
235 CNSWAP,
236 /**
237 * Signal to be sent to parent when we die.
238 */
239 EXIT_SIGNAL,
240 /**
241 * CPU number last executed on.
242 */
243 PROCESSOR,
244 /**
245 * Real-time scheduling priority, a number in the range 1 to 99 for processes scheduled under a real-time
246 * policy, or 0, for non-real-time processes (see sched_setscheduler(2)).
247 */
248 RT_PRIORITY,
249 /**
250 * Scheduling policy (see sched_setscheduler(2)). Decode using the SCHED_* constants in linux/sched.h.
251 */
252 POLICY,
253 /**
254 * Aggregated block I/O delays, measured in clock ticks (centiseconds).
255 */
256 DELAYACCT_BLKIO_TICKS,
257 /**
258 * Guest time of the process (time spent running a vir‐ tual CPU for a guest operating system), measured in
259 * clock ticks.
260 */
261 GUEST_TIME,
262 /**
263 * Guest time of the process's children, measured in clock ticks.
264 */
265 CGUEST_TIME,
266 /**
267 * Address above which program initialized and uninitialized (BSS) data are placed.
268 */
269 START_DATA,
270 /**
271 * Address below which program initialized and uninitialized (BSS) data are placed.
272 */
273 END_DATA,
274 /**
275 * Address above which program heap can be expanded with brk(2).
276 */
277 START_BRK,
278 /**
279 * Address above which program command-line arguments (argv) are placed.
280 */
281 ARG_START,
282
283 /**
284 * Address below program command-line arguments (argv) are placed.
285 */
286 ARG_END,
287
288 /**
289 * Address above which program environment is placed.
290 */
291 ENV_START,
292
293 /**
294 * Address below which program environment is placed.
295 */
296 ENV_END,
297
298 /**
299 * The thread's exit status in the form reported by waitpid(2).
300 */
301 EXIT_CODE;
302 }
303
304 /**
305 * Enum corresponding to the fields in the output of {@code /proc/[pid]/statm}
306 */
307 public enum PidStatM {
308 /**
309 * Total program size
310 */
311 SIZE,
312 /**
313 * Resident set size
314 */
315 RESIDENT,
316 /**
317 * Number of resident shared pages (i.e., backed by a file)
318 */
319 SHARED,
320 /**
321 * Text (code)
322 */
323 TEXT,
324 /**
325 * Library (unused since Linux 2.6; always 0)
326 */
327 LIB,
328 /**
329 * Data + stack
330 */
331 DATA,
332 /**
333 * Dirty pages (unused since Linux 2.6; always 0)
334 */
335 DT;
336 }
337
338 /**
339 * Constant defining the number of integer values in {@code /proc/pid/stat}. 2.6 Kernel has 44 elements, 3.3 has 47,
340 * and 3.5 has 52.
341 */
342 public static final int PROC_PID_STAT_LENGTH;
343 static {
344 String stat = FileUtil.getStringFromFile(ProcPath.SELF_STAT);
345 if (stat.contains(")")) {
346 // add 3 to account for pid, process name in prarenthesis, and state
347 PROC_PID_STAT_LENGTH = ParseUtil.countStringToLongArray(stat, ' ') + 3;
348 } else {
349 // Default assuming recent kernel
350 PROC_PID_STAT_LENGTH = 52;
351 }
352 }
353
354 private ProcessStat() {
355 }
356
357 /**
358 * Reads the statistics in {@code /proc/[pid]/stat} and returns the results.
359 *
360 * @param pid The process ID for which to fetch stats
361 * @return A triplet containing the process name as the first element, a character representing the process state as
362 * the second element, and an EnumMap as the third element, where the numeric values in {@link PidStat} are
363 * mapped to a {@link Long} value.
364 * <p>
365 * If the process doesn't exist, returns null.
366 */
367 public static Triplet<String, Character, Map<PidStat, Long>> getPidStats(int pid) {
368 String stat = FileUtil.getStringFromFile(String.format(Locale.ROOT, ProcPath.PID_STAT, pid));
369 if (stat.isEmpty()) {
370 // If pid doesn't exist
371 return null;
372 }
373 // Get process name from between parentheses and state immediately after
374 int nameStart = stat.indexOf('(') + 1;
375 int nameEnd = stat.indexOf(')');
376 String name = stat.substring(nameStart, nameEnd);
377 Character state = stat.charAt(nameEnd + 2);
378 // Split everything after the state
379 String[] split = ParseUtil.whitespaces.split(stat.substring(nameEnd + 4).trim());
380
381 Map<PidStat, Long> statMap = new EnumMap<>(PidStat.class);
382 PidStat[] enumArray = PidStat.class.getEnumConstants();
383 for (int i = 3; i < enumArray.length && i - 3 < split.length; i++) {
384 statMap.put(enumArray[i], ParseUtil.parseLongOrDefault(split[i - 3], 0L));
385 }
386 return new Triplet<>(name, state, statMap);
387 }
388
389 /**
390 * Reads the statistics in {@code /proc/[pid]/statm} and returns the results.
391 *
392 * @param pid The process ID for which to fetch stats
393 * @return An EnumMap where the numeric values in {@link PidStatM} are mapped to a {@link Long} value.
394 * <p>
395 * If the process doesn't exist, returns null.
396 */
397 public static Map<PidStatM, Long> getPidStatM(int pid) {
398 String statm = FileUtil.getStringFromFile(String.format(Locale.ROOT, ProcPath.PID_STATM, pid));
399 if (statm.isEmpty()) {
400 // If pid doesn't exist
401 return null;
402 }
403 // Split the fields
404 String[] split = ParseUtil.whitespaces.split(statm);
405
406 Map<PidStatM, Long> statmMap = new EnumMap<>(PidStatM.class);
407 PidStatM[] enumArray = PidStatM.class.getEnumConstants();
408 for (int i = 0; i < enumArray.length && i < split.length; i++) {
409 statmMap.put(enumArray[i], ParseUtil.parseLongOrDefault(split[i], 0L));
410 }
411 return statmMap;
412 }
413
414 /**
415 * Gets an array of files in the /proc/{pid}/fd directory.
416 *
417 * @param pid id of process to read file descriptors for
418 * @return An array of File objects representing opened file descriptors of the process
419 */
420 public static File[] getFileDescriptorFiles(int pid) {
421 return listNumericFiles(String.format(Locale.ROOT, ProcPath.PID_FD, pid));
422 }
423
424 /**
425 * Gets an array of files in the /proc directory with only numeric digit filenames, corresponding to processes
426 *
427 * @return An array of File objects for the process files
428 */
429 public static File[] getPidFiles() {
430 return listNumericFiles(ProcPath.PROC);
431 }
432
433 /**
434 * Gets a map of sockets and their corresponding process ID
435 *
436 * @return a map with socket as the key and pid as the value
437 */
438 public static Map<Long, Integer> querySocketToPidMap() {
439 Map<Long, Integer> pidMap = new HashMap<>();
440 for (File f : getPidFiles()) {
441 int pid = ParseUtil.parseIntOrDefault(f.getName(), -1);
442 File[] fds = getFileDescriptorFiles(pid);
443 for (File fd : fds) {
444 String symLink = FileUtil.readSymlinkTarget(fd);
445 if (symLink != null) {
446 Matcher m = SOCKET.matcher(symLink);
447 if (m.matches()) {
448 pidMap.put(ParseUtil.parseLongOrDefault(m.group(1), -1L), pid);
449 }
450 }
451 }
452 }
453 return pidMap;
454 }
455
456 /**
457 * Gets a List of thread ids for a process from the {@code /proc/[pid]/task/} directory with only numeric digit
458 * filenames, corresponding to the threads.
459 *
460 * @param pid process id
461 * @return A list of thread id.
462 */
463 public static List<Integer> getThreadIds(int pid) {
464 File[] threads = listNumericFiles(String.format(Locale.ROOT, ProcPath.TASK_PATH, pid));
465 return Arrays.stream(threads).map(thread -> ParseUtil.parseIntOrDefault(thread.getName(), 0))
466 .filter(threadId -> threadId != pid).collect(Collectors.toList());
467 }
468
469 private static File[] listNumericFiles(String path) {
470 File directory = new File(path);
471 File[] numericFiles = directory.listFiles(file -> Constants.DIGITS.matcher(file.getName()).matches());
472 return numericFiles == null ? new File[0] : numericFiles;
473 }
474
475 /***
476 * Returns Enum STATE for the state value obtained from status file of any process/thread.
477 *
478 * @param stateValue state value from the status file
479 * @return OSProcess.State
480 */
481 public static OSProcess.State getState(char stateValue) {
482 OSProcess.State state;
483 switch (stateValue) {
484 case 'R':
485 state = RUNNING;
486 break;
487 case 'S':
488 state = SLEEPING;
489 break;
490 case 'D':
491 state = WAITING;
492 break;
493 case 'Z':
494 state = ZOMBIE;
495 break;
496 case 'T':
497 state = STOPPED;
498 break;
499 default:
500 state = OTHER;
501 break;
502 }
503 return state;
504 }
505
506 }