1#!/usr/bin/env python 2# SPDX-License-Identifier: BSD-3-Clause 3 4# Copyright (c) 2015 Google, Inc. 5# Copyright (c) 2015 Linaro, Ltd. 6# All rights reserved. 7# 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions are met: 10# 1. Redistributions of source code must retain the above copyright notice, 11# this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright notice, 13# this list of conditions and the following disclaimer in the documentation 14# and/or other materials provided with the distribution. 15# 3. Neither the name of the copyright holder nor the names of its 16# contributors may be used to endorse or promote products derived from this 17# software without specific prior written permission. 18# 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 23# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31from __future__ import print_function 32import csv 33import datetime 34import sys 35import time 36 37dict = {'ping': '2', 'transfer': '3', 'sink': '4'} 38verbose = 1 39 40def abort(): 41 sys.exit(1) 42 43def usage(): 44 print('Usage: looptest TEST SIZE ITERATIONS PATH\n\n' 45 ' Run TEST for a number of ITERATIONS with operation data SIZE bytes\n' 46 ' TEST may be \'ping\' \'transfer\' or \'sink\'\n' 47 ' SIZE indicates the size of transfer <= greybus max payload bytes\n' 48 ' ITERATIONS indicates the number of times to execute TEST at SIZE bytes\n' 49 ' Note if ITERATIONS is set to zero then this utility will\n' 50 ' initiate an infinite (non terminating) test and exit\n' 51 ' without logging any metrics data\n' 52 ' PATH indicates the sysfs path for the loopback greybus entries e.g.\n' 53 ' /sys/bus/greybus/devices/endo0:1:1:1:1/\n' 54 'Examples:\n' 55 ' looptest transfer 128 10000\n' 56 ' looptest ping 0 128\n' 57 ' looptest sink 2030 32768\n' 58 .format(sys.argv[0]), file=sys.stderr) 59 60 abort() 61 62def read_sysfs_int(path): 63 try: 64 f = open(path, "r"); 65 val = f.read(); 66 f.close() 67 return int(val) 68 except IOError as e: 69 print("I/O error({0}): {1}".format(e.errno, e.strerror)) 70 print("Invalid path %s" % path) 71 72def write_sysfs_val(path, val): 73 try: 74 f = open(path, "r+") 75 f.write(val) 76 f.close() 77 except IOError as e: 78 print("I/O error({0}): {1}".format(e.errno, e.strerror)) 79 print("Invalid path %s" % path) 80 81def log_csv(test_name, size, iteration_max, sys_pfx): 82 # file name will test_name_size_iteration_max.csv 83 # every time the same test with the same parameters is run we will then 84 # append to the same CSV with datestamp - representing each test dataset 85 fname = test_name + '_' + size + '_' + str(iteration_max) + '.csv' 86 87 try: 88 # gather data set 89 date = str(datetime.datetime.now()) 90 error = read_sysfs_int(sys_pfx + 'error') 91 request_min = read_sysfs_int(sys_pfx + 'requests_per_second_min') 92 request_max = read_sysfs_int(sys_pfx + 'requests_per_second_max') 93 request_avg = read_sysfs_int(sys_pfx + 'requests_per_second_avg') 94 latency_min = read_sysfs_int(sys_pfx + 'latency_min') 95 latency_max = read_sysfs_int(sys_pfx + 'latency_max') 96 latency_avg = read_sysfs_int(sys_pfx + 'latency_avg') 97 throughput_min = read_sysfs_int(sys_pfx + 'throughput_min') 98 throughput_max = read_sysfs_int(sys_pfx + 'throughput_max') 99 throughput_avg = read_sysfs_int(sys_pfx + 'throughput_avg') 100 101 # derive jitter 102 request_jitter = request_max - request_min 103 latency_jitter = latency_max - latency_min 104 throughput_jitter = throughput_max - throughput_min 105 106 # append data set to file 107 with open(fname, 'a') as csvf: 108 row = csv.writer(csvf, delimiter=",", quotechar="'", 109 quoting=csv.QUOTE_MINIMAL) 110 row.writerow([date, test_name, size, iteration_max, error, 111 request_min, request_max, request_avg, request_jitter, 112 latency_min, latency_max, latency_avg, latency_jitter, 113 throughput_min, throughput_max, throughput_avg, throughput_jitter]) 114 except IOError as e: 115 print("I/O error({0}): {1}".format(e.errno, e.strerror)) 116 117def loopback_run(test_name, size, iteration_max, sys_pfx): 118 test_id = dict[test_name] 119 try: 120 # Terminate any currently running test 121 write_sysfs_val(sys_pfx + 'type', '0') 122 # Set parameter for no wait between messages 123 write_sysfs_val(sys_pfx + 'ms_wait', '0') 124 # Set operation size 125 write_sysfs_val(sys_pfx + 'size', size) 126 # Set iterations 127 write_sysfs_val(sys_pfx + 'iteration_max', str(iteration_max)) 128 # Initiate by setting loopback operation type 129 write_sysfs_val(sys_pfx + 'type', test_id) 130 time.sleep(1) 131 132 if iteration_max == 0: 133 print ("Infinite test initiated CSV won't be logged\n") 134 return 135 136 previous = 0 137 err = 0 138 while True: 139 # get current count bail out if it hasn't changed 140 iteration_count = read_sysfs_int(sys_pfx + 'iteration_count') 141 if previous == iteration_count: 142 err = 1 143 break 144 elif iteration_count == iteration_max: 145 break 146 previous = iteration_count 147 if verbose: 148 print('%02d%% complete %d of %d ' % 149 (100 * iteration_count / iteration_max, 150 iteration_count, iteration_max)) 151 time.sleep(1) 152 if err: 153 print ('\nError executing test\n') 154 else: 155 log_csv(test_name, size, iteration_max, sys_pfx) 156 except ValueError as ve: 157 print("Error: %s " % format(e.strerror), file=sys.stderr) 158 abort() 159 160def main(): 161 if len(sys.argv) < 5: 162 usage() 163 164 if sys.argv[1] in dict.keys(): 165 loopback_run(sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4]) 166 else: 167 usage() 168if __name__ == '__main__': 169 main() 170