[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH XTF v2 3/4] xtf: Add monitor test class



This class starts alongside the domain a monitor application which opens
an event channel corresponding to that domain and handles the received
requests.
Use the "monitor_args" key to pass test specific arguments to the
monitor application.
The arguments will be added in the test's Makefile using the
TEST-EXTRA-INFO variable.

Signed-off-by: Petre Pircalabu <ppircalabu@xxxxxxxxxxxxxxx>
---
 Makefile                  |   6 +-
 build/common.mk           |  22 ++-
 build/files.mk            |   3 +
 build/gen.mk              |  12 ++
 common/report.c           |   8 -
 docs/all-tests.dox        |   3 +
 include/monitor/monitor.h | 136 +++++++++++++
 include/xtf/report.h      |   8 +
 monitor/Makefile          |  20 ++
 monitor/monitor.c         | 481 ++++++++++++++++++++++++++++++++++++++++++++++
 xtf/__init__.py           |   2 +-
 xtf/monitor_test.py       | 132 +++++++++++++
 xtf/utils.py              |  17 ++
 13 files changed, 838 insertions(+), 12 deletions(-)
 create mode 100644 include/monitor/monitor.h
 create mode 100644 monitor/Makefile
 create mode 100644 monitor/monitor.c
 create mode 100644 xtf/monitor_test.py
 create mode 100644 xtf/utils.py

diff --git a/Makefile b/Makefile
index 15a865f..db28075 100644
--- a/Makefile
+++ b/Makefile
@@ -32,7 +32,9 @@ INSTALL_PROGRAM := $(INSTALL) -p
 OBJCOPY         := $(CROSS_COMPILE)objcopy
 PYTHON          := python
 
-export CC CPP INSTALL INSTALL_DATA INSTALL_DIR INSTALL_PROGRAM OBJCOPY PYTHON
+HOSTCC          := gcc
+
+export CC CPP INSTALL INSTALL_DATA INSTALL_DIR INSTALL_PROGRAM OBJCOPY PYTHON 
HOSTCC
 
 .PHONY: all
 all:
@@ -51,7 +53,7 @@ install:
        done
 
 define all_sources
-       find include/ arch/ common/ tests/ -name "*.[hcsS]"
+       find include/ arch/ common/ tests/ monitor/ -name "*.[hcsS]"
 endef
 
 .PHONY: cscope
diff --git a/build/common.mk b/build/common.mk
index b786ddf..1ec0fa4 100644
--- a/build/common.mk
+++ b/build/common.mk
@@ -1,4 +1,4 @@
-ALL_CATEGORIES     := special functional xsa utility in-development
+ALL_CATEGORIES     := special functional xsa utility in-development monitor
 
 ALL_ENVIRONMENTS   := pv64 pv32pae hvm64 hvm32pae hvm32pse hvm32
 
@@ -35,11 +35,20 @@ COMMON_AFLAGS-x86_64 := -m64
 COMMON_CFLAGS-x86_32 := -m32
 COMMON_CFLAGS-x86_64 := -m64
 
+#HOSTCFLAGS := -Wall -Werror
+HOSTCFLAGS  :=
+HOSTLDFLAGS :=
+HOSTLDLIBS  :=
+HOSTCFLAGS  += -D__XEN_TOOLS__ -g -O3 -I$(ROOT)/include/monitor
+HOSTCFLAGS  += -DXC_WANT_COMPAT_DEVICEMODEL_API 
-DXC_WANT_COMPAT_MAP_FOREIGN_API
+HOSTLDLIBS  += -lxenctrl -lxenstore -lxenevtchn
+
 defcfg-pv    := $(ROOT)/config/default-pv.cfg.in
 defcfg-hvm   := $(ROOT)/config/default-hvm.cfg.in
 
 obj-perarch :=
 obj-perenv  :=
+obj-monitor :=
 include $(ROOT)/build/files.mk
 
 
@@ -90,8 +99,19 @@ DEPS-$(1) = $$(head-$(1)) \
 
 endef
 
+# Setup monitor rules
+define MONITOR_setup
+DEPS-MONITOR = \
+       $$(obj-monitor:%.o=%-monitor.o)
+
+%-monitor.o: %.c
+       $$(HOSTCC) $$(HOSTCFLAGS) -c $$< -o $$@
+endef
+
 $(foreach env,$(ALL_ENVIRONMENTS),$(eval $(call PERENV_setup,$(env))))
 
+$(eval $(call MONITOR_setup))
+
 define move-if-changed
        if ! cmp -s $(1) $(2); then mv -f $(1) $(2); else rm -f $(1); fi
 endef
diff --git a/build/files.mk b/build/files.mk
index dfa27e4..972c797 100644
--- a/build/files.mk
+++ b/build/files.mk
@@ -54,3 +54,6 @@ $(foreach env,$(32BIT_ENVIRONMENTS),$(eval obj-$(env) += 
$(obj-32)))
 # 64bit specific objects
 obj-64  += $(ROOT)/arch/x86/entry_64.o
 $(foreach env,$(64BIT_ENVIRONMENTS),$(eval obj-$(env) += $(obj-64)))
+
+# Monitor common objects
+obj-monitor += $(ROOT)/monitor/monitor.o
diff --git a/build/gen.mk b/build/gen.mk
index c19ca6a..1e6773a 100644
--- a/build/gen.mk
+++ b/build/gen.mk
@@ -32,6 +32,9 @@ CLASS ?= "xtf.domu_test.DomuTestInfo"
 .PHONY: build
 build: $(foreach env,$(TEST-ENVS),test-$(env)-$(NAME)) $(TEST-CFGS)
 build: info.json
+ifeq (x$(CATEGORY),xmonitor)
+build: test-monitor-$(NAME)
+endif
 
 MKINFO-OPTS := -n "$(NAME)"
 MKINFO-OPTS += -c "$(CLASS)"
@@ -100,6 +103,15 @@ install-each-env: install-$(1) install-$(1).cfg
 endef
 $(foreach env,$(TEST-ENVS),$(eval $(call PERENV_build,$(env))))
 
+define MONITOR_build
+test-monitor-$(NAME): $(DEPS-MONITOR)
+       @echo $(obj-monitor)
+       @echo $(DEPS-MONITOR)
+       $(HOSTCC) $(HOSTLDFLAGS) $(DEPS-MONITOR) $(HOSTLDLIBS) -o $$@
+endef
+
+$(eval $(call MONITOR_build))
+
 .PHONY: clean
 clean:
        find $(ROOT) \( -name "*.o" -o -name "*.d" \) -delete
diff --git a/common/report.c b/common/report.c
index ffdf098..745713a 100644
--- a/common/report.c
+++ b/common/report.c
@@ -2,14 +2,6 @@
 #include <xtf/report.h>
 #include <xtf/hypercall.h>
 
-enum test_status {
-    STATUS_RUNNING, /**< Test not yet completed.       */
-    STATUS_SUCCESS, /**< Test was successful.          */
-    STATUS_SKIP,    /**< Test cannot be completed.     */
-    STATUS_ERROR,   /**< Issue with the test itself.   */
-    STATUS_FAILURE, /**< Issue with the tested matter. */
-};
-
 /** Current status of this test. */
 static enum test_status status;
 
diff --git a/docs/all-tests.dox b/docs/all-tests.dox
index 732d44c..11b7f41 100644
--- a/docs/all-tests.dox
+++ b/docs/all-tests.dox
@@ -145,4 +145,7 @@ enable BTS.
 @subpage test-nested-svm - Nested SVM tests.
 
 @subpage test-nested-vmx - Nested VT-x tests.
+
+
+@section index-monitor Monitor
 */
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
new file mode 100644
index 0000000..a8e13a8
--- /dev/null
+++ b/include/monitor/monitor.h
@@ -0,0 +1,136 @@
+/*
+ * XTF Monitor interface
+ */
+
+#ifndef XTF_MONITOR_H
+#define XTF_MONITOR_H
+
+#include <inttypes.h>
+#include <xenctrl.h>
+#include <xenevtchn.h>
+#include <xenstore.h>
+#include <xen/vm_event.h>
+
+/**
+ * The value was chosen to be greater than any VM_EVENT_REASON_*
+ */
+#define VM_EVENT_REASON_MAX 20
+
+/* Should be in sync with "test_status" from common/report.c */
+enum xtf_mon_status
+{
+    XTF_MON_STATUS_RUNNING,
+    XTF_MON_STATUS_SUCCESS,
+    XTF_MON_STATUS_SKIP,
+    XTF_MON_STATUS_ERROR,
+    XTF_MON_STATUS_FAILURE,
+};
+
+enum xtf_mon_log_level
+{
+    XTF_MON_LOG_LEVEL_FATAL,
+    XTF_MON_LOG_LEVEL_ERROR,
+    XTF_MON_LOG_LEVEL_WARNING,
+    XTF_MON_LOG_LEVEL_INFO,
+    XTF_MON_LOG_LEVEL_DEBUG,
+    XTF_MON_LOG_LEVEL_TRACE,
+};
+
+void xtf_log(enum xtf_mon_log_level lvl, const char *fmt, ...) 
__attribute__((__format__(__printf__, 2, 3)));
+
+#define XTF_MON_FATAL(format...)    xtf_log(XTF_MON_LOG_LEVEL_FATAL,    format)
+#define XTF_MON_ERROR(format...)    xtf_log(XTF_MON_LOG_LEVEL_ERROR,    format)
+#define XTF_MON_WARNING(format...)  xtf_log(XTF_MON_LOG_LEVEL_WARNING,  format)
+#define XTF_MON_INFO(format...)     xtf_log(XTF_MON_LOG_LEVEL_INFO,     format)
+#define XTF_MON_DEBUG(format...)    xtf_log(XTF_MON_LOG_LEVEL_DEBUG,    format)
+#define XTF_MON_TRACE(format...)    xtf_log(XTF_MON_LOG_LEVEL_TRACE,    format)
+
+typedef int (*vm_event_handler_t)(vm_event_request_t *, vm_event_response_t *);
+
+/** XTF VM Event Ring interface */
+typedef struct xtf_vm_event_ring
+{
+    /* Event channel handle */
+    xenevtchn_handle *xce_handle;
+
+    /* Event channel remote port */
+    xenevtchn_port_or_error_t remote_port;
+
+    /* Event channel local port */
+    evtchn_port_t local_port;
+
+    /* vm_event back ring */
+    vm_event_back_ring_t back_ring;
+
+    /* Shared ring page */
+    void *ring_page;
+} xtf_vm_event_ring_t;
+
+typedef struct xtf_monitor_ops
+{
+    /* Test specific setup */
+    int (*setup)(int, char*[]);
+
+    /* Test specific initialization */
+    int (*init)(void);
+
+    /* Test specific cleanup */
+    int (*cleanup)(void);
+
+    /* Returns the test's result */
+    int (*get_result)(void);
+} xtf_monitor_ops_t;
+
+/* XTF Monitor Driver */
+typedef struct xtf_monitor
+{
+    /* Domain ID */
+    domid_t domain_id;
+
+    /* LibXC intreface handle */
+    xc_interface *xch;
+
+    /* XEN store handle */
+    struct xs_handle *xsh;
+
+    /* XTF VM_EVENT ring */
+    xtf_vm_event_ring_t ring;
+
+    /* Log Level */
+    enum xtf_mon_log_level log_lvl;
+
+    /* Test status */
+    enum xtf_mon_status status;
+
+    /* Test Help message*/
+    const char * help_message;
+
+    /* Test specific operations */
+    xtf_monitor_ops_t ops;
+
+    /* Test specific VM_EVENT request handlers */
+    vm_event_handler_t handlers[VM_EVENT_REASON_MAX];
+
+} xtf_monitor_t;
+
+void usage();
+
+extern xtf_monitor_t *monitor;
+
+#define XTF_MONITOR(param) \
+static void  __attribute__((constructor)) register_monitor_##param() \
+{ \
+    monitor = (xtf_monitor_t *)&param; \
+}
+
+#endif /* XTF_MONITOR_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xtf/report.h b/include/xtf/report.h
index 36517bd..08a9b53 100644
--- a/include/xtf/report.h
+++ b/include/xtf/report.h
@@ -24,6 +24,14 @@
  * kept.
  */
 
+enum test_status {
+    STATUS_RUNNING, /**< Test not yet completed.       */
+    STATUS_SUCCESS, /**< Test was successful.          */
+    STATUS_SKIP,    /**< Test cannot be completed.     */
+    STATUS_ERROR,   /**< Issue with the test itself.   */
+    STATUS_FAILURE, /**< Issue with the tested matter. */
+};
+
 /**
  * Report test success.
  */
diff --git a/monitor/Makefile b/monitor/Makefile
new file mode 100644
index 0000000..64d4f8a
--- /dev/null
+++ b/monitor/Makefile
@@ -0,0 +1,20 @@
+.PHONY: all
+
+all: monitor
+
+HOSTCC ?= gcc
+
+OBJS = monitor.o
+
+#HOSTCFLAGS += -Wall -Werror
+HOSTCFLAGS += -D__XEN_TOOLS__ -g -O0
+HOSTCFLAGS += -DXC_WANT_COMPAT_DEVICEMODEL_API -DXC_WANT_COMPAT_MAP_FOREIGN_API
+
+%.o : %.c
+       $(HOSTCC) -c $(HOSTCFLAGS) $(HOSTCPPFLAGS) $< -o $@
+
+monitor: $(OBJS)
+       $(HOSTCC) -o $@ $^ -lxenctrl -lxenstore -lxenevtchn
+
+clean:
+       $(RM) $(OBJS) monitor
diff --git a/monitor/monitor.c b/monitor/monitor.c
new file mode 100644
index 0000000..d1741bc
--- /dev/null
+++ b/monitor/monitor.c
@@ -0,0 +1,481 @@
+/**
+ * @file monitor/monitor.c
+ *
+ * Common functions for test specific monitor applications.
+ */
+
+#include <errno.h>
+#include <monitor.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#define call_helper(func, ... )         ( (func) ? func(__VA_ARGS__) : 0 )
+
+static const char *status_to_str[] =
+{
+#define STA(x) [XTF_MON_STATUS_ ## x] = #x
+
+    STA(RUNNING),
+    STA(SUCCESS),
+    STA(SKIP),
+    STA(ERROR),
+    STA(FAILURE),
+
+#undef STA
+};
+
+static const char *log_level_to_str[] =
+{
+#define XTFMLL(x) [ XTF_MON_LOG_LEVEL_ ## x] = #x
+
+    XTFMLL(FATAL),
+    XTFMLL(ERROR),
+    XTFMLL(WARNING),
+    XTFMLL(INFO),
+    XTFMLL(DEBUG),
+    XTFMLL(TRACE),
+
+#undef XTFMLL
+};
+
+void xtf_log(enum xtf_mon_log_level lvl, const char *fmt, ...)
+{
+    va_list argptr;
+
+    if ( lvl < 0 || lvl > monitor->log_lvl )
+        return;
+
+    fprintf(stderr, "[%s]\t", log_level_to_str[lvl]);
+    va_start(argptr, fmt);
+    vfprintf(stderr, fmt, argptr);
+    va_end(argptr);
+}
+
+static void xtf_print_status(enum xtf_mon_status s)
+{
+    if ( s > XTF_MON_STATUS_RUNNING && s <= XTF_MON_STATUS_FAILURE )
+        printf("Test result: %s\n", status_to_str[s]);
+}
+
+void usage()
+{
+    fprintf(stderr, "%s", monitor->help_message);
+}
+
+xtf_monitor_t *monitor;
+
+static int xtf_monitor_init()
+{
+    int rc;
+
+    monitor->ring.ring_page = xc_monitor_enable(monitor->xch,
+                                                monitor->domain_id,
+                                                &monitor->ring.remote_port);
+    if ( !monitor->ring.ring_page )
+    {
+        XTF_MON_ERROR("Error enabling monitor\n");
+        return -1;
+    }
+
+    monitor->ring.xce_handle = xenevtchn_open(NULL, 0);
+    if ( !monitor->ring.xce_handle )
+    {
+        XTF_MON_ERROR("Failed to open XEN event channel\n");
+        return -1;
+    }
+
+    rc = xenevtchn_bind_interdomain(monitor->ring.xce_handle,
+                                    monitor->domain_id,
+                                    monitor->ring.remote_port);
+    if ( rc < 0 )
+    {
+        XTF_MON_ERROR("Failed to bind XEN event channel\n");
+        return rc;
+    }
+    monitor->ring.local_port = rc;
+
+    /* Initialise ring */
+    SHARED_RING_INIT((vm_event_sring_t *)monitor->ring.ring_page);
+    BACK_RING_INIT(&monitor->ring.back_ring,
+                   (vm_event_sring_t *)monitor->ring.ring_page,
+                   XC_PAGE_SIZE);
+
+    return 0;
+}
+
+static int xtf_monitor_cleanup()
+{
+    int rc;
+
+    if ( monitor->ring.ring_page )
+        munmap(monitor->ring.ring_page, XC_PAGE_SIZE);
+
+    rc = xc_monitor_disable(monitor->xch, monitor->domain_id);
+    if ( rc != 0 )
+    {
+        XTF_MON_INFO("Error disabling monitor\n");
+        return rc;
+    }
+
+    rc = xenevtchn_unbind(monitor->ring.xce_handle, monitor->ring.local_port);
+    if ( rc != 0 )
+    {
+        XTF_MON_INFO("Failed to unbind XEN event channel\n");
+        return rc;
+    }
+
+    rc = xenevtchn_close(monitor->ring.xce_handle);
+    if ( rc != 0 )
+    {
+        XTF_MON_INFO("Failed to close XEN event channel\n");
+        return rc;
+    }
+
+    return 0;
+}
+
+static int xtf_monitor_wait_for_event(unsigned long ms)
+{
+    int rc;
+    struct pollfd fds[2] = {
+        { .fd = xenevtchn_fd(monitor->ring.xce_handle), .events = POLLIN | 
POLLERR },
+        { .fd = xs_fileno(monitor->xsh), .events = POLLIN | POLLERR },
+    };
+
+    rc = poll(fds, 2, ms);
+
+    if ( rc < 0 )
+        return -errno;
+
+    if ( rc == 0 )
+        return 0;
+
+    if ( fds[0].revents )
+    {
+        int port = xenevtchn_pending(monitor->ring.xce_handle);
+        if ( port == -1 )
+            return -errno;
+
+        rc = xenevtchn_unmask(monitor->ring.xce_handle, port);
+        if ( rc != 0 )
+            return -errno;
+
+        return port;
+    }
+
+    if ( fds[1].revents )
+    {
+        if ( !xs_is_domain_introduced(monitor->xsh, monitor->domain_id) )
+        {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    return -2;  /* Error */
+}
+
+/*
+ * X86 control register names
+ */
+static const char* get_x86_ctrl_reg_name(uint32_t index)
+{
+    switch (index)
+    {
+        case VM_EVENT_X86_CR0:
+            return "CR0";
+        case VM_EVENT_X86_CR3:
+            return "CR3";
+        case VM_EVENT_X86_CR4:
+            return "CR4";
+        case VM_EVENT_X86_XCR0:
+            return "XCR0";
+    }
+    return "";
+}
+
+static void xtf_monitor_dump_request(enum xtf_mon_log_level lvl, 
vm_event_request_t *req)
+{
+    switch ( req->reason )
+    {
+    case VM_EVENT_REASON_MEM_ACCESS:
+        xtf_log(lvl, "PAGE ACCESS: %c%c%c for GFN %"PRIx64" (offset %06"
+                PRIx64") gla %016"PRIx64" (valid: %c; fault in gpt: %c; fault 
with gla: %c) (vcpu %u [%c], altp2m view %u)\n",
+                (req->u.mem_access.flags & MEM_ACCESS_R) ? 'r' : '-',
+                (req->u.mem_access.flags & MEM_ACCESS_W) ? 'w' : '-',
+                (req->u.mem_access.flags & MEM_ACCESS_X) ? 'x' : '-',
+                req->u.mem_access.gfn,
+                req->u.mem_access.offset,
+                req->u.mem_access.gla,
+                (req->u.mem_access.flags & MEM_ACCESS_GLA_VALID) ? 'y' : 'n',
+                (req->u.mem_access.flags & MEM_ACCESS_FAULT_IN_GPT) ? 'y' : 
'n',
+                (req->u.mem_access.flags & MEM_ACCESS_FAULT_WITH_GLA) ? 'y': 
'n',
+                req->vcpu_id,
+                (req->flags & VM_EVENT_FLAG_VCPU_PAUSED) ? 'p' : 'r',
+                req->altp2m_idx);
+        break;
+
+    case VM_EVENT_REASON_SOFTWARE_BREAKPOINT:
+        xtf_log(lvl, "Breakpoint: rip=%016"PRIx64", gfn=%"PRIx64" (vcpu %d)\n",
+                req->data.regs.x86.rip,
+                req->u.software_breakpoint.gfn,
+                req->vcpu_id);
+        break;
+
+    case VM_EVENT_REASON_PRIVILEGED_CALL:
+        xtf_log(lvl, "Privileged call: pc=%"PRIx64" (vcpu %d)\n",
+                req->data.regs.arm.pc,
+                req->vcpu_id);
+
+    case VM_EVENT_REASON_SINGLESTEP:
+        xtf_log(lvl, "Singlestep: rip=%016lx, vcpu %d, altp2m %u\n",
+                req->data.regs.x86.rip,
+                req->vcpu_id,
+                req->altp2m_idx);
+        break;
+
+    case VM_EVENT_REASON_DEBUG_EXCEPTION:
+        printf("Debug exception: rip=%016"PRIx64", vcpu %d. Type: %u. Length: 
%u\n",
+                req->data.regs.x86.rip,
+                req->vcpu_id,
+                req->u.debug_exception.type,
+                req->u.debug_exception.insn_length);
+        break;
+
+    case VM_EVENT_REASON_CPUID:
+        xtf_log(lvl, "CPUID executed: rip=%016"PRIx64", vcpu %d. Insn length: 
%"PRIu32" " \
+                "0x%"PRIx32" 0x%"PRIx32": EAX=0x%"PRIx64" EBX=0x%"PRIx64" 
ECX=0x%"PRIx64" EDX=0x%"PRIx64"\n",
+                req->data.regs.x86.rip,
+                req->vcpu_id,
+                req->u.cpuid.insn_length,
+                req->u.cpuid.leaf,
+                req->u.cpuid.subleaf,
+                req->data.regs.x86.rax,
+                req->data.regs.x86.rbx,
+                req->data.regs.x86.rcx,
+                req->data.regs.x86.rdx);
+        break;
+
+    case VM_EVENT_REASON_DESCRIPTOR_ACCESS:
+        xtf_log(lvl, "Descriptor access: rip=%016"PRIx64", vcpu %d: "\
+                "VMExit info=0x%"PRIx32", descriptor=%d, is write=%d\n",
+                req->data.regs.x86.rip,
+                req->vcpu_id,
+                req->u.desc_access.arch.vmx.instr_info,
+                req->u.desc_access.descriptor,
+                req->u.desc_access.is_write);
+        break;
+
+    case VM_EVENT_REASON_WRITE_CTRLREG:
+        xtf_log(lvl,"Control register written: rip=%016"PRIx64", vcpu %d: "
+                "reg=%s, old_value=%016"PRIx64", new_value=%016"PRIx64"\n",
+                req->data.regs.x86.rip,
+                req->vcpu_id,
+                get_x86_ctrl_reg_name(req->u.write_ctrlreg.index),
+                req->u.write_ctrlreg.old_value,
+                req->u.write_ctrlreg.new_value);
+        break;
+
+    case VM_EVENT_REASON_EMUL_UNIMPLEMENTED:
+        xtf_log(lvl, "Emulation unimplemented: rip=%016lx, vcpu %d:\n",
+            req->data.regs.x86.rip,
+            req->vcpu_id);
+        break;
+    }
+}
+
+static void xtf_vm_event_ring_get_request(xtf_vm_event_ring_t *evt, 
vm_event_request_t *req)
+{
+    vm_event_back_ring_t *back_ring;
+    RING_IDX req_cons;
+
+    back_ring = &evt->back_ring;
+    req_cons = back_ring->req_cons;
+
+    /* Copy request */
+    memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req));
+    req_cons++;
+
+    /* Update ring */
+    back_ring->req_cons = req_cons;
+    back_ring->sring->req_event = req_cons + 1;
+}
+
+static void xtf_vm_event_ring_put_response(xtf_vm_event_ring_t *evt, 
vm_event_response_t *rsp)
+{
+    vm_event_back_ring_t *back_ring;
+    RING_IDX rsp_prod;
+
+    back_ring = &evt->back_ring;
+    rsp_prod = back_ring->rsp_prod_pvt;
+
+    /* Copy response */
+    memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp));
+    rsp_prod++;
+
+    /* Update ring */
+    back_ring->rsp_prod_pvt = rsp_prod;
+    RING_PUSH_RESPONSES(back_ring);
+}
+
+static int xtf_monitor_loop()
+{
+    vm_event_request_t req;
+    vm_event_response_t rsp;
+    int rc;
+
+    /*
+     * NOTE: The test harness waits for this message to unpause
+     * the monitored DOMU.
+     */
+    printf("Monitor initialization complete.\n");
+
+    for (;;)
+    {
+        rc = xtf_monitor_wait_for_event(100);
+        if ( rc < -1 )
+        {
+            XTF_MON_ERROR("Error getting event");
+            return rc;
+        }
+        else if ( rc == 1 )
+        {
+            XTF_MON_INFO("Domain %d exited\n", monitor->domain_id);
+            return 0;
+        }
+
+        while ( RING_HAS_UNCONSUMED_REQUESTS(&monitor->ring.back_ring) )
+        {
+            xtf_vm_event_ring_get_request(&monitor->ring, &req);
+
+            if ( req.version != VM_EVENT_INTERFACE_VERSION )
+            {
+                XTF_MON_ERROR("Error: vm_event interface version mismatch!\n");
+                return -1;
+            }
+
+            memset( &rsp, 0, sizeof (rsp) );
+            rsp.version = VM_EVENT_INTERFACE_VERSION;
+            rsp.vcpu_id = req.vcpu_id;
+            rsp.flags = (req.flags & VM_EVENT_FLAG_VCPU_PAUSED);
+            rsp.reason = req.reason;
+
+            rc = 0;
+
+            xtf_monitor_dump_request(XTF_MON_LOG_LEVEL_DEBUG, &req);
+
+            if ( req.reason >= VM_EVENT_REASON_MAX || 
!monitor->handlers[req.reason] )
+                XTF_MON_ERROR("Unhandled request: reason = %d\n", req.reason);
+            else
+            {
+                rc = monitor->handlers[req.reason](&req, &rsp);
+                if (rc)
+                    return rc;
+
+                /* Put the response on the ring */
+                xtf_vm_event_ring_put_response(&monitor->ring, &rsp);
+            }
+        }
+        /* Tell Xen page is ready */
+        rc = xenevtchn_notify(monitor->ring.xce_handle, 
monitor->ring.local_port);
+        if ( rc )
+        {
+            XTF_MON_ERROR("Error resuming page");
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int main(int argc, char* argv[])
+{
+    int rc;
+
+    monitor->status = XTF_MON_STATUS_RUNNING;
+    monitor->log_lvl = XTF_MON_LOG_LEVEL_ERROR;
+
+    /* test specific setup sequence */
+    rc = call_helper(monitor->ops.setup, argc, argv);
+    if ( rc )
+    {
+        monitor->status = XTF_MON_STATUS_ERROR;
+        goto e_exit;
+    }
+
+    monitor->xch = xc_interface_open(NULL, NULL, 0);
+    if ( !monitor->xch )
+    {
+        XTF_MON_FATAL("Error initialising xenaccess\n");
+        rc = -EINVAL;
+        monitor->status = XTF_MON_STATUS_ERROR;
+        goto e_exit;
+    }
+
+    monitor->xsh = xs_open(XS_OPEN_READONLY);
+    if ( !monitor->xsh )
+    {
+        XTF_MON_FATAL("Error opening XEN store\n");
+        rc = -EINVAL;
+        monitor->status = XTF_MON_STATUS_ERROR;
+        goto cleanup;
+    }
+
+    if ( !xs_watch( monitor->xsh, "@releaseDomain", "RELEASE_TOKEN") )
+    {
+        XTF_MON_FATAL("Error monitoring releaseDomain\n");
+        rc = -EINVAL;
+        monitor->status = XTF_MON_STATUS_ERROR;
+        goto cleanup;
+    }
+
+    /* test specific initialization sequence */
+    rc = xtf_monitor_init();
+    if ( !rc )
+        rc = call_helper(monitor->ops.init);
+    if ( rc )
+    {
+        monitor->status = XTF_MON_STATUS_ERROR;
+        goto cleanup;
+    }
+
+    /* Run test */
+    rc = xtf_monitor_loop();
+    if ( rc )
+    {
+        XTF_MON_ERROR("Error running test\n");
+        monitor->status = XTF_MON_STATUS_ERROR;
+    }
+    else
+        monitor->status = call_helper(monitor->ops.get_result);
+
+cleanup:
+    /* test specific cleanup sequence */
+    call_helper(monitor->ops.cleanup);
+    xtf_monitor_cleanup();
+    if ( monitor->xsh )
+    {
+        xs_unwatch(monitor->xsh, "@releaseDomain", "RELEASE_TOKEN");
+        xs_close(monitor->xsh);
+        monitor->xsh = NULL;
+    }
+
+    xc_interface_close(monitor->xch);
+
+e_exit:
+    xtf_print_status(monitor->status);
+    return rc;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xtf/__init__.py b/xtf/__init__.py
index 07c269a..e405013 100644
--- a/xtf/__init__.py
+++ b/xtf/__init__.py
@@ -3,7 +3,7 @@
 
 # All test categories
 default_categories     = set(("functional", "xsa"))
-non_default_categories = set(("special", "utility", "in-development", "host"))
+non_default_categories = set(("special", "utility", "in-development", "host", 
"monitor"))
 all_categories         = default_categories | non_default_categories
 
 # All test environments
diff --git a/xtf/monitor_test.py b/xtf/monitor_test.py
new file mode 100644
index 0000000..b9b010e
--- /dev/null
+++ b/xtf/monitor_test.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+""" Monitor test classes.
+
+    The monitor test spawns an test monitor (event channel handler application)
+    instance and runs a DomU image which interacts with it.
+"""
+
+import os
+from   subprocess import Popen
+
+from   xtf.exceptions import RunnerError
+from   xtf.domu_test import DomuTestInstance, DomuTestInfo
+from   xtf.executable_test import ExecutableTestInstance
+from   xtf.logger import Logger
+from   xtf.test import TestResult, TestInstance
+from   xtf.utils import XTFAsyncCall
+
+class MonitorTestInstance(TestInstance):
+    """Monitor test instance"""
+
+    def __init__(self, env, name, variation, monitor_args):
+        super(MonitorTestInstance, self).__init__(name)
+
+        self.env, self.variation = env, variation
+
+        if self.env is None:
+            raise RunnerError("No environment for '%s'" % (self.name, ))
+
+        self.monitor_args = monitor_args.replace("@@VM_PATH@@", self.vm_path())
+
+        self.domu_test = None
+        self.monitor_test = None
+
+    def vm_name(self):
+        """ Return the VM name as `xl` expects it. """
+        return repr(self)
+
+    def cfg_path(self):
+        """ Return the path to the `xl` config file for this test. """
+        return os.path.join("tests", self.name, repr(self) + ".cfg")
+
+    def __repr__(self):
+        if self.variation:
+            return "test-%s-%s~%s" % (self.env, self.name, self.variation)
+        return "test-%s-%s" % (self.env, self.name)
+
+    def vm_path(self):
+        """ Return the VM path. """
+        return os.path.join("tests", self.name, repr(self))
+
+    def monitor_path(self):
+        """ Return the path to the test's monitor app if applicable. """
+        return os.path.join("tests", self.name, "test-monitor-" + self.name)
+
+    def start_monitor(self, dom_id):
+        """ Starts the monitor application. """
+        cmd = [" ".join([self.monitor_path(), self.monitor_args, str(dom_id)])]
+        Logger().log("Executing '%s'" % (" ".join(cmd), ))
+        return Popen(cmd, shell=True)
+
+    def set_up(self, opts, result):
+        self.domu_test = DomuTestInstance(self.env, self.name, self.variation)
+        self.domu_test.set_up(opts, result)
+        if result != TestResult.SUCCESS:
+            return
+
+        monitor_cmd = ' '.join([self.monitor_path(), self.monitor_args,
+                                str(self.domu_test.domu.dom_id)])
+
+        self.monitor_test = ExecutableTestInstance(self.name, '/bin/sh',
+                                                   ['-c', monitor_cmd], "")
+        self.monitor_test.set_up(opts, result)
+        match = self.monitor_test.wait_pattern(['Monitor initialization 
complete.'])
+        if match != 0:
+            result.set(TestResult.CRASH)
+
+    def run(self, result):
+        t1 = XTFAsyncCall(target=self.domu_test.run, args=(result,))
+        t2 = XTFAsyncCall(target=self.monitor_test.wait_pattern,
+                args=(self.result_pattern(), ))
+
+        for th in (t1, t2):
+            th.start()
+
+        t1.join()
+        res = TestResult(t2.join())
+        if res > result:
+            result.set(str(res))
+
+
+    def clean_up(self, result):
+        if self.domu_test:
+            self.domu_test.clean_up(result)
+
+        if self.monitor_test:
+            self.monitor_test.clean_up(result)
+
+class MonitorTestInfo(DomuTestInfo):
+    """Monitor test info"""
+
+    def __init__(self, test_json):
+        super(MonitorTestInfo, self).__init__(test_json)
+        self.instance_class = MonitorTestInstance
+        self.monitor_args = self.extra['monitor_args']
+
+    def all_instances(self, env_filter = None, vary_filter = None):
+        """Return a list of TestInstances, for each supported environment.
+        Optionally filtered by env_filter.  May return an empty list if
+        the filter doesn't match any supported environment.
+        """
+
+        if env_filter:
+            envs = set(env_filter).intersection(self.envs)
+        else:
+            envs = self.envs
+
+        if vary_filter:
+            variations = set(vary_filter).intersection(self.variations)
+        else:
+            variations = self.variations
+
+        res = []
+        if variations:
+            for env in envs:
+                for vary in variations:
+                    res.append(self.instance_class(env, self.name, vary,
+                                                   self.monitor_args))
+        else:
+            res = [ self.instance_class(env, self.name, None, 
self.monitor_args)
+                    for env in envs ]
+        return res
diff --git a/xtf/utils.py b/xtf/utils.py
new file mode 100644
index 0000000..96c570b
--- /dev/null
+++ b/xtf/utils.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+""" XTF utils """
+
+import threading
+
+class XTFAsyncCall(threading.Thread):
+    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
+        super(XTFAsyncCall, self).__init__(group, target, name, args, kwargs)
+        self._return = None
+    def run(self):
+        if self._Thread__target is not None:
+            self._return = self._Thread__target(*self._Thread__args,
+                                                **self._Thread__kwargs)
+    def join(self):
+        threading.Thread.join(self)
+        return self._return
-- 
2.7.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.