Skip to content

bpo-36670: fixed encoding issue with libregrtest on win systems #15488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 30 additions & 20 deletions Lib/test/libregrtest/win_utils.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import _winapi
import winreg
import msvcrt
import os
import subprocess
import uuid
from test import support


# 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 = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\CurrentLanguage"


class WindowsLoadTracker():
Expand All @@ -25,14 +26,15 @@ class WindowsLoadTracker():

def __init__(self):
self.load = 0.0
self.p = None
self.typePerfProcess = None
self.counter_name = ''
self.start()

def start(self):
# Create a named pipe which allows for asynchronous IO in Windows
pipe_name = r'\\.\pipe\typeperf_output_' + str(uuid.uuid4())
pipe_name = r'\\.\pipe\typeperf_output_' + str(uuid.uuid4())

open_mode = _winapi.PIPE_ACCESS_INBOUND
open_mode = _winapi.PIPE_ACCESS_INBOUND
open_mode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
open_mode |= _winapi.FILE_FLAG_OVERLAPPED

Expand All @@ -55,31 +57,39 @@ def start(self):
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)

# Close our copy of the write end of the pipe
os.close(command_stdout)
self.counter_name = self._get_counter_name()
command = ['typeperf', self.counter_name, '-si', str(SAMPLING_INTERVAL)]
self.typePerfProcess = subprocess.Popen(' '.join(command), stdout=command_stdout, cwd=support.SAVEDCWD)

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:
counter_list = winreg.QueryValueEx(perfkey, 'Counter')[0]
# converting the alternating counter list to dict
i = iter(counter_list)
counter_dict = dict(zip(i, i))
# system counter is at '2' and processor queue length is '44'
counter_name = r'"\{}\{}"'.format(counter_dict['2'], counter_dict['44'])
return counter_name

def close(self):
if self.p is None:
if self.typePerfProcess is None:
return
self.p.kill()
self.p.wait()
self.p = None
self.typePerfProcess.kill()
self.typePerfProcess.wait()
self.typePerfProcess = 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()
response = overlapped.getbuffer()
# output is windows 'oem' encoded
return response.decode(encoding='oem', errors='ignore')

def getloadavg(self):
typeperf_output = self.read_output()
Expand All @@ -93,7 +103,7 @@ def getloadavg(self):
# "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:
if line.strip() == '' or '\\\\' in line or len(toks) != 2:
continue

load = float(toks[1].replace('"', ''))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're here, could you handle the case where float raises a ValueError (or maybe a TypeError? But I think we're guaranteed to only get ValueErrors at this point)? We can continue to the next line in that case.

Copy link
Contributor Author

@LorenzMende LorenzMende Aug 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi zooba,
what about

Suggested change
load = float(toks[1].replace('"', ''))
try:
toks = line.strip().split(',')
load = float(toks[-1].replace('"', ''))
except:
continue

this would handle the ValueError/ TypeError as well as take care of the line parsing.
Or is the bare exception handling unfavourable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LorenzMende That looks like a good option to me. You'll have to merge the change yourself though - I don't have permissions to your PR branch.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
regrtest: fixed typeperf encoding for cpu usage evaluation on win 10, lookup
of correct typeperf key in winreg removed closing of the stdout pipe