View Javadoc
1   /*
2    * Copyright 2020-2023 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.driver.windows.registry;
6   
7   import java.util.Collection;
8   import java.util.HashMap;
9   import java.util.List;
10  import java.util.Map;
11  
12  import com.sun.jna.platform.win32.WinBase;
13  
14  import oshi.annotation.concurrent.Immutable;
15  import oshi.annotation.concurrent.ThreadSafe;
16  import oshi.driver.windows.perfmon.ProcessInformation;
17  import oshi.driver.windows.perfmon.ProcessInformation.ProcessPerformanceProperty;
18  import oshi.util.GlobalConfig;
19  import oshi.util.tuples.Pair;
20  import oshi.util.tuples.Triplet;
21  
22  /**
23   * Utility to read process data from HKEY_PERFORMANCE_DATA information with backup from Performance Counters or WMI
24   */
25  @ThreadSafe
26  public final class ProcessPerformanceData {
27  
28      private static final String PROCESS = "Process";
29      private static final boolean PERFDATA = GlobalConfig.get(GlobalConfig.OSHI_OS_WINDOWS_HKEYPERFDATA, true);
30  
31      private ProcessPerformanceData() {
32      }
33  
34      /**
35       * Query the registry for process performance counters
36       *
37       * @param pids An optional collection of process IDs to filter the list to. May be null for no filtering.
38       * @return A map with Process ID as the key and a {@link PerfCounterBlock} object populated with performance counter
39       *         information if successful, or null otherwise.
40       */
41      public static Map<Integer, PerfCounterBlock> buildProcessMapFromRegistry(Collection<Integer> pids) {
42          // Grab the data from the registry.
43          Triplet<List<Map<ProcessPerformanceProperty, Object>>, Long, Long> processData = null;
44          if (PERFDATA) {
45              processData = HkeyPerformanceDataUtil.readPerfDataFromRegistry(PROCESS, ProcessPerformanceProperty.class);
46          }
47          if (processData == null) {
48              return null;
49          }
50          List<Map<ProcessPerformanceProperty, Object>> processInstanceMaps = processData.getA();
51          long now = processData.getC(); // 1970 epoch
52  
53          // Create a map and fill it
54          Map<Integer, PerfCounterBlock> processMap = new HashMap<>();
55          // Iterate instances.
56          for (Map<ProcessPerformanceProperty, Object> processInstanceMap : processInstanceMaps) {
57              int pid = ((Integer) processInstanceMap.get(ProcessPerformanceProperty.IDPROCESS)).intValue();
58              String name = (String) processInstanceMap.get(ProcessPerformanceProperty.NAME);
59              if ((pids == null || pids.contains(pid)) && !"_Total".equals(name)) {
60                  // Field name is elapsed time but the value is the process start time
61                  long ctime = (Long) processInstanceMap.get(ProcessPerformanceProperty.ELAPSEDTIME);
62                  // if creation time value is less than current millis, it's in 1970 epoch,
63                  // otherwise it's 1601 epoch and we must convert
64                  if (ctime > now) {
65                      ctime = WinBase.FILETIME.filetimeToDate((int) (ctime >> 32), (int) (ctime & 0xffffffffL)).getTime();
66                  }
67                  long upTime = now - ctime;
68                  if (upTime < 1L) {
69                      upTime = 1L;
70                  }
71                  processMap.put(pid,
72                          new PerfCounterBlock(name,
73                                  (Integer) processInstanceMap.get(ProcessPerformanceProperty.CREATINGPROCESSID),
74                                  (Integer) processInstanceMap.get(ProcessPerformanceProperty.PRIORITYBASE),
75                                  (Long) processInstanceMap.get(ProcessPerformanceProperty.PRIVATEBYTES), ctime, upTime,
76                                  (Long) processInstanceMap.get(ProcessPerformanceProperty.IOREADBYTESPERSEC),
77                                  (Long) processInstanceMap.get(ProcessPerformanceProperty.IOWRITEBYTESPERSEC),
78                                  (Integer) processInstanceMap.get(ProcessPerformanceProperty.PAGEFAULTSPERSEC)));
79              }
80          }
81          return processMap;
82      }
83  
84      /**
85       * Query PerfMon for process performance counters
86       *
87       * @param pids An optional collection of process IDs to filter the list to. May be null for no filtering.
88       * @return A map with Process ID as the key and a {@link PerfCounterBlock} object populated with performance counter
89       *         information.
90       */
91      public static Map<Integer, PerfCounterBlock> buildProcessMapFromPerfCounters(Collection<Integer> pids) {
92          return buildProcessMapFromPerfCounters(pids, null);
93      }
94  
95      /**
96       * Query PerfMon for process performance counters
97       *
98       * @param pids     An optional collection of process IDs to filter the list to. May be null for no filtering.
99       * @param procName Filter by this process name.
100      * @return A map with Process ID as the key and a {@link PerfCounterBlock} object populated with performance counter
101      *         information.
102      */
103     public static Map<Integer, PerfCounterBlock> buildProcessMapFromPerfCounters(Collection<Integer> pids,
104             String procName) {
105         Map<Integer, PerfCounterBlock> processMap = new HashMap<>();
106         Pair<List<String>, Map<ProcessPerformanceProperty, List<Long>>> instanceValues = ProcessInformation
107                 .queryProcessCounters();
108         long now = System.currentTimeMillis(); // 1970 epoch
109         List<String> instances = instanceValues.getA();
110         Map<ProcessPerformanceProperty, List<Long>> valueMap = instanceValues.getB();
111         List<Long> pidList = valueMap.get(ProcessPerformanceProperty.IDPROCESS);
112         List<Long> ppidList = valueMap.get(ProcessPerformanceProperty.CREATINGPROCESSID);
113         List<Long> priorityList = valueMap.get(ProcessPerformanceProperty.PRIORITYBASE);
114         List<Long> ioReadList = valueMap.get(ProcessPerformanceProperty.IOREADBYTESPERSEC);
115         List<Long> ioWriteList = valueMap.get(ProcessPerformanceProperty.IOWRITEBYTESPERSEC);
116         List<Long> workingSetSizeList = valueMap.get(ProcessPerformanceProperty.PRIVATEBYTES);
117         List<Long> elapsedTimeList = valueMap.get(ProcessPerformanceProperty.ELAPSEDTIME);
118         List<Long> pageFaultsList = valueMap.get(ProcessPerformanceProperty.PAGEFAULTSPERSEC);
119 
120         for (int inst = 0; inst < instances.size(); inst++) {
121             int pid = pidList.get(inst).intValue();
122             if (pids == null || pids.contains(pid)) {
123                 // Field name is elapsed time but the value is the process start time
124                 long ctime = elapsedTimeList.get(inst);
125                 // if creation time value is less than current millis, it's in 1970 epoch,
126                 // otherwise it's 1601 epoch and we must convert
127                 if (ctime > now) {
128                     ctime = WinBase.FILETIME.filetimeToDate((int) (ctime >> 32), (int) (ctime & 0xffffffffL)).getTime();
129                 }
130                 long upTime = now - ctime;
131                 if (upTime < 1L) {
132                     upTime = 1L;
133                 }
134                 processMap.put(pid,
135                         new PerfCounterBlock(instances.get(inst), ppidList.get(inst).intValue(),
136                                 priorityList.get(inst).intValue(), workingSetSizeList.get(inst), ctime, upTime,
137                                 ioReadList.get(inst), ioWriteList.get(inst), pageFaultsList.get(inst).intValue()));
138             }
139         }
140         return processMap;
141     }
142 
143     /**
144      * Class to encapsulate data from the registry performance counter block
145      */
146     @Immutable
147     public static class PerfCounterBlock {
148         private final String name;
149         private final int parentProcessID;
150         private final int priority;
151         private final long residentSetSize;
152         private final long startTime;
153         private final long upTime;
154         private final long bytesRead;
155         private final long bytesWritten;
156         private final int pageFaults;
157 
158         public PerfCounterBlock(String name, int parentProcessID, int priority, long residentSetSize, long startTime,
159                 long upTime, long bytesRead, long bytesWritten, int pageFaults) {
160             this.name = name;
161             this.parentProcessID = parentProcessID;
162             this.priority = priority;
163             this.residentSetSize = residentSetSize;
164             this.startTime = startTime;
165             this.upTime = upTime;
166             this.bytesRead = bytesRead;
167             this.bytesWritten = bytesWritten;
168             this.pageFaults = pageFaults;
169         }
170 
171         /**
172          * @return the name
173          */
174         public String getName() {
175             return name;
176         }
177 
178         /**
179          * @return the parentProcessID
180          */
181         public int getParentProcessID() {
182             return parentProcessID;
183         }
184 
185         /**
186          * @return the priority
187          */
188         public int getPriority() {
189             return priority;
190         }
191 
192         /**
193          * @return the residentSetSize
194          */
195         public long getResidentSetSize() {
196             return residentSetSize;
197         }
198 
199         /**
200          * @return the startTime
201          */
202         public long getStartTime() {
203             return startTime;
204         }
205 
206         /**
207          * @return the upTime
208          */
209         public long getUpTime() {
210             return upTime;
211         }
212 
213         /**
214          * @return the bytesRead
215          */
216         public long getBytesRead() {
217             return bytesRead;
218         }
219 
220         /**
221          * @return the bytesWritten
222          */
223         public long getBytesWritten() {
224             return bytesWritten;
225         }
226 
227         /**
228          * @return the pageFaults
229          */
230         public long getPageFaults() {
231             return pageFaults;
232         }
233     }
234 }