View Javadoc
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 }