1*4882a593Smuzhiyun#!/usr/bin/env python3 2*4882a593Smuzhiyun# -*- coding: utf-8 -*- 3*4882a593Smuzhiyun 4*4882a593Smuzhiyun# SPDX-License-Identifier: BSD-3-Clause 5*4882a593Smuzhiyun# Copyright (C) 2015 Enea Software AB 6*4882a593Smuzhiyun# Author: Thomas Lundström <thomas.lundstrom@enea.com> 7*4882a593Smuzhiyun 8*4882a593Smuzhiyun# The script measures interrupt latency together with different types of 9*4882a593Smuzhiyun# system load. This is done using the programs cyclictest and stress. 10*4882a593Smuzhiyun# 11*4882a593Smuzhiyun# The main output is: 12*4882a593Smuzhiyun# 13*4882a593Smuzhiyun# Best case (min) latency 14*4882a593Smuzhiyun# This has very limited value, but is presented since it can be done 15*4882a593Smuzhiyun# easily 16*4882a593Smuzhiyun# 17*4882a593Smuzhiyun# Average latency 18*4882a593Smuzhiyun# This value is of interrest for throughput oriented systems. Limited 19*4882a593Smuzhiyun# value for a real-time system. Also presented because it is easy to do. 20*4882a593Smuzhiyun# 21*4882a593Smuzhiyun# Worst case (max) latency 22*4882a593Smuzhiyun# This is the interesting number for a real-time system. The number 23*4882a593Smuzhiyun# presented is (of cource) the largest number observed. The challenge 24*4882a593Smuzhiyun# is to know how the observed worst case relates to the actual worst case. 25*4882a593Smuzhiyun# 26*4882a593Smuzhiyun# To get an indication of the confidence, the following method is used: 27*4882a593Smuzhiyun# 1) Instead of one long run, the measurement is made as a set of shorter 28*4882a593Smuzhiyun# runs. The number of runs must be a power of 2 for reasons that will 29*4882a593Smuzhiyun# shorlty be obvious 30*4882a593Smuzhiyun# 31*4882a593Smuzhiyun# 2) First, a list of the max values are created. 32*4882a593Smuzhiyun# 33*4882a593Smuzhiyun# 3) The smallest value in that list is recorded. 34*4882a593Smuzhiyun# 35*4882a593Smuzhiyun# 4) Then a new list is create by taking the max value of each pair of 36*4882a593Smuzhiyun# values in the original list. In this list the smallest value is 37*4882a593Smuzhiyun# recorded. 38*4882a593Smuzhiyun# 39*4882a593Smuzhiyun# 5) Step 3 is repeated until there is only one value in the list. See 40*4882a593Smuzhiyun# example below: 41*4882a593Smuzhiyun# 42*4882a593Smuzhiyun# Samples: 43*4882a593Smuzhiyun# | 44 | | | | | 44*4882a593Smuzhiyun# | 77 | 77 | | | | 45*4882a593Smuzhiyun# | 118 | | | | | 46*4882a593Smuzhiyun# | 119 | 119 | 119 | | | 47*4882a593Smuzhiyun# | 138 | | | | | 48*4882a593Smuzhiyun# | 57 | 138 | | | | 49*4882a593Smuzhiyun# | 175 | | | | | 50*4882a593Smuzhiyun# | 130 | 175 | 175 | 175 | | 51*4882a593Smuzhiyun# | 54 | | | | | 52*4882a593Smuzhiyun# | 150 | 150 | | | | 53*4882a593Smuzhiyun# | 47 | | | | | 54*4882a593Smuzhiyun# | 59 | 59 | 150 | | | 55*4882a593Smuzhiyun# | 199 | | | | | 56*4882a593Smuzhiyun# | 115 | 199 | | | | 57*4882a593Smuzhiyun# | 177 | | | | | 58*4882a593Smuzhiyun# | 129 | 177 | 199 | 199 | 199 | 59*4882a593Smuzhiyun# 60*4882a593Smuzhiyun# Smallest value: 61*4882a593Smuzhiyun# | 44 | 59 | 119 | 175 | 199 | 62*4882a593Smuzhiyun# 63*4882a593Smuzhiyun# 6) The generated list of smallest values is analyzed. In this case, it 64*4882a593Smuzhiyun# can be observed that the values are increasing significantly through 65*4882a593Smuzhiyun# the entire list, which leads to the conclusion that the number of 66*4882a593Smuzhiyun# samples is too small. 67*4882a593Smuzhiyun# If instead the list had been (167, 191, 196, 199, 199), there had 68*4882a593Smuzhiyun# been a very small, or no, increase at the end of the list. We might 69*4882a593Smuzhiyun# then suspect that the number of samples is probably large enough. 70*4882a593Smuzhiyun# There is however no guarantee for that. 71*4882a593Smuzhiyun# 72*4882a593Smuzhiyun# Steps 1-2 are done in run_cyclictest_suite 73*4882a593Smuzhiyun# Steps 3-5 are done in gen_minmax_list. 74*4882a593Smuzhiyun# Step 6 needs to be done manually since there is (yet) no well defined 75*4882a593Smuzhiyun# FAIL criterion and a theoretically solid PASS criterion may never be 76*4882a593Smuzhiyun# available. 77*4882a593Smuzhiyun 78*4882a593Smuzhiyunimport multiprocessing 79*4882a593Smuzhiyunimport os 80*4882a593Smuzhiyunimport re 81*4882a593Smuzhiyunimport signal 82*4882a593Smuzhiyunimport subprocess 83*4882a593Smuzhiyunimport time 84*4882a593Smuzhiyunimport traceback 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun# See comment on the function set_hung_tmo 87*4882a593Smuzhiyunhas_hung_task_detection = True 88*4882a593Smuzhiyun 89*4882a593Smuzhiyun#------------------------------------------------------------------------------- 90*4882a593Smuzhiyun 91*4882a593Smuzhiyunclass TestFail(Exception): 92*4882a593Smuzhiyun def __init__(self, msg): 93*4882a593Smuzhiyun self.msg = msg 94*4882a593Smuzhiyun 95*4882a593Smuzhiyun def __str__(self): 96*4882a593Smuzhiyun return "Test failure: (" + self.msg + ")" 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun#------------------------------------------------------------------------------- 99*4882a593Smuzhiyun 100*4882a593Smuzhiyundef tc_name(sub_name): 101*4882a593Smuzhiyun return "rt_bmark.intlat." + sub_name 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun#------------------------------------------------------------------------------- 104*4882a593Smuzhiyun# log() does the same job as print except that a '#' is added at the beginning 105*4882a593Smuzhiyun# of each line. This causes TEFEL to ignore it 106*4882a593Smuzhiyun 107*4882a593Smuzhiyundef log(*msg): 108*4882a593Smuzhiyun tmp = "".join(map(str, msg)) # 'map(str, ...' allows numbers 109*4882a593Smuzhiyun for line in tmp.splitlines(): 110*4882a593Smuzhiyun print("#", line) 111*4882a593Smuzhiyun 112*4882a593Smuzhiyun#------------------------------------------------------------------------------- 113*4882a593Smuzhiyun# Like log(), but with a timestamp added 114*4882a593Smuzhiyun 115*4882a593Smuzhiyundef log_ts(*msg): 116*4882a593Smuzhiyun ts = time.localtime() 117*4882a593Smuzhiyun stamp = "%2d:%02d:%02d: " % (ts.tm_hour, ts.tm_min, ts.tm_sec) 118*4882a593Smuzhiyun log(stamp, *msg) 119*4882a593Smuzhiyun 120*4882a593Smuzhiyun#------------------------------------------------------------------------------- 121*4882a593Smuzhiyun 122*4882a593Smuzhiyundef log_test_header(seq_no, nr_of_tests, name): 123*4882a593Smuzhiyun log("=" * 78) 124*4882a593Smuzhiyun log() 125*4882a593Smuzhiyun log(" Test case (%d/%d): %s" % (seq_no, nr_of_tests, tc_name(name))) 126*4882a593Smuzhiyun log() 127*4882a593Smuzhiyun log("." * 78) 128*4882a593Smuzhiyun log() 129*4882a593Smuzhiyun 130*4882a593Smuzhiyun#------------------------------------------------------------------------------- 131*4882a593Smuzhiyun 132*4882a593Smuzhiyundef start_stress(*args): 133*4882a593Smuzhiyun stress_cmd = [ "stress-ng" ] 134*4882a593Smuzhiyun added_stress_types = [] 135*4882a593Smuzhiyun req_stress_types = set(args) 136*4882a593Smuzhiyun cpu_cnt = str(multiprocessing.cpu_count()) 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun # The function cond_add_stress appends the options to the stress 139*4882a593Smuzhiyun # command if the stress type is in the set of requested stress types 140*4882a593Smuzhiyun 141*4882a593Smuzhiyun def cond_add_stress(stress_type, options): 142*4882a593Smuzhiyun if stress_type in req_stress_types: 143*4882a593Smuzhiyun req_stress_types.remove(stress_type) 144*4882a593Smuzhiyun added_stress_types.append(stress_type) 145*4882a593Smuzhiyun stress_cmd.extend(options) 146*4882a593Smuzhiyun 147*4882a593Smuzhiyun #---------- 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun cond_add_stress("io", ["-i", cpu_cnt]) 150*4882a593Smuzhiyun cond_add_stress("cpu", ["-c", cpu_cnt]) 151*4882a593Smuzhiyun cond_add_stress("hdd", ["-d", cpu_cnt, "--hdd-bytes", "20M"]) 152*4882a593Smuzhiyun cond_add_stress("vm", ["-m", cpu_cnt, "--vm-bytes", "10M"]) 153*4882a593Smuzhiyun 154*4882a593Smuzhiyun unknown = ", ".join(req_stress_types) 155*4882a593Smuzhiyun if unknown != "": 156*4882a593Smuzhiyun raise TestFail("Unknown stress type(s): %s" % unknown) 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun if not added_stress_types: 159*4882a593Smuzhiyun log("No stress requested") 160*4882a593Smuzhiyun return None 161*4882a593Smuzhiyun 162*4882a593Smuzhiyun added = "+".join(added_stress_types) 163*4882a593Smuzhiyun stress_cmd_str = " ".join(stress_cmd) 164*4882a593Smuzhiyun 165*4882a593Smuzhiyun log("Starting stress(", added, ")") 166*4882a593Smuzhiyun log(" Command: '", stress_cmd_str, "'") 167*4882a593Smuzhiyun log() 168*4882a593Smuzhiyun 169*4882a593Smuzhiyun # start_new_session causes stress to be executed in a separate 170*4882a593Smuzhiyun # session, => it gets a new process group (incl. children). It 171*4882a593Smuzhiyun # can then be terminated using os.killpg in end_stress without 172*4882a593Smuzhiyun # terminating this script. 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun p = subprocess.Popen(stress_cmd, start_new_session=True) 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun return p 177*4882a593Smuzhiyun 178*4882a593Smuzhiyun#------------------------------------------------------------------------------- 179*4882a593Smuzhiyun 180*4882a593Smuzhiyundef end_stress(p): 181*4882a593Smuzhiyun if p is None: 182*4882a593Smuzhiyun # The value None indicates that no stress scenario was started 183*4882a593Smuzhiyun return 184*4882a593Smuzhiyun 185*4882a593Smuzhiyun if p.poll() is not None: 186*4882a593Smuzhiyun raise TestFail("stress prematurely terminated.") 187*4882a593Smuzhiyun 188*4882a593Smuzhiyun os.killpg(os.getpgid(p.pid), signal.SIGTERM) 189*4882a593Smuzhiyun log("Terminated stress") 190*4882a593Smuzhiyun 191*4882a593Smuzhiyun#------------------------------------------------------------------------------- 192*4882a593Smuzhiyun 193*4882a593Smuzhiyundef us2hms_str(us): 194*4882a593Smuzhiyun s = (us+500000) // 1000000 # Round microseconds to s 195*4882a593Smuzhiyun m = s//60 196*4882a593Smuzhiyun s -= 60*m; 197*4882a593Smuzhiyun h = m//60 198*4882a593Smuzhiyun m -= 60*h 199*4882a593Smuzhiyun 200*4882a593Smuzhiyun return "%d:%02d:%02d" % (h, m, s) 201*4882a593Smuzhiyun 202*4882a593Smuzhiyun#------------------------------------------------------------------------------- 203*4882a593Smuzhiyun# Sometime the hung task supervision is triggered during execution of 204*4882a593Smuzhiyun# cyclictest (cyclictest starves stress). To avoid that, the supervision 205*4882a593Smuzhiyun# is temporarily disabled 206*4882a593Smuzhiyun 207*4882a593Smuzhiyundef set_hung_tmo(new_tmo): 208*4882a593Smuzhiyun global has_hung_task_detection 209*4882a593Smuzhiyun 210*4882a593Smuzhiyun tmo_file = "/proc/sys/kernel/hung_task_timeout_secs" 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun if not has_hung_task_detection: 213*4882a593Smuzhiyun return 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun if not os.access(tmo_file, os.W_OK): 216*4882a593Smuzhiyun log("Hung task detection not supported") 217*4882a593Smuzhiyun log(" (File ", tmo_file, " not found)") 218*4882a593Smuzhiyun has_hung_task_detection = False 219*4882a593Smuzhiyun return 220*4882a593Smuzhiyun 221*4882a593Smuzhiyun orig_tmo = int(subprocess.check_output(["cat", tmo_file]).strip()) 222*4882a593Smuzhiyun if new_tmo != orig_tmo: 223*4882a593Smuzhiyun cmd = ( "echo " + str(new_tmo) + " > " + tmo_file ) 224*4882a593Smuzhiyun subprocess.check_output(cmd, shell=True) 225*4882a593Smuzhiyun log("Changed timeout for detection of hung tasks: ", 226*4882a593Smuzhiyun orig_tmo, " -> ", new_tmo) 227*4882a593Smuzhiyun 228*4882a593Smuzhiyun return orig_tmo 229*4882a593Smuzhiyun 230*4882a593Smuzhiyun#------------------------------------------------------------------------------- 231*4882a593Smuzhiyun 232*4882a593Smuzhiyundef gen_minmax_list(max_list): 233*4882a593Smuzhiyun res = [min(max_list)] 234*4882a593Smuzhiyun 235*4882a593Smuzhiyun while True: 236*4882a593Smuzhiyun tmp = max_list 237*4882a593Smuzhiyun max_list = [] 238*4882a593Smuzhiyun while tmp: 239*4882a593Smuzhiyun max_list.append(max(tmp.pop(0), tmp.pop(0))) 240*4882a593Smuzhiyun 241*4882a593Smuzhiyun res.append(min(max_list)) 242*4882a593Smuzhiyun 243*4882a593Smuzhiyun if len(max_list) < 2: 244*4882a593Smuzhiyun return res 245*4882a593Smuzhiyun 246*4882a593Smuzhiyun#------------------------------------------------------------------------------- 247*4882a593Smuzhiyun# Parameters for cyclictest: 248*4882a593Smuzhiyun# 249*4882a593Smuzhiyun# On the -S option (from cyclictest.c): 250*4882a593Smuzhiyun# -S implies options -a -t -n and same priority of all threads 251*4882a593Smuzhiyun# -a: One thread per core 252*4882a593Smuzhiyun# -n: use clock_nanosleep instead of posix interval timers 253*4882a593Smuzhiyun# -t: (without argument) Set number of threads to the number 254*4882a593Smuzhiyun# of cpus 255*4882a593Smuzhiyun 256*4882a593Smuzhiyuninterval_core_0 = 100 # Timer interval on core 0 [us] 257*4882a593Smuzhiyuninterval_delta = 20 # Interval increment for each core [us] 258*4882a593Smuzhiyunloop_count = 30000 # Number of loops (on core 0). 259*4882a593Smuzhiyun 260*4882a593Smuzhiyuncmd = ("cyclictest", 261*4882a593Smuzhiyun "-S", # Standard SMP testing. See below 262*4882a593Smuzhiyun "-p", "99", # RT priority 99 263*4882a593Smuzhiyun "-q", # Quiet mode, i.e. print only a summary 264*4882a593Smuzhiyun "-i", str(interval_core_0), 265*4882a593Smuzhiyun "-d", str(interval_delta), 266*4882a593Smuzhiyun "-l", str(loop_count) 267*4882a593Smuzhiyun ) 268*4882a593Smuzhiyunrex = re.compile(b"C:\s*(\d+).*Min:\s*(\d+).*Avg:\s*(\d+).*Max:\s*(\d+)") 269*4882a593Smuzhiyun 270*4882a593Smuzhiyundef run_cyclictest_once(): 271*4882a593Smuzhiyun res = subprocess.check_output(cmd) 272*4882a593Smuzhiyun 273*4882a593Smuzhiyun # minlist and maxlist are lists with the extremes for each core 274*4882a593Smuzhiyun # avg_cnt is the sum of cycles for all cores 275*4882a593Smuzhiyun # avg_sum is the sum of (cycle count*average) for each core 276*4882a593Smuzhiyun # Since cyclictest runs different number of cycles on 277*4882a593Smuzhiyun # different cores, avg_sum/avg_cnt gives a more accurate 278*4882a593Smuzhiyun # value of the overall average than just taking the average 279*4882a593Smuzhiyun # of each core's averages 280*4882a593Smuzhiyun 281*4882a593Smuzhiyun minlist = [] 282*4882a593Smuzhiyun maxlist = [] 283*4882a593Smuzhiyun avg_sum = 0 284*4882a593Smuzhiyun avg_cnt = 0 285*4882a593Smuzhiyun 286*4882a593Smuzhiyun for line in res.splitlines(): 287*4882a593Smuzhiyun m = rex.search(line) 288*4882a593Smuzhiyun if m is not None: 289*4882a593Smuzhiyun minlist.append(int(m.group(2))) 290*4882a593Smuzhiyun maxlist.append(int(m.group(4))) 291*4882a593Smuzhiyun n = int(m.group(1)) 292*4882a593Smuzhiyun avg_sum += n * int(m.group(3)) 293*4882a593Smuzhiyun avg_cnt += n 294*4882a593Smuzhiyun 295*4882a593Smuzhiyun return min(minlist), [avg_sum, avg_cnt], max(maxlist) 296*4882a593Smuzhiyun 297*4882a593Smuzhiyun#------------------------------------------------------------------------------- 298*4882a593Smuzhiyun# A precondition for the tracking of min-max values is that 299*4882a593Smuzhiyun# the suite size os a power of 2. 300*4882a593Smuzhiyun 301*4882a593SmuzhiyunN = 5 302*4882a593Smuzhiyunsuite_size = 2**N 303*4882a593Smuzhiyun 304*4882a593Smuzhiyunest_exec_time_once = interval_core_0 * loop_count 305*4882a593Smuzhiyunest_exec_time_suite = suite_size * est_exec_time_once 306*4882a593Smuzhiyun 307*4882a593Smuzhiyundef run_cyclictest_suite(): 308*4882a593Smuzhiyun log("Starting cyclictest") 309*4882a593Smuzhiyun log(" Command : ", " ".join(cmd)) 310*4882a593Smuzhiyun log(" Number of cycles : ", loop_count*suite_size, 311*4882a593Smuzhiyun " (", suite_size, " sets of ", loop_count, " cycles)") 312*4882a593Smuzhiyun log(" Exec. time (est) : ", us2hms_str(est_exec_time_suite)) 313*4882a593Smuzhiyun log() 314*4882a593Smuzhiyun 315*4882a593Smuzhiyun orig_tmo = set_hung_tmo(0) # 0 => Disable 316*4882a593Smuzhiyun 317*4882a593Smuzhiyun # float('inf') emulates infinity. At least in the sense that it is 318*4882a593Smuzhiyun # guaranteed to be larger than any actual number. 319*4882a593Smuzhiyun ack_min = float('inf') 320*4882a593Smuzhiyun ack_avg = [0, 0] 321*4882a593Smuzhiyun 322*4882a593Smuzhiyun log() 323*4882a593Smuzhiyun log_ts("Start of execution") 324*4882a593Smuzhiyun t = time.time() 325*4882a593Smuzhiyun max_list = [] 326*4882a593Smuzhiyun 327*4882a593Smuzhiyun for i in range(0, suite_size): 328*4882a593Smuzhiyun tmp_min, tmp_avg, tmp_max = run_cyclictest_once() 329*4882a593Smuzhiyun 330*4882a593Smuzhiyun msg = "%2d/%2d:" % (i+1, suite_size) 331*4882a593Smuzhiyun msg += " min: %4d" % tmp_min 332*4882a593Smuzhiyun msg += " avg: %5.1f" % (float(tmp_avg[0])/tmp_avg[1]) 333*4882a593Smuzhiyun msg += " max: %4d" % tmp_max 334*4882a593Smuzhiyun log_ts(msg) 335*4882a593Smuzhiyun 336*4882a593Smuzhiyun # Track minimum value 337*4882a593Smuzhiyun if tmp_min < ack_min: 338*4882a593Smuzhiyun ack_min = tmp_min 339*4882a593Smuzhiyun 340*4882a593Smuzhiyun # Track smallest max value 341*4882a593Smuzhiyun max_list.append(tmp_max) 342*4882a593Smuzhiyun 343*4882a593Smuzhiyun ack_avg[0] += tmp_avg[0] 344*4882a593Smuzhiyun ack_avg[1] += tmp_avg[1] 345*4882a593Smuzhiyun 346*4882a593Smuzhiyun t = time.time()-t 347*4882a593Smuzhiyun log_ts("Cyclictest completed. Actual execution time:", 348*4882a593Smuzhiyun us2hms_str(t*1000000)) 349*4882a593Smuzhiyun log() 350*4882a593Smuzhiyun set_hung_tmo(orig_tmo) 351*4882a593Smuzhiyun 352*4882a593Smuzhiyun return ack_min, float(ack_avg[0])/ack_avg[1], gen_minmax_list(max_list) 353*4882a593Smuzhiyun 354*4882a593Smuzhiyun#------------------------------------------------------------------------------- 355*4882a593Smuzhiyun 356*4882a593Smuzhiyunclass cyclictest_runner: 357*4882a593Smuzhiyun def run_test(self, seq_no, nr_of_tests, name, stressparams): 358*4882a593Smuzhiyun 359*4882a593Smuzhiyun try: 360*4882a593Smuzhiyun log_test_header(seq_no, nr_of_tests, name) 361*4882a593Smuzhiyun 362*4882a593Smuzhiyun p = start_stress(*stressparams) 363*4882a593Smuzhiyun 364*4882a593Smuzhiyun bm_min, bm_avg, bm_max_list = run_cyclictest_suite() 365*4882a593Smuzhiyun 366*4882a593Smuzhiyun end_stress(p) 367*4882a593Smuzhiyun 368*4882a593Smuzhiyun bm_max = bm_max_list[-1] 369*4882a593Smuzhiyun 370*4882a593Smuzhiyun log() 371*4882a593Smuzhiyun log("Min: %d us" % bm_min) 372*4882a593Smuzhiyun log("Avg: %.1f us" % bm_avg) 373*4882a593Smuzhiyun log("Max: %d us" % bm_max) 374*4882a593Smuzhiyun log() 375*4882a593Smuzhiyun log("Max list: ", bm_max_list) 376*4882a593Smuzhiyun log() 377*4882a593Smuzhiyun log("PASS") 378*4882a593Smuzhiyun 379*4882a593Smuzhiyun print() 380*4882a593Smuzhiyun print(tc_name(name), "[Min/us,Avg/us,Max/us]:",) 381*4882a593Smuzhiyun print("%d,%.1f,%d" % (bm_min,bm_avg, bm_max)) 382*4882a593Smuzhiyun print("PASS:", tc_name(name)) 383*4882a593Smuzhiyun print() 384*4882a593Smuzhiyun 385*4882a593Smuzhiyun except Exception: 386*4882a593Smuzhiyun log() 387*4882a593Smuzhiyun log("Exception!") 388*4882a593Smuzhiyun log() 389*4882a593Smuzhiyun log("Traceback:", traceback.format_exc()) 390*4882a593Smuzhiyun log() 391*4882a593Smuzhiyun log("WD: ", os.getcwd()) 392*4882a593Smuzhiyun log() 393*4882a593Smuzhiyun log("FAIL") 394*4882a593Smuzhiyun print() 395*4882a593Smuzhiyun print("FAIL:", tc_name(name)) 396*4882a593Smuzhiyun print() 397*4882a593Smuzhiyun 398*4882a593Smuzhiyun#------------------------------------------------------------------------------- 399*4882a593Smuzhiyun 400*4882a593Smuzhiyunrunner = cyclictest_runner() 401*4882a593Smuzhiyun 402*4882a593Smuzhiyuntests = (("no_stress", []), 403*4882a593Smuzhiyun ("cpu", ["cpu"]), 404*4882a593Smuzhiyun ("hdd", ["hdd"]), 405*4882a593Smuzhiyun ("io", ["io"]), 406*4882a593Smuzhiyun ("vm", ["vm"]), 407*4882a593Smuzhiyun ("full", ["io", "cpu", "hdd", "vm"]), 408*4882a593Smuzhiyun ) 409*4882a593Smuzhiyun 410*4882a593Smuzhiyunnr_of_tests = len(tests) 411*4882a593Smuzhiyunfor seq_no, params in enumerate(tests, start=1): 412*4882a593Smuzhiyun runner.run_test(seq_no, nr_of_tests, *params) 413