[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 *)¶m; \ +} + +#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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |