1
2
3
4
5 package oshi.util.platform.windows;
6
7 import java.util.EnumMap;
8 import java.util.Locale;
9 import java.util.Map;
10 import java.util.Objects;
11 import java.util.Set;
12 import java.util.concurrent.ConcurrentHashMap;
13
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import com.sun.jna.platform.win32.PdhUtil;
18 import com.sun.jna.platform.win32.PdhUtil.PdhException;
19 import com.sun.jna.platform.win32.VersionHelpers;
20 import com.sun.jna.platform.win32.Win32Exception;
21 import com.sun.jna.platform.win32.COM.Wbemcli;
22 import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiQuery;
23 import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
24
25 import oshi.annotation.concurrent.ThreadSafe;
26 import oshi.util.platform.windows.PerfDataUtil.PerfCounter;
27
28
29
30
31 @ThreadSafe
32 public final class PerfCounterQuery {
33
34 private static final Logger LOG = LoggerFactory.getLogger(PerfCounterQuery.class);
35
36 private static final boolean IS_VISTA_OR_GREATER = VersionHelpers.IsWindowsVistaOrGreater();
37
38
39 private static final Set<String> FAILED_QUERY_CACHE = ConcurrentHashMap.newKeySet();
40
41
42 private static final ConcurrentHashMap<String, String> LOCALIZE_CACHE = new ConcurrentHashMap<>();
43
44
45
46
47 public static final String TOTAL_INSTANCE = "_Total";
48 public static final String TOTAL_OR_IDLE_INSTANCES = "_Total|Idle";
49 public static final String TOTAL_INSTANCES = "*_Total";
50 public static final String NOT_TOTAL_INSTANCE = "^" + TOTAL_INSTANCE;
51 public static final String NOT_TOTAL_INSTANCES = "^" + TOTAL_INSTANCES;
52
53 private PerfCounterQuery() {
54 }
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 public static <T extends Enum<T>> Map<T, Long> queryValues(Class<T> propertyEnum, String perfObject,
71 String perfWmiClass) {
72 if (!FAILED_QUERY_CACHE.contains(perfObject)) {
73 Map<T, Long> valueMap = queryValuesFromPDH(propertyEnum, perfObject);
74 if (!valueMap.isEmpty()) {
75 return valueMap;
76 }
77
78 LOG.info("Disabling further attempts to query {}.", perfObject);
79 FAILED_QUERY_CACHE.add(perfObject);
80 }
81 return queryValuesFromWMI(propertyEnum, perfWmiClass);
82 }
83
84
85
86
87
88
89
90
91
92
93
94
95
96 public static <T extends Enum<T>> Map<T, Long> queryValuesFromPDH(Class<T> propertyEnum, String perfObject) {
97 T[] props = propertyEnum.getEnumConstants();
98
99 String perfObjectLocalized = PerfCounterQuery.localizeIfNeeded(perfObject, false);
100 EnumMap<T, PerfCounter> counterMap = new EnumMap<>(propertyEnum);
101 EnumMap<T, Long> valueMap = new EnumMap<>(propertyEnum);
102 try (PerfCounterQueryHandler pdhQueryHandler = new PerfCounterQueryHandler()) {
103
104 for (T prop : props) {
105 PerfCounter counter = PerfDataUtil.createCounter(perfObjectLocalized,
106 ((PdhCounterProperty) prop).getInstance(), ((PdhCounterProperty) prop).getCounter());
107 counterMap.put(prop, counter);
108 if (!pdhQueryHandler.addCounterToQuery(counter)) {
109 return valueMap;
110 }
111 }
112
113 if (0 < pdhQueryHandler.updateQuery()) {
114 for (T prop : props) {
115 valueMap.put(prop, pdhQueryHandler.queryCounter(counterMap.get(prop)));
116 }
117 }
118 }
119 return valueMap;
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133 public static <T extends Enum<T>> Map<T, Long> queryValuesFromWMI(Class<T> propertyEnum, String wmiClass) {
134 WmiQuery<T> query = new WmiQuery<>(wmiClass, propertyEnum);
135 WmiResult<T> result = Objects.requireNonNull(WmiQueryHandler.createInstance()).queryWMI(query);
136 EnumMap<T, Long> valueMap = new EnumMap<>(propertyEnum);
137 if (result.getResultCount() > 0) {
138 for (T prop : propertyEnum.getEnumConstants()) {
139 switch (result.getCIMType(prop)) {
140 case Wbemcli.CIM_UINT16:
141 valueMap.put(prop, (long) WmiUtil.getUint16(result, prop, 0));
142 break;
143 case Wbemcli.CIM_UINT32:
144 valueMap.put(prop, WmiUtil.getUint32asLong(result, prop, 0));
145 break;
146 case Wbemcli.CIM_UINT64:
147 valueMap.put(prop, WmiUtil.getUint64(result, prop, 0));
148 break;
149 case Wbemcli.CIM_DATETIME:
150 valueMap.put(prop, WmiUtil.getDateTime(result, prop, 0).toInstant().toEpochMilli());
151 break;
152 default:
153 throw new ClassCastException("Unimplemented CIM Type Mapping.");
154 }
155 }
156 }
157 return valueMap;
158 }
159
160
161
162
163
164
165
166
167
168
169
170
171 public static String localizeIfNeeded(String perfObject, boolean force) {
172 return !force && IS_VISTA_OR_GREATER ? perfObject
173 : LOCALIZE_CACHE.computeIfAbsent(perfObject, PerfCounterQuery::localizeUsingPerfIndex);
174 }
175
176 private static String localizeUsingPerfIndex(String perfObject) {
177 String localized = perfObject;
178 try {
179 localized = PdhUtil.PdhLookupPerfNameByIndex(null, PdhUtil.PdhLookupPerfIndexByEnglishName(perfObject));
180 } catch (Win32Exception e) {
181 LOG.warn(
182 "Unable to locate English counter names in registry Perflib 009. Assuming English counters. Error {}. {}",
183 String.format(Locale.ROOT, "0x%x", e.getHR().intValue()),
184 "See https://support.microsoft.com/en-us/help/300956/how-to-manually-rebuild-performance-counter-library-values");
185 } catch (PdhException e) {
186 LOG.debug("Unable to localize {} performance counter. Error {}.", perfObject,
187 String.format(Locale.ROOT, "0x%x", e.getErrorCode()));
188 }
189 if (localized.isEmpty()) {
190 return perfObject;
191 }
192 LOG.debug("Localized {} to {}", perfObject, localized);
193 return localized;
194 }
195
196
197
198
199 public interface PdhCounterProperty {
200
201
202
203 String getInstance();
204
205
206
207
208 String getCounter();
209 }
210 }