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 }