From e804299c3f62e1739c16385193620810e0259f00 Mon Sep 17 00:00:00 2001
From: Tomasz Grabiec <tgrabiec@cloudius-systems.com>
Date: Mon, 28 Apr 2014 18:34:10 +0200
Subject: [PATCH] tests: introduce tracing smoke test

The test verifies basic tracing functionallity:
 * that samples are collected by OSv
 * extraction via `trace extract` succeeds
 * `trace summary` shows expected tracepoint names
 * listing via `trace list` shows tracepoints with arguments
 * network packet parsing works

This test is run as part of standard 'make check'.

Note: this single test can be executed as easily as:

 $ scripts/test.py --test tracing_smoke_test

Signed-off-by: Tomasz Grabiec <tgrabiec@cloudius-systems.com>
Signed-off-by: Pekka Enberg <penberg@cloudius-systems.com>
---
 scripts/test.py               |  1 +
 scripts/tests/test_net.py     |  3 ++-
 scripts/tests/test_tracing.py | 35 ++++++++++++++++++++++++++
 scripts/tests/testing.py      | 46 ++++++++++++++++++++++++-----------
 4 files changed, 70 insertions(+), 15 deletions(-)
 create mode 100644 scripts/tests/test_tracing.py

diff --git a/scripts/test.py b/scripts/test.py
index 0215b2082..89740dc3f 100755
--- a/scripts/test.py
+++ b/scripts/test.py
@@ -8,6 +8,7 @@ import os
 import re
 
 import tests.test_net
+import tests.test_tracing
 
 from operator import attrgetter
 from tests.testing import *
diff --git a/scripts/tests/test_net.py b/scripts/tests/test_net.py
index 0c7481faf..d0ddd22b9 100644
--- a/scripts/tests/test_net.py
+++ b/scripts/tests/test_net.py
@@ -8,7 +8,8 @@ def is_broken_pipe_error(e):
 @test
 def tcp_close_without_reading():
     host_port = 7777
-    server = run_guest('/tests/misc-tcp-close-without-reading.so', forward=[(host_port, 7777)])
+    server = run_command_in_guest('/tests/misc-tcp-close-without-reading.so',
+        forward=[(host_port, 7777)])
 
     wait_for_line(server, 'listening...')
 
diff --git a/scripts/tests/test_tracing.py b/scripts/tests/test_tracing.py
new file mode 100644
index 000000000..14eef46c7
--- /dev/null
+++ b/scripts/tests/test_tracing.py
@@ -0,0 +1,35 @@
+from testing import *
+import os
+import subprocess
+
+@test
+def tracing_smoke_test():
+    path = '/this/path/does/not/exist'
+    guest = Guest(['--trace=vfs_*,net_packet*', '-e', path], hold_with_poweroff=True, show_output_on_error=False)
+    try:
+        wait_for_line(guest, 'run_main(): cannot execute %s. Powering off.' % path)
+
+        trace_script = os.path.join(osv_base, 'scripts', 'trace.py')
+
+        if os.path.exists('tracefile'):
+            os.remove('tracefile')
+
+        assert(subprocess.call([trace_script, 'extract']) == 0)
+
+        summary = subprocess.check_output([trace_script, 'summary'])
+        assert('vfs_open' in summary)
+        assert('vfs_open_err' in summary)
+
+        samples = subprocess.check_output([trace_script, 'list'])
+        assert('vfs_open             "%s" 0x0 00' % path in samples)
+        assert('vfs_open_err         2' in samples)
+
+        tcpdump = subprocess.check_output([trace_script, 'tcpdump'])
+        assert('0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from' in tcpdump)
+        assert('192.168.122.1.67 > 255.255.255.255.68: BOOTP/DHCP, Reply' in tcpdump)
+
+        tcpdump = subprocess.check_output([trace_script, 'list', '--tcpdump'])
+        assert('0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from' in tcpdump)
+        assert('192.168.122.1.67 > 255.255.255.255.68: BOOTP/DHCP, Reply' in tcpdump)
+    finally:
+        guest.kill()
diff --git a/scripts/tests/testing.py b/scripts/tests/testing.py
index e4a09548d..bbf9bdd08 100644
--- a/scripts/tests/testing.py
+++ b/scripts/tests/testing.py
@@ -4,10 +4,13 @@ import sys
 import subprocess
 import threading
 import traceback
+import socket
 
 tests = []
 _verbose_output = False
 
+osv_base = '.'
+
 class TestFailed(Exception):
     pass
 
@@ -24,7 +27,7 @@ class SingleCommandTest(Test):
         self.command = command
 
     def run(self):
-        run_guest_sync(self.command)
+        run_command_in_guest(self.command).join()
 
 class test(Test):
     """
@@ -72,13 +75,14 @@ def scan_errors(s):
     return False
 
 class SupervisedProcess:
-    def __init__(self, args, show_output=False):
+    def __init__(self, args, show_output=False, show_output_on_error=True):
         self.process = subprocess.Popen(args, stdout=subprocess.PIPE)
         self.cv = threading.Condition()
         self.lines = []
         self.output_collector_done = False
         self.has_errors = False
         self.show_output = show_output
+        self.show_output_on_error = show_output_on_error
 
         self.output_collector_thread = threading.Thread(target=self._output_collector)
         self.output_collector_thread.start()
@@ -89,7 +93,7 @@ class SupervisedProcess:
 
             if not self.has_errors and scan_errors(line):
                 self.has_errors = True
-                if not self.show_output:
+                if self.show_output_on_error and not self.show_output:
                     sys.stdout.write(self.output)
                     sys.stdout.flush()
                     self.show_output = True
@@ -162,21 +166,35 @@ class SupervisedProcess:
         assert not self.output_collector_thread.isAlive()
         return self.has_errors or self.process.returncode
 
+def run_command_in_guest(command, **kwargs):
+    return Guest(["-s", "-e", command], **kwargs)
+
+class Guest(SupervisedProcess):
+    def __init__(self, args, forward=[], hold_with_poweroff=False, show_output_on_error=True):
+        run_script = os.path.join(osv_base, "scripts/run.py")
+
+        if hold_with_poweroff:
+            args.append('-H')
+
+        self.monitor_socket = 'qemu-monitor'
+        args.extend(['--pass-args=-monitor unix:%s,server,nowait' % self.monitor_socket])
 
-def run_guest(command, forward=[]):
-    osv_base = '.'
-    run_script = os.path.join(osv_base, "scripts/run.py")
-    args = ["-s", "-e", command]
+        for rule in forward:
+            args.extend(['--forward', 'tcp:%s::%s' % rule])
 
-    for rule in forward:
-        args.extend(['--forward', 'tcp:%s::%s' % rule])
+        SupervisedProcess.__init__(self, [run_script] + args,
+            show_output=_verbose_output,
+            show_output_on_error=show_output_on_error)
 
-    return SupervisedProcess([run_script] + args, show_output=_verbose_output)
+    def send_command(self, command):
+        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        s.connect(self.monitor_socket)
+        s.send(command)
+        s.send('\n')
+        s.close()
 
-def run_guest_sync(*args, **kwargs):
-    guest = run_guest(*args, **kwargs)
-    guest.join()
-    return guest
+    def kill(self):
+        self.send_command('quit')
 
 def wait_for_line(guest, text):
     for line in guest.read_lines():
-- 
GitLab