1
2
3
4
5 package oshi.driver.windows.registry;
6
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.EnumMap;
10 import java.util.HashMap;
11 import java.util.List;
12 import java.util.Map;
13
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import com.sun.jna.Memory;
18 import com.sun.jna.platform.win32.Advapi32;
19 import com.sun.jna.platform.win32.Advapi32Util;
20 import com.sun.jna.platform.win32.Win32Exception;
21 import com.sun.jna.platform.win32.WinBase.FILETIME;
22 import com.sun.jna.platform.win32.WinError;
23 import com.sun.jna.platform.win32.WinPerf.PERF_COUNTER_BLOCK;
24 import com.sun.jna.platform.win32.WinPerf.PERF_COUNTER_DEFINITION;
25 import com.sun.jna.platform.win32.WinPerf.PERF_DATA_BLOCK;
26 import com.sun.jna.platform.win32.WinPerf.PERF_INSTANCE_DEFINITION;
27 import com.sun.jna.platform.win32.WinPerf.PERF_OBJECT_TYPE;
28 import com.sun.jna.platform.win32.WinReg;
29
30 import oshi.annotation.SuppressForbidden;
31 import oshi.annotation.concurrent.ThreadSafe;
32 import oshi.jna.ByRef.CloseableIntByReference;
33 import oshi.util.platform.windows.PerfCounterWildcardQuery.PdhCounterWildcardProperty;
34 import oshi.util.tuples.Pair;
35 import oshi.util.tuples.Triplet;
36
37
38
39
40 @ThreadSafe
41 public final class HkeyPerformanceDataUtil {
42
43 private static final Logger LOG = LoggerFactory.getLogger(HkeyPerformanceDataUtil.class);
44
45
46
47
48
49 private static final String HKEY_PERFORMANCE_TEXT = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009";
50 private static final String COUNTER = "Counter";
51 private static final Map<String, Integer> COUNTER_INDEX_MAP = mapCounterIndicesFromRegistry();
52
53 private static int maxPerfBufferSize = 16384;
54
55 private HkeyPerformanceDataUtil() {
56 }
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public static <T extends Enum<T> & PdhCounterWildcardProperty> Triplet<List<Map<T, Object>>, Long, Long> readPerfDataFromRegistry(
72 String objectName, Class<T> counterEnum) {
73
74
75 Pair<Integer, EnumMap<T, Integer>> indices = getCounterIndices(objectName, counterEnum);
76 if (indices == null) {
77 return null;
78 }
79
80
81 try (Memory pPerfData = readPerfDataBuffer(objectName)) {
82 if (pPerfData == null) {
83 return null;
84 }
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 PERF_DATA_BLOCK perfData = new PERF_DATA_BLOCK(pPerfData.share(0));
102 long perfTime100nSec = perfData.PerfTime100nSec.getValue();
103 long now = FILETIME.filetimeToDate((int) (perfTime100nSec >> 32), (int) (perfTime100nSec & 0xffffffffL))
104 .getTime();
105
106
107 long perfObjectOffset = perfData.HeaderLength;
108 for (int obj = 0; obj < perfData.NumObjectTypes; obj++) {
109 PERF_OBJECT_TYPE perfObject = new PERF_OBJECT_TYPE(pPerfData.share(perfObjectOffset));
110
111
112
113 if (perfObject.ObjectNameTitleIndex == COUNTER_INDEX_MAP.get(objectName).intValue()) {
114
115
116
117 long perfCounterOffset = perfObjectOffset + perfObject.HeaderLength;
118
119 Map<Integer, Integer> counterOffsetMap = new HashMap<>();
120 Map<Integer, Integer> counterSizeMap = new HashMap<>();
121 for (int counter = 0; counter < perfObject.NumCounters; counter++) {
122 PERF_COUNTER_DEFINITION perfCounter = new PERF_COUNTER_DEFINITION(
123 pPerfData.share(perfCounterOffset));
124 counterOffsetMap.put(perfCounter.CounterNameTitleIndex, perfCounter.CounterOffset);
125 counterSizeMap.put(perfCounter.CounterNameTitleIndex, perfCounter.CounterSize);
126
127 perfCounterOffset += perfCounter.ByteLength;
128 }
129
130
131
132 long perfInstanceOffset = perfObjectOffset + perfObject.DefinitionLength;
133
134
135 List<Map<T, Object>> counterMaps = new ArrayList<>(perfObject.NumInstances);
136 for (int inst = 0; inst < perfObject.NumInstances; inst++) {
137 PERF_INSTANCE_DEFINITION perfInstance = new PERF_INSTANCE_DEFINITION(
138 pPerfData.share(perfInstanceOffset));
139 long perfCounterBlockOffset = perfInstanceOffset + perfInstance.ByteLength;
140
141 Map<T, Object> counterMap = new EnumMap<>(counterEnum);
142 T[] counterKeys = counterEnum.getEnumConstants();
143
144
145 counterMap.put(counterKeys[0],
146 pPerfData.getWideString(perfInstanceOffset + perfInstance.NameOffset));
147 for (int i = 1; i < counterKeys.length; i++) {
148 T key = counterKeys[i];
149 int keyIndex = COUNTER_INDEX_MAP.get(key.getCounter());
150
151 int size = counterSizeMap.getOrDefault(keyIndex, 0);
152
153
154 if (size == 4) {
155 counterMap.put(key,
156 pPerfData.getInt(perfCounterBlockOffset + counterOffsetMap.get(keyIndex)));
157 } else if (size == 8) {
158 counterMap.put(key,
159 pPerfData.getLong(perfCounterBlockOffset + counterOffsetMap.get(keyIndex)));
160 } else {
161
162 return null;
163 }
164 }
165 counterMaps.add(counterMap);
166
167
168
169
170
171
172
173 perfInstanceOffset = perfCounterBlockOffset
174 + new PERF_COUNTER_BLOCK(pPerfData.share(perfCounterBlockOffset)).ByteLength;
175 }
176
177
178 return new Triplet<>(counterMaps, perfTime100nSec, now);
179 }
180
181 perfObjectOffset += perfObject.TotalByteLength;
182 }
183 }
184
185 return null;
186 }
187
188
189
190
191
192
193
194
195
196
197
198
199 private static <T extends Enum<T> & PdhCounterWildcardProperty> Pair<Integer, EnumMap<T, Integer>> getCounterIndices(
200 String objectName, Class<T> counterEnum) {
201 if (!COUNTER_INDEX_MAP.containsKey(objectName)) {
202 LOG.debug("Couldn't find counter index of {}.", objectName);
203 return null;
204 }
205 int counterIndex = COUNTER_INDEX_MAP.get(objectName);
206 T[] enumConstants = counterEnum.getEnumConstants();
207 EnumMap<T, Integer> indexMap = new EnumMap<>(counterEnum);
208
209
210 for (int i = 1; i < enumConstants.length; i++) {
211 T key = enumConstants[i];
212 String counterName = key.getCounter();
213 if (!COUNTER_INDEX_MAP.containsKey(counterName)) {
214 LOG.debug("Couldn't find counter index of {}.", counterName);
215 return null;
216 }
217 indexMap.put(key, COUNTER_INDEX_MAP.get(counterName));
218 }
219
220 return new Pair<>(counterIndex, indexMap);
221 }
222
223
224
225
226
227
228
229
230 private static synchronized Memory readPerfDataBuffer(String objectName) {
231
232 String objectIndexStr = Integer.toString(COUNTER_INDEX_MAP.get(objectName));
233
234
235
236 try (CloseableIntByReference lpcbData = new CloseableIntByReference(maxPerfBufferSize)) {
237 Memory pPerfData = new Memory(maxPerfBufferSize);
238 int ret = Advapi32.INSTANCE.RegQueryValueEx(WinReg.HKEY_PERFORMANCE_DATA, objectIndexStr, 0, null,
239 pPerfData, lpcbData);
240 if (ret != WinError.ERROR_SUCCESS && ret != WinError.ERROR_MORE_DATA) {
241 LOG.error("Error reading performance data from registry for {}.", objectName);
242 pPerfData.close();
243 return null;
244 }
245
246 while (ret == WinError.ERROR_MORE_DATA) {
247 maxPerfBufferSize += 8192;
248 lpcbData.setValue(maxPerfBufferSize);
249 pPerfData.close();
250 pPerfData = new Memory(maxPerfBufferSize);
251 ret = Advapi32.INSTANCE.RegQueryValueEx(WinReg.HKEY_PERFORMANCE_DATA, objectIndexStr, 0, null,
252 pPerfData, lpcbData);
253 }
254 return pPerfData;
255 }
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269
270 @SuppressForbidden(reason = "Catching the error here")
271 private static Map<String, Integer> mapCounterIndicesFromRegistry() {
272 HashMap<String, Integer> indexMap = new HashMap<>();
273 try {
274 String[] counterText = Advapi32Util.registryGetStringArray(WinReg.HKEY_LOCAL_MACHINE, HKEY_PERFORMANCE_TEXT,
275 COUNTER);
276 for (int i = 1; i < counterText.length; i += 2) {
277 indexMap.putIfAbsent(counterText[i], Integer.parseInt(counterText[i - 1]));
278 }
279 } catch (Win32Exception we) {
280 LOG.error(
281 "Unable to locate English counter names in registry Perflib 009. Counters may need to be rebuilt: ",
282 we);
283 } catch (NumberFormatException nfe) {
284
285 LOG.error("Unable to parse English counter names in registry Perflib 009.");
286 }
287 return Collections.unmodifiableMap(indexMap);
288 }
289 }