1 /*
2 * Copyright 2020-2024 The OSHI Project Contributors
3 * SPDX-License-Identifier: MIT
4 */
5 package oshi.driver.linux.proc;
6
7 import java.util.List;
8
9 import oshi.annotation.concurrent.ThreadSafe;
10 import oshi.hardware.CentralProcessor.TickType;
11 import oshi.util.FileUtil;
12 import oshi.util.ParseUtil;
13 import oshi.util.platform.linux.ProcPath;
14
15 /**
16 * Utility to read CPU statistics from {@code /proc/stat}
17 */
18 @ThreadSafe
19 public final class CpuStat {
20
21 private CpuStat() {
22 }
23
24 /**
25 * Gets the System CPU ticks array from {@code /proc/stat}
26 *
27 * @return Array of CPU ticks
28 */
29 public static long[] getSystemCpuLoadTicks() {
30 long[] ticks = new long[TickType.values().length];
31 // /proc/stat expected format
32 // first line is overall user,nice,system,idle,iowait,irq, etc.
33 // cpu 3357 0 4313 1362393 ...
34 String tickStr;
35 List<String> procStat = FileUtil.readLines(ProcPath.STAT, 1);
36 if (procStat.isEmpty()) {
37 return ticks;
38 }
39 tickStr = procStat.get(0);
40
41 // Split the line. Note the first (0) element is "cpu" so remaining
42 // elements are offset by 1 from the enum index
43 String[] tickArr = ParseUtil.whitespaces.split(tickStr);
44 if (tickArr.length <= TickType.IDLE.getIndex()) {
45 // If ticks don't at least go user/nice/system/idle, abort
46 return ticks;
47 }
48 // Note tickArr is offset by 1 because first element is "cpu"
49 for (int i = 0; i < TickType.values().length; i++) {
50 ticks[i] = ParseUtil.parseLongOrDefault(tickArr[i + 1], 0L);
51 }
52 // Ignore guest or guest_nice, they are included in user/nice
53 return ticks;
54 }
55
56 /**
57 * Gets an arrya of Processor CPU ticks array from /proc/stat
58 *
59 * @param logicalProcessorCount The number of logical processors, which corresponds to the number of lines to read
60 * from the file.
61 * @return Array of CPU ticks for each processor
62 */
63 public static long[][] getProcessorCpuLoadTicks(int logicalProcessorCount) {
64 long[][] ticks = new long[logicalProcessorCount][TickType.values().length];
65 // /proc/stat expected format
66 // first line is overall user,nice,system,idle, etc.
67 // cpu 3357 0 4313 1362393 ...
68 // per-processor subsequent lines for cpu0, cpu1, etc.
69 int cpu = 0;
70 List<String> procStat = FileUtil.readFile(ProcPath.STAT);
71 for (String stat : procStat) {
72 if (stat.startsWith("cpu") && !stat.startsWith("cpu ")) {
73 // Split the line. Note the first (0) element is "cpu" so
74 // remaining
75 // elements are offset by 1 from the enum index
76 String[] tickArr = ParseUtil.whitespaces.split(stat);
77 if (tickArr.length <= TickType.IDLE.getIndex()) {
78 // If ticks don't at least go user/nice/system/idle, abort
79 return ticks;
80 }
81 // Note tickArr is offset by 1
82 for (int i = 0; i < TickType.values().length; i++) {
83 ticks[cpu][i] = ParseUtil.parseLongOrDefault(tickArr[i + 1], 0L);
84 }
85 // Ignore guest or guest_nice, they are included in
86 if (++cpu >= logicalProcessorCount) {
87 break;
88 }
89 }
90 }
91 return ticks;
92 }
93
94 /**
95 * Gets the number of context switches from /proc/stat
96 *
97 * @return The number of context switches if available, -1 otherwise
98 */
99 public static long getContextSwitches() {
100 List<String> procStat = FileUtil.readFile(ProcPath.STAT);
101 for (String stat : procStat) {
102 if (stat.startsWith("ctxt ")) {
103 String[] ctxtArr = ParseUtil.whitespaces.split(stat);
104 if (ctxtArr.length == 2) {
105 return ParseUtil.parseLongOrDefault(ctxtArr[1], 0);
106 }
107 }
108 }
109 return 0L;
110 }
111
112 /**
113 * Gets the number of interrupts from /proc/stat
114 *
115 * @return The number of interrupts if available, -1 otherwise
116 */
117 public static long getInterrupts() {
118 List<String> procStat = FileUtil.readFile(ProcPath.STAT);
119 for (String stat : procStat) {
120 if (stat.startsWith("intr ")) {
121 String[] intrArr = ParseUtil.whitespaces.split(stat);
122 if (intrArr.length > 2) {
123 return ParseUtil.parseLongOrDefault(intrArr[1], 0);
124 }
125 }
126 }
127 return 0L;
128 }
129
130 /**
131 * Gets the boot time from /proc/stat
132 *
133 * @return The boot time if available, 0 otherwise
134 */
135 public static long getBootTime() {
136 // Boot time given by btime variable in /proc/stat.
137 List<String> procStat = FileUtil.readFile(ProcPath.STAT);
138 for (String stat : procStat) {
139 if (stat.startsWith("btime")) {
140 String[] bTime = ParseUtil.whitespaces.split(stat);
141 return ParseUtil.parseLongOrDefault(bTime[1], 0L);
142 }
143 }
144 return 0;
145 }
146 }