| 
									
										
										
										
											2019-04-09 08:20:41 -04:00
										 |  |  | import _winapi | 
					
						
							|  |  |  | import msvcrt | 
					
						
							| 
									
										
										
										
											2019-04-26 04:08:53 +02:00
										 |  |  | import os | 
					
						
							|  |  |  | import subprocess | 
					
						
							| 
									
										
										
										
											2019-04-09 08:20:41 -04:00
										 |  |  | 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' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WindowsLoadTracker(): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     This class asynchronously interacts with the `typeperf` command to read | 
					
						
							| 
									
										
										
										
											2019-07-31 08:16:13 +10:00
										 |  |  |     the system load on Windows. Multiprocessing and threads can't be used | 
					
						
							| 
									
										
										
										
											2019-04-09 08:20:41 -04:00
										 |  |  |     here because they interfere with the test suite's cases for those | 
					
						
							|  |  |  |     modules. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self.load = 0.0 | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:33 -07:00
										 |  |  |         self.p = None | 
					
						
							| 
									
										
										
										
											2019-04-09 08:20:41 -04:00
										 |  |  |         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()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         open_mode =  _winapi.PIPE_ACCESS_INBOUND | 
					
						
							|  |  |  |         open_mode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE | 
					
						
							|  |  |  |         open_mode |= _winapi.FILE_FLAG_OVERLAPPED | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # This is the read end of the pipe, where we will be grabbing output | 
					
						
							|  |  |  |         self.pipe = _winapi.CreateNamedPipe( | 
					
						
							|  |  |  |             pipe_name, open_mode, _winapi.PIPE_WAIT, | 
					
						
							|  |  |  |             1, BUFSIZE, BUFSIZE, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         # The write end of the pipe which is passed to the created process | 
					
						
							|  |  |  |         pipe_write_end = _winapi.CreateFile( | 
					
						
							|  |  |  |             pipe_name, _winapi.GENERIC_WRITE, 0, _winapi.NULL, | 
					
						
							|  |  |  |             _winapi.OPEN_EXISTING, 0, _winapi.NULL | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         # Open up the handle as a python file object so we can pass it to | 
					
						
							|  |  |  |         # subprocess | 
					
						
							|  |  |  |         command_stdout = msvcrt.open_osfhandle(pipe_write_end, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Connect to the read end of the pipe in overlap/async mode | 
					
						
							|  |  |  |         overlap = _winapi.ConnectNamedPipe(self.pipe, overlapped=True) | 
					
						
							|  |  |  |         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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-26 11:12:26 +02:00
										 |  |  |     def close(self): | 
					
						
							|  |  |  |         if self.p is None: | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2019-04-09 08:20:41 -04:00
										 |  |  |         self.p.kill() | 
					
						
							|  |  |  |         self.p.wait() | 
					
						
							| 
									
										
										
										
											2019-04-26 11:12:26 +02:00
										 |  |  |         self.p = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __del__(self): | 
					
						
							|  |  |  |         self.close() | 
					
						
							| 
									
										
										
										
											2019-04-09 08:20:41 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getloadavg(self): | 
					
						
							|  |  |  |         typeperf_output = self.read_output() | 
					
						
							|  |  |  |         # Nothing to update, just return the current load | 
					
						
							|  |  |  |         if not typeperf_output: | 
					
						
							|  |  |  |             return self.load | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Process the backlog of load values | 
					
						
							|  |  |  |         for line in typeperf_output.splitlines(): | 
					
						
							|  |  |  |             # 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: | 
					
						
							|  |  |  |                 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 | 
					
						
							|  |  |  |             new_load = self.load * LOAD_FACTOR_1 + load * (1.0 - LOAD_FACTOR_1) | 
					
						
							|  |  |  |             self.load = new_load | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return self.load |