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