#!/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()
|