#!/usr/bin/env python # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2015 Google, Inc. # Copyright (c) 2015 Linaro, Ltd. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # 3. Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from __future__ import print_function import csv import datetime import sys import time dict = {'ping': '2', 'transfer': '3', 'sink': '4'} verbose = 1 def abort(): sys.exit(1) def usage(): print('Usage: looptest TEST SIZE ITERATIONS PATH\n\n' ' Run TEST for a number of ITERATIONS with operation data SIZE bytes\n' ' TEST may be \'ping\' \'transfer\' or \'sink\'\n' ' SIZE indicates the size of transfer <= greybus max payload bytes\n' ' ITERATIONS indicates the number of times to execute TEST at SIZE bytes\n' ' Note if ITERATIONS is set to zero then this utility will\n' ' initiate an infinite (non terminating) test and exit\n' ' without logging any metrics data\n' ' PATH indicates the sysfs path for the loopback greybus entries e.g.\n' ' /sys/bus/greybus/devices/endo0:1:1:1:1/\n' 'Examples:\n' ' looptest transfer 128 10000\n' ' looptest ping 0 128\n' ' looptest sink 2030 32768\n' .format(sys.argv[0]), file=sys.stderr) abort() def read_sysfs_int(path): try: f = open(path, "r"); val = f.read(); f.close() return int(val) except IOError as e: print("I/O error({0}): {1}".format(e.errno, e.strerror)) print("Invalid path %s" % path) def write_sysfs_val(path, val): try: f = open(path, "r+") f.write(val) f.close() except IOError as e: print("I/O error({0}): {1}".format(e.errno, e.strerror)) print("Invalid path %s" % path) def log_csv(test_name, size, iteration_max, sys_pfx): # file name will test_name_size_iteration_max.csv # every time the same test with the same parameters is run we will then # append to the same CSV with datestamp - representing each test dataset fname = test_name + '_' + size + '_' + str(iteration_max) + '.csv' try: # gather data set date = str(datetime.datetime.now()) error = read_sysfs_int(sys_pfx + 'error') request_min = read_sysfs_int(sys_pfx + 'requests_per_second_min') request_max = read_sysfs_int(sys_pfx + 'requests_per_second_max') request_avg = read_sysfs_int(sys_pfx + 'requests_per_second_avg') latency_min = read_sysfs_int(sys_pfx + 'latency_min') latency_max = read_sysfs_int(sys_pfx + 'latency_max') latency_avg = read_sysfs_int(sys_pfx + 'latency_avg') throughput_min = read_sysfs_int(sys_pfx + 'throughput_min') throughput_max = read_sysfs_int(sys_pfx + 'throughput_max') throughput_avg = read_sysfs_int(sys_pfx + 'throughput_avg') # derive jitter request_jitter = request_max - request_min latency_jitter = latency_max - latency_min throughput_jitter = throughput_max - throughput_min # append data set to file with open(fname, 'a') as csvf: row = csv.writer(csvf, delimiter=",", quotechar="'", quoting=csv.QUOTE_MINIMAL) row.writerow([date, test_name, size, iteration_max, error, request_min, request_max, request_avg, request_jitter, latency_min, latency_max, latency_avg, latency_jitter, throughput_min, throughput_max, throughput_avg, throughput_jitter]) except IOError as e: print("I/O error({0}): {1}".format(e.errno, e.strerror)) def loopback_run(test_name, size, iteration_max, sys_pfx): test_id = dict[test_name] try: # Terminate any currently running test write_sysfs_val(sys_pfx + 'type', '0') # Set parameter for no wait between messages write_sysfs_val(sys_pfx + 'ms_wait', '0') # Set operation size write_sysfs_val(sys_pfx + 'size', size) # Set iterations write_sysfs_val(sys_pfx + 'iteration_max', str(iteration_max)) # Initiate by setting loopback operation type write_sysfs_val(sys_pfx + 'type', test_id) time.sleep(1) if iteration_max == 0: print ("Infinite test initiated CSV won't be logged\n") return previous = 0 err = 0 while True: # get current count bail out if it hasn't changed iteration_count = read_sysfs_int(sys_pfx + 'iteration_count') if previous == iteration_count: err = 1 break elif iteration_count == iteration_max: break previous = iteration_count if verbose: print('%02d%% complete %d of %d ' % (100 * iteration_count / iteration_max, iteration_count, iteration_max)) time.sleep(1) if err: print ('\nError executing test\n') else: log_csv(test_name, size, iteration_max, sys_pfx) except ValueError as ve: print("Error: %s " % format(e.strerror), file=sys.stderr) abort() def main(): if len(sys.argv) < 5: usage() if sys.argv[1] in dict.keys(): loopback_run(sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4]) else: usage() if __name__ == '__main__': main()