Tests: Move tests to tests/regression
[lttng-tools.git] / tests / regression / run-report.py
diff --git a/tests/regression/run-report.py b/tests/regression/run-report.py
new file mode 100755 (executable)
index 0000000..acee181
--- /dev/null
@@ -0,0 +1,444 @@
+#!/usr/bin/env python
+
+import os, sys
+import subprocess
+import threading
+import Queue
+import time
+import shlex
+
+from signal import signal, SIGTERM, SIGINT, SIGPIPE, SIG_DFL
+
+SESSIOND_BIN_NAME = "lttng-sessiond"
+SESSIOND_BIN_PATH = "src/bin/lttng-sessiond/"
+CONSUMERD_BIN_NAME = "lttng-consumerd"
+CONSUMERD_BIN_PATH = "src/bin/lttng-consumerd/"
+TESTDIR_PATH = ""
+
+PRINT_BRACKET = "\033[1;34m[\033[1;33m+\033[1;34m]\033[00m"
+PRINT_RED_BRACKET = "\033[1;31m[+]\033[00m"
+PRINT_GREEN_BRACKET = "\033[1;32m[+]\033[00m"
+PRINT_ARROW = "\033[1;32m-->\033[00m"
+
+is_root = 1
+no_stats = 0
+stop_sampling = 1
+
+top_cpu_legend = { 'us': "User CPU time", 'sy': "System CPU time",
+        'id': "Idle CPU time", 'ni': "Nice CPU time", 'wa': "iowait",
+        'hi': "Hardware IRQ", 'si': "Software Interrupts", 'st': "Steal Time", }
+
+cpu_ret_q = Queue.Queue()
+mem_ret_q = Queue.Queue()
+test_ret_q = Queue.Queue()
+
+global sdaemon_proc
+global worker_proc
+
+def cpu_create_usage_dict(top_line):
+    """
+    Return a dictionnary from a 'top' cpu line.
+    Ex: Cpu(s):  2.1%us,  1.2%sy,  0.0%ni, 96.2%id,  0.4%wa,  0.0%hi,  0.0%si,  0.0%st
+    """
+    top_dict = {'us': 0, 'sy': 0, 'ni': 0, 'id': 0, 'wa': 0, 'hi': 0, 'si': 0, 'st': 0}
+
+    # Split expression and remove first value which is "Cpu(s)"
+    top_line = top_line.replace(",","")
+    words = top_line.split()[1:]
+
+
+    for key in top_dict:
+        index = words.index(key)
+        # Add the value to the dictionnary
+        val = words[index-1]
+        top_dict[key] = float(val)
+
+    return top_dict
+
+def cpu_average_usage(top_lines):
+    """
+    Return a dictionnary of 'top' CPU stats but averaging all values.
+    """
+    avg_dict = {'us': 0, 'sy': 0, 'ni': 0, 'id': 0, 'wa': 0, 'hi': 0, 'si': 0, 'st': 0}
+    # Average count
+    count = 0.0
+
+    for line in top_lines:
+        tmp_dict = cpu_create_usage_dict(line)
+        # Add value to avg dictionnary
+        for key in tmp_dict:
+            avg_dict[key] += tmp_dict[key]
+
+        count += 1.0
+
+    for key in avg_dict:
+        avg_dict[key] = avg_dict[key] / count
+
+    return (count, avg_dict)
+
+def cpu_sample_usage(pid=None):
+    """
+    Sample CPU usage for num iterations.
+    If num is greater than 1, the average will be computed.
+    """
+    args = ["top", "-b", "-n", "1"]
+    if pid:
+        args.append("-p")
+        args.append(str(pid))
+
+    # Spawn top process
+    top = subprocess.Popen(args, stdout = subprocess.PIPE)
+
+    grep = subprocess.Popen(["grep", "Cpu"], stdin = top.stdout,
+            stdout = subprocess.PIPE)
+    top.stdout.close()
+
+    return grep.communicate()[0].strip("\n")
+
+def mem_sample_usage(pid):
+    """
+    Sample memory usage using /proc and a pid
+    """
+    args = ["cat", "/proc/" + str(pid) + "/status"]
+
+    if not os.path.isfile(args[1]):
+        return -1
+
+    mem_proc = subprocess.Popen(args, stdout = subprocess.PIPE)
+
+    grep = subprocess.Popen(["grep", "^VmRSS"], stdin = mem_proc.stdout,
+            stdout = subprocess.PIPE)
+    mem_proc.stdout.close()
+
+    # Return virtual memory size in kilobytes (kB)
+    #ret = grep.communicate()[0].split()
+    ret = grep.communicate()[0].split()
+
+    if len(ret) > 1:
+        ret = ret[1]
+    else:
+        ret = 0
+
+    return int(ret)
+
+class SamplingWorker(threading.Thread):
+    def __init__(self, s_type, worker = None, delay = 0.2, pid = 0):
+        threading.Thread.__init__ (self)
+        self.s_type = s_type
+        self.delay = delay
+        self.pid = pid
+        self.worker = worker
+
+    def run(self):
+        count = 1
+        lines = []
+
+        if self.s_type == "cpu":
+            while 1:
+                if self.worker == None:
+                    cpu_line = cpu_sample_usage(self.pid)
+                    lines.append(cpu_line)
+                    break
+                elif self.worker.is_alive():
+                    cpu_line = cpu_sample_usage(self.pid)
+                    lines.append(cpu_line)
+                else:
+                    break
+
+                # Delay sec per memory sampling
+                time.sleep(self.delay)
+
+            count, stats = cpu_average_usage(lines)
+            cpu_ret_q.put((count, stats))
+            # grep process has ended here
+
+        elif self.s_type == "mem":
+            count = 0
+            mem_stat = 0
+
+            while 1:
+                if self.worker == None:
+                    cpu_line = cpu_sample_usage(self.pid)
+                    lines.append(cpu_line)
+                    break
+                elif self.worker.is_alive():
+                    mem_stat += get_mem_usage(self.pid)
+                    count += 1
+                else:
+                    break
+
+                # Delay sec per memory sampling
+                time.sleep(self.delay)
+
+            mem_ret_q.put((count, mem_stat))
+
+class TestWorker(threading.Thread):
+    def __init__(self, path, name, env):
+        threading.Thread.__init__(self)
+        self.path = path
+        self.name = name
+        self.env  = env
+
+    def run(self):
+        bin_path_name = os.path.join(self.path, self.name)
+
+        test = subprocess.Popen([bin_path_name], env=self.env, preexec_fn = lambda: signal(SIGPIPE, SIG_DFL))
+        test.wait()
+
+        # Send ret value to main thread
+        test_ret_q.put(test.returncode)
+
+def get_pid(procname):
+    """
+    Return pid of process name using 'pidof' command
+    """
+    pidof = subprocess.Popen(["pidof", procname], stdout = subprocess.PIPE)
+    pid = pidof.communicate()[0].split()
+
+    if pid == []:
+        return 0
+
+    return int(pid[0])
+
+def spawn_session_daemon():
+    """
+    Exec the session daemon and return PID
+    """
+    global sdaemon_proc
+
+    pid = get_pid(SESSIOND_BIN_NAME)
+    if pid != 0:
+        os.kill(pid, SIGTERM)
+
+    bin_path = os.path.join(TESTDIR_PATH, "..", SESSIOND_BIN_PATH, SESSIOND_BIN_NAME)
+    consumer_path = os.path.join(TESTDIR_PATH, "..", CONSUMERD_BIN_PATH, CONSUMERD_BIN_NAME)
+
+    if not os.path.isfile(bin_path):
+        print "Error: No session daemon binary found. Compiled?"
+        return 0
+
+    try:
+        args = shlex.split("libtool execute " + bin_path
+                           + " --consumerd32-path=" + consumer_path
+                           + " --consumerd64-path=" + consumer_path)
+
+        sdaemon_proc = subprocess.Popen(args, shell = False, stderr = subprocess.PIPE)
+
+    except OSError, e:
+        print e
+        return 0
+
+    time.sleep(1)
+
+    return get_pid("lt-" + SESSIOND_BIN_NAME)
+
+def start_test(name):
+    """
+    Spawn test and return exit code
+    """
+    tw = TestWorker(".", name)
+    tw.start()
+
+    return test_ret_q.get(True)
+
+def print_cpu_stats(stats, count):
+    """
+    Pretty print on one line the CPU stats
+    """
+    sys.stdout.write(PRINT_ARROW + " Cpu [sampled %d time(s)]:\n   " % (count))
+    for stat in stats:
+        sys.stdout.write(" %s: %.2f, " % (stat, stats[stat]))
+    print ""
+
+def get_cpu_usage(delay=1, pid=0):
+    """
+    Spawn a worker thread to sample cpu usage.
+    """
+    sw = SamplingWorker("cpu", delay = delay, pid = pid)
+    sw.start()
+
+    return cpu_ret_q.get(True)
+
+def get_mem_usage(pid):
+    """
+    Get memory usage for PID
+    """
+    return mem_sample_usage(pid)
+
+def print_test_success(ret, expect):
+    """
+    Print if test has failed or pass according to the expected value.
+    """
+    if ret != expect:
+        print "\n" + PRINT_RED_BRACKET + \
+                " Failed: ret = %d (expected %d)" % (ret, expect)
+        return 1
+    else:
+        print "\n" + PRINT_BRACKET + \
+                " Passed: ret = %d (expected %d)" % (ret, expect)
+        return 0
+
+def run_test(test):
+    """
+    Run test 'name' and output report of the test with stats.
+    """
+    global worker_proc
+    global sdaemon_proc
+    dem_pid = 0     # Session daemon pid
+
+    print PRINT_BRACKET + " %s" % (test['name'])
+    print PRINT_ARROW + " %s" % (test['desc'])
+    if no_stats:
+        print PRINT_ARROW + " Statistics will NOT be collected"
+    else:
+        print PRINT_ARROW + " Statistics of the session daemon will be collected"
+
+    if test['kern'] and not is_root:
+        print "Needs root for kernel tracing. Skipping"
+        return 0
+
+    if not os.path.isfile(test['bin']):
+        print "Unable to find test file '%s'. Skipping" % (test['bin'])
+        return 0
+
+    # Session daemon is controlled by the test
+    if test['daemon'] == "test":
+        print PRINT_ARROW + " Session daemon is controlled by the test"
+        env = os.environ
+        env['TEST_NO_SESSIOND'] = '0'
+        tw = TestWorker(".", test['bin'], env)
+        tw.start()
+        ret = test_ret_q.get(True)
+        print_test_success(ret, test['success'])
+        return 0
+    elif test['daemon'] == False:
+        print PRINT_ARROW + " No session daemon needed"
+        env = os.environ
+        env['TEST_NO_SESSIOND'] = '1'
+        tw = TestWorker(".", test['bin'], env)
+        tw.start()
+        ret = test_ret_q.get(True)
+        print_test_success(ret, test['success'])
+        return 0
+    else:
+        print PRINT_ARROW + " Session daemon needed"
+
+    dem_pid = spawn_session_daemon()
+    if dem_pid <= 0:
+        print "Unable to start %s. Stopping" % (SESSIOND_BIN_NAME)
+        print sdaemon_proc.communicate()[1]
+        return 0
+
+    print PRINT_BRACKET + " Session daemon spawned (pid: %d)\n" % (dem_pid)
+
+    if not no_stats:
+        mem_before = get_mem_usage(dem_pid)
+        print PRINT_BRACKET + " Stats *before* test:"
+        print PRINT_ARROW + " Mem (kB): %d" % (mem_before)
+        cpu_count, cpu_stats = get_cpu_usage(pid = dem_pid)
+        print_cpu_stats(cpu_stats, cpu_count)
+
+    # Sessiond was already spawned, do not let the test spawn
+    # an additional sessiond
+    env = os.environ
+    env['TEST_NO_SESSIOND'] = '1'
+
+    tw = TestWorker(".", test['bin'], env)
+    tw.start()
+
+    if not no_stats:
+        # Start CPU sampling for test
+        sw_cpu = SamplingWorker("cpu", worker = tw, pid = dem_pid)
+        sw_cpu.start()
+        sw_mem = SamplingWorker("mem", worker = tw, pid = dem_pid)
+        sw_mem.start()
+
+    ret = test_ret_q.get(True)
+
+    if not no_stats:
+        time.sleep(2)
+        # Compute memory average
+        mem_count, mem_during = mem_ret_q.get(True)
+        mem_during = float(mem_during) / float(mem_count)
+        cpu_count, cpu_stats = cpu_ret_q.get(True)
+
+        print "\n" + PRINT_BRACKET + " Stats *during* test:"
+        print PRINT_ARROW + " Mem (kB): %.0f [sampled %d time(s)]" % (mem_during, mem_count)
+        print_cpu_stats(cpu_stats, cpu_count)
+
+        mem_after = get_mem_usage(dem_pid)
+        print "\n" + PRINT_BRACKET + " Stats *after* test:"
+        print PRINT_ARROW + " Mem (kB): %d" % (mem_after)
+        cpu_count, cpu_stats = get_cpu_usage(pid = dem_pid)
+        print_cpu_stats(cpu_stats, cpu_count)
+
+        print "\n" + PRINT_BRACKET + " Memory usage differences:"
+        print PRINT_ARROW + " Diff during and before (kB): %d" % (mem_during - mem_before)
+        print PRINT_ARROW + " Diff during and after (kB): %d" % (mem_during - mem_after)
+        print PRINT_ARROW + " Diff before and after (kB): %d" % (mem_after - mem_before)
+
+    # Return value of 0 means that is passed else it failed
+    ret = print_test_success(ret, test['success'])
+
+    # Stop session daemon
+    if dem_pid > 0:
+        print PRINT_BRACKET + " Stopping session daemon (pid: %d)..." % (dem_pid)
+        try:
+            os.kill(dem_pid, SIGTERM)
+            # This call simply does not work... It seems python does not relay the signal
+            # to the child processes of sdaemon_proc.
+            # sdaemon_proc.terminate()
+            if ret != 0:
+                print sdaemon_proc.communicate()[1]
+            elif sdaemon_proc.returncode == None:
+                sdaemon_proc.communicate()
+        except OSError, e:
+            print e
+
+    # Make sure all thread are released
+    if not no_stats:
+        tw.join()
+        sw_cpu.join()
+        sw_mem.join()
+
+    return ret
+
+def main():
+    for test in Tests:
+        if not test['enabled']:
+            continue
+
+        ret = run_test(test)
+        if ret != 0:
+            # Stop all tests, the last one failed
+            return
+        print ""
+
+def cleanup(signo, stack):
+    """ Cleanup function """
+    sys.exit(0)
+
+if __name__ == "__main__":
+    if not os.getuid() == 0:
+        is_root = 0
+        print "NOTICE: Not root. No kernel tracing will be tested\n"
+
+    if os.path.isfile("test_list.py"):
+        from test_list import Tests
+    else:
+        print "No test_list.py found. Stopping"
+        cleanup(0, 0)
+
+    TESTDIR_PATH = os.getcwd()
+
+    if len(sys.argv) > 1:
+        if sys.argv[1] == "--no-stats":
+            no_stats = 1
+
+    try:
+        signal(SIGTERM, cleanup)
+        signal(SIGINT, cleanup)
+        main()
+        cleanup(0, 0)
+    except KeyboardInterrupt:
+        cleanup(0, 0)
This page took 0.027697 seconds and 4 git commands to generate.