Skip to content

Commit 9501ade

Browse files
authored
[Windows] fix pdh counters on localized versions of windows (#3713)
1 parent 64a7b15 commit 9501ade

File tree

1 file changed

+59
-10
lines changed

1 file changed

+59
-10
lines changed

checks/libs/win/winpdh.py

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Unless explicitly stated otherwise all files in this repository are licensed
22
# under the Apache License Version 2.0.
33
# This product includes software developed at Datadog (https://www.datadoghq.com/).
4-
# Copyright 2017 Datadog, Inc.
4+
# Copyright 2018 Datadog, Inc.
55

6+
from collections import defaultdict
67
import time
78
import win32pdh
89
import _winreg
@@ -13,16 +14,23 @@
1314
SINGLE_INSTANCE_KEY = "__single_instance"
1415
class WinPDHCounter(object):
1516
# store the dictionary of pdh counter names
16-
pdh_counter_dict = {}
17+
pdh_counter_dict = defaultdict(list)
1718

1819
def __init__(self, class_name, counter_name, log, instance_name = None, machine_name = None, precision=None):
20+
self.logger = log
1921
self._get_counter_dictionary()
20-
self._class_name = win32pdh.LookupPerfNameByIndex(None, int(WinPDHCounter.pdh_counter_dict[class_name]))
21-
self._counter_name = win32pdh.LookupPerfNameByIndex(None, int(WinPDHCounter.pdh_counter_dict[counter_name]))
22+
class_name_index_list = WinPDHCounter.pdh_counter_dict[class_name]
23+
if len(class_name_index_list) == 0:
24+
self.logger.warn("Class %s was not in counter name list, attempting english counter" % class_name)
25+
self._class_name = class_name
26+
else:
27+
if len(class_name_index_list) > 1:
28+
self.logger.warn("Class %s had multiple (%d) indices, using first" % (class_name, len(class_name_index_list)))
29+
self._class_name = win32pdh.LookupPerfNameByIndex(None, int(class_name_index_list[0]))
2230

2331
self._is_single_instance = False
2432
self.hq = win32pdh.OpenQuery()
25-
self.logger = log
33+
2634
self.counterdict = {}
2735
if precision is None:
2836
self._precision = win32pdh.PDH_FMT_DOUBLE
@@ -31,7 +39,7 @@ def __init__(self, class_name, counter_name, log, instance_name = None, machine_
3139
counters, instances = win32pdh.EnumObjectItems(None, machine_name, self._class_name, win32pdh.PERF_DETAIL_WIZARD)
3240
if instance_name is None and len(instances) > 0:
3341
for inst in instances:
34-
path = win32pdh.MakeCounterPath((machine_name, self._class_name, inst, None, 0, self._counter_name))
42+
path = self._make_counter_path(machine_name, counter_name, inst, counters)
3543
try:
3644
self.counterdict[inst] = win32pdh.AddCounter(self.hq, path)
3745
except:
@@ -57,7 +65,7 @@ def __init__(self, class_name, counter_name, log, instance_name = None, machine_
5765
instance_name, class_name
5866
))
5967
return
60-
path = win32pdh.MakeCounterPath((machine_name, self._class_name, instance_name, None, 0, self._counter_name))
68+
path = self._make_counter_path(machine_name, counter_name, instance_name, counters)
6169
try:
6270
self.logger.debug("Path: %s\n" % unicode(path))
6371
except:
@@ -97,7 +105,6 @@ def get_all_values(self):
97105

98106
for inst, counter_handle in self.counterdict.iteritems():
99107
try:
100-
self.logger.info("Getting %s %d" % (inst, self._precision))
101108
t, val = win32pdh.GetFormattedCounterValue(counter_handle, self._precision)
102109
ret[inst] = val
103110
except Exception as e:
@@ -142,6 +149,48 @@ def _get_counter_dictionary(self):
142149
idx = 0
143150
idx_max = len(val)
144151
while idx < idx_max:
145-
# counter index is idx + 1, counter name is ids
146-
WinPDHCounter.pdh_counter_dict[val[idx+1]] = val[idx]
152+
# counter index is idx , counter name is idx + 1
153+
WinPDHCounter.pdh_counter_dict[val[idx+1]].append(val[idx])
147154
idx += 2
155+
156+
def _make_counter_path(self, machine_name, counter_name, instance_name, counters):
157+
'''
158+
When handling non english versions, the counters don't work quite as documented.
159+
This is because strings like "Bytes Sent/sec" might appear multiple times in the
160+
english master, and might not have mappings for each index.
161+
162+
Search each index, and make sure the requested counter name actually appears in
163+
the list of available counters; that's the counter we'll use.
164+
'''
165+
counter_name_index_list = WinPDHCounter.pdh_counter_dict[counter_name]
166+
path = ""
167+
for index in counter_name_index_list:
168+
c = win32pdh.LookupPerfNameByIndex(None, int(index))
169+
if c is None or len(c) == 0:
170+
self.logger.debug("Index %s not found, skipping" % index)
171+
continue
172+
173+
# check to see if this counter is in the list of counters for this class
174+
if c not in counters:
175+
try:
176+
self.logger.debug("Index %s counter %s not in counter list" % (index, unicode(c)))
177+
except:
178+
# some unicode characters are not translatable here. Don't fail just
179+
# because we couldn't log
180+
self.logger.debug("Index %s not in counter list" % index)
181+
pass
182+
183+
continue
184+
185+
# see if we can create a counter
186+
try:
187+
path = win32pdh.MakeCounterPath((machine_name, self._class_name, instance_name, None, 0, c))
188+
self.logger.debug("Successfully created path %s" % index)
189+
break
190+
except:
191+
try:
192+
self.logger.info("Unable to make path with counter %s, trying next available" % unicode(c))
193+
except:
194+
self.logger.info("Unable to make path with counter index %s, trying next available" % index)
195+
pass
196+
return path

0 commit comments

Comments
 (0)