@@ -11,8 +11,9 @@ def __init__(self, rulefname, reportfname='', t=5, debug=False, datafname='', fu
11
11
self .rulefname = rulefname
12
12
self .reportfname = reportfname
13
13
self .rules = None
14
- self .collectlist = metrics
15
- self .metrics = set (metrics )
14
+ self .collectlist :str = metrics
15
+ self .metrics = self .__set_metrics (metrics )
16
+ self .skiplist = set ()
16
17
self .tolerance = t
17
18
18
19
self .workloads = [x for x in workload .split ("," ) if x ]
@@ -41,6 +42,12 @@ def __init__(self, rulefname, reportfname='', t=5, debug=False, datafname='', fu
41
42
self .debug = debug
42
43
self .fullrulefname = fullrulefname
43
44
45
+ def __set_metrics (self , metrics = '' ):
46
+ if metrics != '' :
47
+ return set (metrics .split ("," ))
48
+ else :
49
+ return set ()
50
+
44
51
def read_json (self , filename : str ) -> dict :
45
52
try :
46
53
with open (Path (filename ).resolve (), "r" ) as f :
@@ -113,7 +120,7 @@ def get_value(self, name:str, ridx:int = 0) -> list:
113
120
All future test(s) on this metric will fail.
114
121
115
122
@param name: name of the metric
116
- @returns: list with value found in self.results; list is empty when not value found.
123
+ @returns: list with value found in self.results; list is empty when value is not found.
117
124
"""
118
125
results = []
119
126
data = self .results [ridx ] if ridx in self .results else self .results [0 ]
@@ -123,7 +130,6 @@ def get_value(self, name:str, ridx:int = 0) -> list:
123
130
elif name .replace ('.' , '1' ).isdigit ():
124
131
results .append (float (name ))
125
132
else :
126
- self .errlist .append ("Metric '%s' is not collected or the value format is incorrect" % (name ))
127
133
self .ignoremetrics .add (name )
128
134
return results
129
135
@@ -138,27 +144,32 @@ def pos_val_test(self):
138
144
Failure: when metric value is negative or not provided.
139
145
Metrics with negative value will be added into the self.failtests['PositiveValueTest'] and self.ignoremetrics.
140
146
"""
141
- negmetric = set ()
142
- missmetric = set ()
147
+ negmetric = dict ()
143
148
pcnt = 0
144
149
tcnt = 0
150
+ rerun = list ()
145
151
for name , val in self .get_results ().items ():
146
- if val is None or val == '' :
147
- missmetric .add (name )
148
- self .errlist .append ("Metric '%s' is not collected" % (name ))
149
- elif val < 0 :
150
- negmetric .add ("{0}(={1:.4f})" .format (name , val ))
151
- self .collectlist [0 ].append (name )
152
+ if val < 0 :
153
+ negmetric [name ] = val
154
+ rerun .append (name )
152
155
else :
153
156
pcnt += 1
154
157
tcnt += 1
158
+ if len (rerun ) > 0 and len (rerun ) < 20 :
159
+ second_results = dict ()
160
+ self .second_test (rerun , second_results )
161
+ for name , val in second_results .items ():
162
+ if name not in negmetric : continue
163
+ if val >= 0 :
164
+ del negmetric [name ]
165
+ pcnt += 1
155
166
156
167
self .failtests ['PositiveValueTest' ]['Total Tests' ] = tcnt
157
168
self .failtests ['PositiveValueTest' ]['Passed Tests' ] = pcnt
158
- if len (negmetric ) or len ( missmetric ) > 0 :
159
- self .ignoremetrics .update (negmetric )
160
- self . ignoremetrics . update ( missmetric )
161
- self .failtests ['PositiveValueTest' ]['Failed Tests' ].append ({'NegativeValue' :list ( negmetric ), 'MissingValue' : list ( missmetric ) })
169
+ if len (negmetric . keys ()) :
170
+ self .ignoremetrics .update (negmetric . keys () )
171
+ negmessage = [ "{0}(={1:.4f})" . format ( name , val ) for name , val in negmetric . items ()]
172
+ self .failtests ['PositiveValueTest' ]['Failed Tests' ].append ({'NegativeValue' : negmessage })
162
173
163
174
return
164
175
@@ -259,21 +270,36 @@ def single_test(self, rule:dict):
259
270
metrics = rule ['Metrics' ]
260
271
passcnt = 0
261
272
totalcnt = 0
262
- faillist = []
273
+ faillist = list ()
274
+ failures = dict ()
275
+ rerun = list ()
263
276
for m in metrics :
264
277
totalcnt += 1
265
278
result = self .get_value (m ['Name' ])
266
- if len (result ) > 0 and self .check_bound (result [0 ], lbv , ubv , t ):
279
+ if len (result ) > 0 and self .check_bound (result [0 ], lbv , ubv , t ) or m [ 'Name' ] in self . skiplist :
267
280
passcnt += 1
268
281
else :
269
- faillist .append ({'MetricName' :m ['Name' ], 'CollectedValue' :result })
270
- self .collectlist [0 ].append (m ['Name' ])
282
+ failures [m ['Name' ]] = result
283
+ rerun .append (m ['Name' ])
284
+
285
+ if len (rerun ) > 0 and len (rerun ) < 20 :
286
+ second_results = dict ()
287
+ self .second_test (rerun , second_results )
288
+ for name , val in second_results .items ():
289
+ if name not in failures : continue
290
+ if self .check_bound (val , lbv , ubv , t ):
291
+ passcnt += 1
292
+ del failures [name ]
293
+ else :
294
+ failures [name ] = val
295
+ self .results [0 ][name ] = val
271
296
272
297
self .totalcnt += totalcnt
273
298
self .passedcnt += passcnt
274
299
self .failtests ['SingleMetricTest' ]['Total Tests' ] += totalcnt
275
300
self .failtests ['SingleMetricTest' ]['Passed Tests' ] += passcnt
276
- if len (faillist ) != 0 :
301
+ if len (failures .keys ()) != 0 :
302
+ faillist = [{'MetricName' :name , 'CollectedValue' :val } for name , val in failures .items ()]
277
303
self .failtests ['SingleMetricTest' ]['Failed Tests' ].append ({'RuleIndex' :rule ['RuleIndex' ],
278
304
'RangeLower' : rule ['RangeLower' ],
279
305
'RangeUpper' : rule ['RangeUpper' ],
@@ -316,7 +342,7 @@ def check_rule(self, testtype, metric_list):
316
342
return True
317
343
318
344
# Start of Collector and Converter
319
- def convert (self , data : list , idx : int ):
345
+ def convert (self , data : list , metricvalues : dict ):
320
346
"""
321
347
Convert collected metric data from the -j output to dict of {metric_name:value}.
322
348
"""
@@ -326,20 +352,29 @@ def convert(self, data: list, idx: int):
326
352
if "metric-unit" in result and result ["metric-unit" ] != "(null)" and result ["metric-unit" ] != "" :
327
353
name = result ["metric-unit" ].split (" " )[1 ] if len (result ["metric-unit" ].split (" " )) > 1 \
328
354
else result ["metric-unit" ]
329
- if idx not in self .results : self .results [idx ] = dict ()
330
- self .results [idx ][name .lower ()] = float (result ["metric-value" ])
355
+ metricvalues [name .lower ()] = float (result ["metric-value" ])
331
356
except ValueError as error :
332
357
continue
333
358
return
334
359
335
- def collect_perf (self , data_file : str , workload : str ):
360
+ def _run_perf (self , metric , workload : str ):
361
+ tool = 'perf'
362
+ command = [tool , 'stat' , '-j' , '-M' , f"{ metric } " , "-a" ]
363
+ wl = workload .split ()
364
+ command .extend (wl )
365
+ print (" " .join (command ))
366
+ cmd = subprocess .run (command , stderr = subprocess .PIPE , encoding = 'utf-8' )
367
+ data = [x + '}' for x in cmd .stderr .split ('}\n ' ) if x ]
368
+ return data
369
+
370
+
371
+ def collect_perf (self , workload : str ):
336
372
"""
337
373
Collect metric data with "perf stat -M" on given workload with -a and -j.
338
374
"""
339
375
self .results = dict ()
340
- tool = 'perf'
341
376
print (f"Starting perf collection" )
342
- print (f"Workload : { workload } " )
377
+ print (f"Long workload : { workload } " )
343
378
collectlist = dict ()
344
379
if self .collectlist != "" :
345
380
collectlist [0 ] = {x for x in self .collectlist .split ("," )}
@@ -353,17 +388,20 @@ def collect_perf(self, data_file: str, workload: str):
353
388
collectlist [rule ["RuleIndex" ]] = ["," .join (list (set (metrics )))]
354
389
355
390
for idx , metrics in collectlist .items ():
356
- if idx == 0 : wl = "sleep 0.5" . split ()
357
- else : wl = workload . split ()
391
+ if idx == 0 : wl = "true"
392
+ else : wl = workload
358
393
for metric in metrics :
359
- command = [tool , 'stat' , '-j' , '-M' , f"{ metric } " , "-a" ]
360
- command .extend (wl )
361
- print (" " .join (command ))
362
- cmd = subprocess .run (command , stderr = subprocess .PIPE , encoding = 'utf-8' )
363
- data = [x + '}' for x in cmd .stderr .split ('}\n ' ) if x ]
364
- self .convert (data , idx )
365
- self .collectlist = dict ()
366
- self .collectlist [0 ] = list ()
394
+ data = self ._run_perf (metric , wl )
395
+ if idx not in self .results : self .results [idx ] = dict ()
396
+ self .convert (data , self .results [idx ])
397
+ return
398
+
399
+ def second_test (self , collectlist , second_results ):
400
+ workload = self .workloads [self .wlidx ]
401
+ for metric in collectlist :
402
+ data = self ._run_perf (metric , workload )
403
+ self .convert (data , second_results )
404
+
367
405
# End of Collector and Converter
368
406
369
407
# Start of Rule Generator
@@ -381,7 +419,7 @@ def parse_perf_metrics(self):
381
419
if 'MetricName' not in m :
382
420
print ("Warning: no metric name" )
383
421
continue
384
- name = m ['MetricName' ]
422
+ name = m ['MetricName' ]. lower ()
385
423
self .metrics .add (name )
386
424
if 'ScaleUnit' in m and (m ['ScaleUnit' ] == '1%' or m ['ScaleUnit' ] == '100%' ):
387
425
self .pctgmetrics .add (name .lower ())
@@ -391,14 +429,12 @@ def parse_perf_metrics(self):
391
429
392
430
return
393
431
394
- def remove_unsupported_rules (self , rules , skiplist : set = None ):
395
- for m in skiplist :
396
- self .metrics .discard (m )
432
+ def remove_unsupported_rules (self , rules ):
397
433
new_rules = []
398
434
for rule in rules :
399
435
add_rule = True
400
436
for m in rule ["Metrics" ]:
401
- if m ["Name" ] not in self .metrics :
437
+ if m ["Name" ] in self . skiplist or m [ "Name" ] not in self .metrics :
402
438
add_rule = False
403
439
break
404
440
if add_rule :
@@ -415,15 +451,15 @@ def create_rules(self):
415
451
"""
416
452
data = self .read_json (self .rulefname )
417
453
rules = data ['RelationshipRules' ]
418
- skiplist = set (data ['SkipList' ])
419
- self .rules = self .remove_unsupported_rules (rules , skiplist )
454
+ self . skiplist = set ([ name . lower () for name in data ['SkipList' ] ])
455
+ self .rules = self .remove_unsupported_rules (rules )
420
456
pctgrule = {'RuleIndex' :0 ,
421
457
'TestType' :'SingleMetricTest' ,
422
458
'RangeLower' :'0' ,
423
459
'RangeUpper' : '100' ,
424
460
'ErrorThreshold' : self .tolerance ,
425
461
'Description' :'Metrics in percent unit have value with in [0, 100]' ,
426
- 'Metrics' : [{'Name' : m } for m in self .pctgmetrics ]}
462
+ 'Metrics' : [{'Name' : m . lower () } for m in self .pctgmetrics ]}
427
463
self .rules .append (pctgrule )
428
464
429
465
# Re-index all rules to avoid repeated RuleIndex
@@ -479,8 +515,9 @@ def test(self):
479
515
self .parse_perf_metrics ()
480
516
self .create_rules ()
481
517
for i in range (0 , len (self .workloads )):
518
+ self .wlidx = i
482
519
self ._init_data ()
483
- self .collect_perf (self .datafname , self . workloads [i ])
520
+ self .collect_perf (self .workloads [i ])
484
521
# Run positive value test
485
522
self .pos_val_test ()
486
523
for r in self .rules :
0 commit comments