14
14
LOAD_FACTOR_1 = 0.9200444146293232478931553241
15
15
16
16
# Seconds per measurement
17
- SAMPLING_INTERVAL = 5
17
+ SAMPLING_INTERVAL = 1
18
18
# Windows registry subkey of HKEY_LOCAL_MACHINE where the counter names
19
19
# of typeperf are registered
20
20
COUNTER_REGISTRY_KEY = (r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"
@@ -32,7 +32,7 @@ class WindowsLoadTracker():
32
32
def __init__ (self ):
33
33
self .load = 0.0
34
34
self .counter_name = ''
35
- self ._buffer = b ''
35
+ self ._buffer = ''
36
36
self .popen = None
37
37
self .start ()
38
38
@@ -95,44 +95,60 @@ def close(self):
95
95
def __del__ (self ):
96
96
self .close ()
97
97
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 ):
99
113
overlapped , _ = _winapi .ReadFile (self .pipe , BUFSIZE , True )
100
114
bytes_read , res = overlapped .GetOverlappedResult (False )
101
115
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
108
136
109
137
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 ()
114
140
115
- # Process the backlog of load values
116
- for line in typeperf_output .splitlines ():
117
141
# Ignore the initial header:
118
142
# "(PDH-CSV 4.0)","\\\\WIN\\System\\Processor Queue Length"
119
- if '\\ \\ ' in line :
143
+ if 'PDH-CSV ' in line :
120
144
continue
121
145
122
146
# Ignore blank lines
123
- if not line . strip () :
147
+ if not line :
124
148
continue
125
149
126
- # typeperf outputs in a CSV format like this:
127
- # "07/19/2018 01:32:26.605","3.000000"
128
- # (date, process queue length)
129
150
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 )
136
152
except ValueError :
137
153
print_warning ("Failed to parse typeperf output: %a" % line )
138
154
continue
0 commit comments