From 4b9389362dbefcb559e4167296484f2aca475b5e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Mon, 16 Sep 2013 15:07:09 +0200 Subject: [PATCH] Adds RTEMS target to makefile and associated Python scripts. The bash scripts made too many assumptions about target (namely, that it is linux) so it was easier to write a replacement to support RTEMS. Currently targets Python 3+, but with minimal changes (print and shebang) can run on Python 2.6+. --- gw-tests/Makefile | 4 ++ gw-tests/lib.py | 155 ++++++++++++++++++++++++++++++++++++++++ gw-tests/rtems_bench.py | 93 ++++++++++++++++++++++++ gw-tests/tests.py | 34 +++++++++ 4 files changed, 286 insertions(+) create mode 100644 gw-tests/lib.py create mode 100755 gw-tests/rtems_bench.py create mode 100644 gw-tests/tests.py diff --git a/gw-tests/Makefile b/gw-tests/Makefile index 5cb074c..ddfc9a4 100644 --- a/gw-tests/Makefile +++ b/gw-tests/Makefile @@ -33,6 +33,10 @@ endef $(foreach result_dir,$(PLOT_SCRIPTS:%/plot.sh=%),$(eval $(call plot_template,$(result_dir)))) $(foreach result_dir,$(PLOT_SCRIPTS:%/plot.sh=%),$(eval $(call plotall_template,$(result_dir)))) +.PHONY: RTEMS +RTEMS: + ./rtems_bench.py + .PHONY: html html: ./genhtml/genhtml.py results diff --git a/gw-tests/lib.py b/gw-tests/lib.py new file mode 100644 index 0000000..700eacb --- /dev/null +++ b/gw-tests/lib.py @@ -0,0 +1,155 @@ +import subprocess +import sys +import os +import shutil +import stat +import time +import glob + +load_types = ("cpu", "eth", "none") +traffic_modes = ("oneatatime", "50", "flood") + +class settings: + """Wrapper class for "globals" + Used to keep track of current test settings for other functions. + """ + hostname = None + targetname = "rtems4.10-midam" + traffic = None + load = None + testname = None + +def set_hostname(name): + settings.hostname = name + +def set_targetname(name): + settings.targetname = name + +def set_traffic(traffic): + settings.traffic = traffic + +def set_load(load): + settings.load = load + +def set_testname(name): + settings.testname = name + +def set_testenv(hostname = None, targetname = None, traffic = None, load = None, testname = None): + set_hostname(hostname) + set_targetname(targetname) + set_traffic(traffic) + set_load(load) + set_testname(testname) + +gateway_IP_addr = "192.168.2.3" +ping_flood_cmd = "ping -f -s 60000 -q {}".format(gateway_IP_addr) + +#Prepares 755 flag for setting privileges +file_flag = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH + +base_plot_string = ('#!/bin/bash\n' + 'export kvers={0}\n' + 'export hostkvers={1}\n' + 'export traffic={2}\n' + 'export load={3}\n' + 'cd $(dirname $0)/../../../../../..\n' + 'exec ./{4}.sh --plot "$@"\n') + +tftpboot_path = "/var/lib/tftpboot/ryu/" +load_image_path = tftpboot_path + "rtems4.10_{}_FULL_LOAD" +no_load_image_path = tftpboot_path + "rtems4.10_{}_NO_LOAD" +boot_image_path = tftpboot_path + "uImage" + +eth_process = None +def start_eth_load(): + """Starts eth load by ping_flood_cmd""" + global eth_process + eth_process = subprocess.Popen(ping_flood_cmd.split()) + +def stop_eth_load(): + """Stops eth load if active""" + global eth_process + if eth_process is not None: + eth_process.terminate() + eth_process = None + print("Eth load stopped") + else: + print("Couldn't stop eth load -- was not active") + +def restart_board(): + """Physically restarts the board""" + result = subprocess.call("../scripts/restart") + print("Waiting on restart for 15s.") + time.sleep(15) # 15 seconds should be enough to boot up fully even when it runs slowly + +def start_cpu_load(test_name): + """Actually flashes different image, because I don't have working way of communicating commands to the board.""" + shutil.copy(load_image_path.format(settings.testname), boot_image_path) + restart_board() + +def stop_cpu_load(test_name): + """Actually flashes different image, because I don't have working way of communicating commands to the board.""" + shutil.copy(no_load_image_path.format(settings.testname), boot_image_path) + restart_board() + +def run_in_dir(directory, test): + """Runs given test in given directory and after it is finished, changes it back.""" + old = os.getcwd() + os.chdir(directory) + test.run() + os.chdir(old) + +def run_test(test): + """Prepares proper directory for test and then runs it in the directory.""" + target_dir = os.path.join("results", settings.hostname, settings.targetname, settings.traffic, settings.load, test.name) + prepare_directory(target_dir) + run_in_dir(target_dir, test) + +def prepare_directory(path): + """Creates given path if it does not already exist.""" + if not os.path.exists(path): + os.makedirs(path) + + +def create_plot_script(): + """Creates plot.sh with correct format to be graphed.""" + with open("plot.sh", "w+") as file: + file.write(base_plot_string.format(settings.targetname, settings.hostname, settings.traffic, settings.load, settings.testname)) + os.chmod("plot.sh", file_flag) + +def get_latester_arg(msg_len): + """Returns first part of the latester arguments.""" + if settings.traffic == "oneatatime": + return "-o" + elif settings.traffic == "flood": + return "" + elif settings.traffic == "50": + return "-p " + str(2 * (44 + 8 * msg_len)) + else: + raise InvalidModeException("invalid argument for get_latester_arg") + +def call_latester(args): + """Calls latester with the supplied arguments""" + cmd = "latester " + args + subprocess.call(cmd.split()) + +def symlink_results(orig_name): + """Symlinks all .txt files in path given by ../{orig_name}/ into current directory under their original names.""" + files = glob.iglob("../{}/*.txt".format(orig_name)) + for txt_file in files: + link_file = txt_file.split(os.path.sep)[-1] + if os.path.islink(link_file) or os.path.exists(link_file): + os.remove(link_file) + os.symlink(txt_file, link_file) + +def read_hostname(): + """Reads uname from host kernel.""" + k_call = subprocess.Popen("uname -r".split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, error = k_call.communicate() + #we have to convert output from bytearray to string and strip trailing newline from output + return "host-" + output.decode(sys.stdout.encoding).rstrip() + + +def original_name_from_histogram_name(hist_name): + """Retuns name of the original test from the name of its histogram.""" + return "-".join(hist_name.split("-")[:-1]) \ No newline at end of file diff --git a/gw-tests/rtems_bench.py b/gw-tests/rtems_bench.py new file mode 100755 index 0000000..51539b8 --- /dev/null +++ b/gw-tests/rtems_bench.py @@ -0,0 +1,93 @@ +#!/usr/local/bin/python3.3 + +#note, this script should generally work with any version of Python above 2.5 (lot of used functions of the subprocess module are there since 2.6) +""" +Quick and dirty script to automate benchmarking ryu board with RTEMS on board. + +Should create the same directory structure as the shell scripts used when targeting linux, +but only uses two tests (one actually, the second one is reinterpretation of the data of the first one.) + +Expects to be run from within the gw-tests directory and that the layout of boot files remains constant. +""" + +import subprocess +import sys, os, shutil, stat +from time import sleep + +import tests +import lib + +if os.geteuid() != 0: + print("This script needs to be run as root.") + exit(1) + + + +def run(): + print("Starting the benchmark.") + hostname = lib.read_hostname() + print("Hostname is: ", hostname) + lib.set_hostname(hostname) + for test in tests.tests: + testname = test.name + print("Current test is: ", testname) + lib.set_testname(testname) + for load in lib.load_types: + lib.set_load(load) + print("Current load is: ", load) + + #Currently, because of problems with serial port, tests have associated images for behaviour + #otherwise, they are just histogram generators + if test.has_image: + #First part of load "handlers" -> start the proper load. + if load == "cpu": + #we have to reflash cpu load image + #thus, this function actually waits for ~15s + print("Starting CPU load.") + lib.start_cpu_load(testname) + + if load == "eth": + print("Starting eth load") + lib.start_eth_load() + + for traffic in lib.traffic_modes: + if traffic == "flood": + subprocess.call("ifconfig can0 txqueuelen 200".split()) #this stops latester from ending due to ENOBUFS errors + print("Can mode: ", traffic) + + lib.set_traffic(traffic) + print("Testenv prepared") + #Tests are run here. + lib.run_test(test) + + + #restart every can_mode change, just to be sure? + #restart_board() + #give time to recover from load? + sleep(5) + + #Currently, because of problems with serial port, tests have associated images for behaviour + #otherwise, they are just histogram generators + if test.has_image: + #second part of the load "handlers" -> stopping the load + if load == "cpu": + #we have to reflash "no cpu load" image + #thus, this function actually waits for ~15s + print("Stopping cpu load") + lib.stop_cpu_load(testname) + + if load == "eth": + print("Stopping eth load") + lib.stop_eth_load() + + + + + print("Run should be now finished.") + + + + + +if __name__ == "__main__": + run() diff --git a/gw-tests/tests.py b/gw-tests/tests.py new file mode 100644 index 0000000..a06a1ae --- /dev/null +++ b/gw-tests/tests.py @@ -0,0 +1,34 @@ +import lib + + +class Test: + def __init__(self, name, func, has_image): + self.name = name + self.func = func + self.has_image = has_image + + def run(self): + self.func(self) + +def nop(self): + print(self.name) + for l in (2, 4, 6, 8): + lib.call_latester(" -d can0 -d can1 -d can2 -c 10000 {0} -l {1} -n len{1}".format(lib.get_latester_arg(l), l)) + lib.create_plot_script() + + +def nop_time(self): + lib.symlink_results(lib.original_name_from_histogram_name(self.name)) + lib.create_plot_script() + +def nop_highprio(self): + nop(self) + +def nop_highprio_time(self): + nop_time(self) + + +tests = (Test("nop", nop, True), + Test("nop-time", nop_time, False), + Test("nop-highprio", nop_highprio, True), + Test("nop-highprio-time", nop_highprio_time, False)) \ No newline at end of file -- 2.39.2