1414LOAD_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
2020COUNTER_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