Adds RTEMS target to makefile and associated Python scripts.
authorMartin Hořeňovský <Martin.Horenovsky@gmail.com>
Mon, 16 Sep 2013 13:07:09 +0000 (15:07 +0200)
committerMartin Hořeňovský <Martin.Horenovsky@gmail.com>
Mon, 16 Sep 2013 13:07:09 +0000 (15:07 +0200)
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
gw-tests/lib.py [new file with mode: 0644]
gw-tests/rtems_bench.py [new file with mode: 0755]
gw-tests/tests.py [new file with mode: 0644]

index 5cb074c..ddfc9a4 100644 (file)
@@ -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 (file)
index 0000000..700eacb
--- /dev/null
@@ -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 (executable)
index 0000000..51539b8
--- /dev/null
@@ -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\r
+            #otherwise, they are just histogram generators\r
+            if test.has_image:\r
+                #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 (file)
index 0000000..a06a1ae
--- /dev/null
@@ -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