Skip to content

Commit c65119d

Browse files
authored
bpo-36670: Enhance regrtest WindowsLoadTracker (GH-16553)
The last line is now passed to the parser even if it does not end with a newline, but only if it's a valid value.
1 parent 3e04cd2 commit c65119d

File tree

1 file changed

+42
-26
lines changed

1 file changed

+42
-26
lines changed

Lib/test/libregrtest/win_utils.py

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
LOAD_FACTOR_1 = 0.9200444146293232478931553241
1515

1616
# Seconds per measurement
17-
SAMPLING_INTERVAL = 5
17+
SAMPLING_INTERVAL = 1
1818
# Windows registry subkey of HKEY_LOCAL_MACHINE where the counter names
1919
# of typeperf are registered
2020
COUNTER_REGISTRY_KEY = (r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"
@@ -32,7 +32,7 @@ class WindowsLoadTracker():
3232
def __init__(self):
3333
self.load = 0.0
3434
self.counter_name = ''
35-
self._buffer = b''
35+
self._buffer = ''
3636
self.popen = None
3737
self.start()
3838

@@ -95,44 +95,60 @@ def close(self):
9595
def __del__(self):
9696
self.close()
9797

98-
def read_output(self):
98+
def _parse_line(self, line):
99+
# typeperf outputs in a CSV format like this:
100+
# "07/19/2018 01:32:26.605","3.000000"
101+
# (date, process queue length)
102+
tokens = line.split(',')
103+
if len(tokens) != 2:
104+
raise ValueError
105+
106+
value = tokens[1]
107+
if not value.startswith('"') or not value.endswith('"'):
108+
raise ValueError
109+
value = value[1:-1]
110+
return float(value)
111+
112+
def read_lines(self):
99113
overlapped, _ = _winapi.ReadFile(self.pipe, BUFSIZE, True)
100114
bytes_read, res = overlapped.GetOverlappedResult(False)
101115
if res != 0:
102-
return
103-
104-
# self._buffer stores an incomplete line
105-
output = self._buffer + overlapped.getbuffer()
106-
output, _, self._buffer = output.rpartition(b'\n')
107-
return output.decode('oem', 'replace')
116+
return ()
117+
118+
output = overlapped.getbuffer()
119+
output = output.decode('oem', 'replace')
120+
output = self._buffer + output
121+
lines = output.splitlines(True)
122+
123+
# bpo-36670: typeperf only writes a newline *before* writing a value,
124+
# not after. Sometimes, the written line in incomplete (ex: only
125+
# timestamp, without the process queue length). Only pass the last line
126+
# to the parser if it's a valid value, otherwise store it in
127+
# self._buffer.
128+
try:
129+
self._parse_line(lines[-1])
130+
except ValueError:
131+
self._buffer = lines.pop(-1)
132+
else:
133+
self._buffer = ''
134+
135+
return lines
108136

109137
def getloadavg(self):
110-
typeperf_output = self.read_output()
111-
# Nothing to update, just return the current load
112-
if not typeperf_output:
113-
return self.load
138+
for line in self.read_lines():
139+
line = line.rstrip()
114140

115-
# Process the backlog of load values
116-
for line in typeperf_output.splitlines():
117141
# Ignore the initial header:
118142
# "(PDH-CSV 4.0)","\\\\WIN\\System\\Processor Queue Length"
119-
if '\\\\' in line:
143+
if 'PDH-CSV' in line:
120144
continue
121145

122146
# Ignore blank lines
123-
if not line.strip():
147+
if not line:
124148
continue
125149

126-
# typeperf outputs in a CSV format like this:
127-
# "07/19/2018 01:32:26.605","3.000000"
128-
# (date, process queue length)
129150
try:
130-
tokens = line.split(',')
131-
if len(tokens) != 2:
132-
raise ValueError
133-
134-
value = tokens[1].replace('"', '')
135-
load = float(value)
151+
load = self._parse_line(line)
136152
except ValueError:
137153
print_warning("Failed to parse typeperf output: %a" % line)
138154
continue

0 commit comments

Comments
 (0)