summaryrefslogtreecommitdiffstats
path: root/Lib/test/libregrtest/win_utils.py
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@redhat.com>2019-10-01 10:29:36 (GMT)
committerGitHub <noreply@github.com>2019-10-01 10:29:36 (GMT)
commit982bfa4da07b2e5749a0f4e68f99e972bcc3a549 (patch)
tree9581db8d39159df09c590df363341da654cc965e /Lib/test/libregrtest/win_utils.py
parent8462a4936b3a551dc546a6adea04a70b0a07ca67 (diff)
downloadcpython-982bfa4da07b2e5749a0f4e68f99e972bcc3a549.zip
cpython-982bfa4da07b2e5749a0f4e68f99e972bcc3a549.tar.gz
cpython-982bfa4da07b2e5749a0f4e68f99e972bcc3a549.tar.bz2
bpo-36670: Multiple regrtest bugfixes (GH-16511)
* Windows: Fix counter name in WindowsLoadTracker. Counter names are localized: use the registry to get the counter name. Original change written by Lorenz Mende. * Regrtest.main() now ensures that the Windows load tracker is also killed if an exception is raised * TestWorkerProcess now ensures that worker processes are no longer running before exiting: kill also worker processes when an exception is raised. * Enhance regrtest messages and warnings: include test name, duration, add a worker identifier, etc. * Rename MultiprocessRunner to TestWorkerProcess * Use print_warning() to display warnings. Co-Authored-By: Lorenz Mende <Lorenz.mende@gmail.com>
Diffstat (limited to 'Lib/test/libregrtest/win_utils.py')
-rw-r--r--Lib/test/libregrtest/win_utils.py67
1 files changed, 52 insertions, 15 deletions
diff --git a/Lib/test/libregrtest/win_utils.py b/Lib/test/libregrtest/win_utils.py
index ec2d6c6..f0c17b9 100644
--- a/Lib/test/libregrtest/win_utils.py
+++ b/Lib/test/libregrtest/win_utils.py
@@ -3,16 +3,22 @@ import msvcrt
import os
import subprocess
import uuid
+import winreg
from test import support
+from test.libregrtest.utils import print_warning
# Max size of asynchronous reads
BUFSIZE = 8192
# Exponential damping factor (see below)
LOAD_FACTOR_1 = 0.9200444146293232478931553241
+
# Seconds per measurement
SAMPLING_INTERVAL = 5
-COUNTER_NAME = r'\System\Processor Queue Length'
+# Windows registry subkey of HKEY_LOCAL_MACHINE where the counter names
+# of typeperf are registered
+COUNTER_REGISTRY_KEY = (r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"
+ r"\Perflib\CurrentLanguage")
class WindowsLoadTracker():
@@ -25,7 +31,8 @@ class WindowsLoadTracker():
def __init__(self):
self.load = 0.0
- self.p = None
+ self.counter_name = ''
+ self.popen = None
self.start()
def start(self):
@@ -55,31 +62,46 @@ class WindowsLoadTracker():
overlap.GetOverlappedResult(True)
# Spawn off the load monitor
- command = ['typeperf', COUNTER_NAME, '-si', str(SAMPLING_INTERVAL)]
- self.p = subprocess.Popen(command, stdout=command_stdout, cwd=support.SAVEDCWD)
+ counter_name = self._get_counter_name()
+ command = ['typeperf', counter_name, '-si', str(SAMPLING_INTERVAL)]
+ self.popen = subprocess.Popen(' '.join(command), stdout=command_stdout, cwd=support.SAVEDCWD)
# Close our copy of the write end of the pipe
os.close(command_stdout)
+ def _get_counter_name(self):
+ # accessing the registry to get the counter localization name
+ with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, COUNTER_REGISTRY_KEY) as perfkey:
+ counters = winreg.QueryValueEx(perfkey, 'Counter')[0]
+
+ # Convert [key1, value1, key2, value2, ...] list
+ # to {key1: value1, key2: value2, ...} dict
+ counters = iter(counters)
+ counters_dict = dict(zip(counters, counters))
+
+ # System counter has key '2' and Processor Queue Length has key '44'
+ system = counters_dict['2']
+ process_queue_length = counters_dict['44']
+ return f'"\\{system}\\{process_queue_length}"'
+
def close(self):
- if self.p is None:
+ if self.popen is None:
return
- self.p.kill()
- self.p.wait()
- self.p = None
+ self.popen.kill()
+ self.popen.wait()
+ self.popen = None
def __del__(self):
self.close()
def read_output(self):
- import _winapi
-
overlapped, _ = _winapi.ReadFile(self.pipe, BUFSIZE, True)
bytes_read, res = overlapped.GetOverlappedResult(False)
if res != 0:
return
- return overlapped.getbuffer().decode()
+ output = overlapped.getbuffer()
+ return output.decode('oem', 'replace')
def getloadavg(self):
typeperf_output = self.read_output()
@@ -89,14 +111,29 @@ class WindowsLoadTracker():
# Process the backlog of load values
for line in typeperf_output.splitlines():
+ # Ignore the initial header:
+ # "(PDH-CSV 4.0)","\\\\WIN\\System\\Processor Queue Length"
+ if '\\\\' in line:
+ continue
+
+ # Ignore blank lines
+ if not line.strip():
+ continue
+
# typeperf outputs in a CSV format like this:
# "07/19/2018 01:32:26.605","3.000000"
- toks = line.split(',')
- # Ignore blank lines and the initial header
- if line.strip() == '' or (COUNTER_NAME in line) or len(toks) != 2:
+ # (date, process queue length)
+ try:
+ tokens = line.split(',')
+ if len(tokens) != 2:
+ raise ValueError
+
+ value = tokens[1].replace('"', '')
+ load = float(value)
+ except ValueError:
+ print_warning("Failed to parse typeperf output: %a" % line)
continue
- load = float(toks[1].replace('"', ''))
# We use an exponentially weighted moving average, imitating the
# load calculation on Unix systems.
# https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation