]> xenbits.xensource.com Git - people/pauldu/stats.git/commitdiff
Initial commit master
authorPaul Durrant <paul.durrant@citrix.com>
Thu, 1 Dec 2016 13:17:28 +0000 (13:17 +0000)
committerPaul Durrant <paul.durrant@citrix.com>
Fri, 2 Dec 2016 13:42:02 +0000 (13:42 +0000)
Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
Makefile [new file with mode: 0644]
debug.h [new file with mode: 0644]
stats.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..1ac294d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,49 @@
+TARGET = xen-stats
+
+OBJS :=        stats.o
+
+CFLAGS  = -I$(shell pwd)/include
+
+# _GNU_SOURCE for asprintf.
+CFLAGS += -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_GNU_SOURCE 
+CFLAGS += -Wall -Werror -g -O1 
+
+ifeq ($(shell uname),Linux)
+LDLIBS := -lutil -lrt
+endif
+
+LDLIBS += -lxenstore -lxenctrl -lxenevtchn -lxengnttab
+
+# Get gcc to generate the dependencies for us.
+CFLAGS   += -Wp,-MD,$(@D)/.$(@F).d
+
+SUBDIRS  = $(filter-out ./,$(dir $(OBJS) $(LIBS)))
+DEPS     = .*.d
+
+LDFLAGS := -g 
+
+all: $(TARGET)
+
+$(TARGET): $(LIBS) $(OBJS)
+       gcc -o $@ $(LDFLAGS) $(OBJS) $(LIBS) $(LDLIBS)
+
+%.o: %.c
+       gcc -o $@ $(CFLAGS) -c $<
+
+.PHONY: ALWAYS
+
+clean:
+       $(foreach dir,$(SUBDIRS),make -C $(dir) clean)
+       rm -f $(OBJS)
+       rm -f $(DEPS)
+       rm -f $(TARGET)
+       rm -f TAGS
+
+.PHONY: TAGS
+TAGS:
+       find . -name \*.[ch] | etags -
+
+-include $(DEPS)
+
+print-%:
+       echo $($*)
diff --git a/debug.h b/debug.h
new file mode 100644 (file)
index 0000000..816d778
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,40 @@
+/*  
+ * Copyright (c) 2012, Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef  _DEBUG_H
+#define  _DEBUG_H
+
+#define DBG(...)                            \
+    do {                                    \
+        fprintf(stderr, "%s: ", __func__);  \
+        fprintf(stderr, __VA_ARGS__);       \
+        fflush(stderr);                     \
+    } while (FALSE)
+
+#endif  /* _DEBUG_H */
+
diff --git a/stats.c b/stats.c
new file mode 100644 (file)
index 0000000..a902570
--- /dev/null
+++ b/stats.c
@@ -0,0 +1,1175 @@
+/*  
+ * Copyright (c) 2016, Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sched.h>
+#include <assert.h>
+
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+
+#include <locale.h>
+
+#include <xenctrl.h>
+#include <xenstore.h>
+#include <xenevtchn.h>
+#include <xengnttab.h>
+
+#include "xen_stats.h"
+
+#include "debug.h"
+
+#define FALSE 0
+
+enum {
+    STATS_OPT_DOMAIN,
+    STATS_OPT_PROVIDER,
+    STATS_OPT_SET,
+    STATS_OPT_INTERVAL,
+    STATS_NR_OPTS
+    };
+
+static struct option stats_option[] = {
+    {"domain", 1, NULL, 0},
+    {"provider", 1, NULL, 0},
+    {"set", 1, NULL, 0},
+    {"interval", 1, NULL, 0},
+    {NULL, 0, NULL, 0}
+};
+
+static const char *stats_option_text[] = {
+    "<domid>",
+    "<provider>",
+    "<set>",
+    "<interval>",
+    NULL
+};
+
+static const char *prog;
+
+static void
+usage(void)
+{
+    int i;
+
+    fprintf(stderr, "Usage: %s <options>\n\n", prog);
+
+    for (i = 0; i < STATS_NR_OPTS; i++)
+        fprintf(stderr, "\t--%s %s\n",
+                stats_option[i].name,
+                stats_option_text[i]);
+
+    fprintf(stderr, "\n");
+
+    exit(2);
+}
+
+typedef enum {
+    STATS_SEQ_UNINITIALIZED = 0,
+    STATS_SEQ_CTRL_OPEN,
+    STATS_SEQ_STORE_OPEN,
+    STATS_SEQ_EVTCHN_OPEN,
+    STATS_SEQ_GNTTAB_OPEN,
+    STATS_SEQ_SET_FOUND,
+    STATS_SEQ_SET_NOTIFIED,
+    STATS_SEQ_WATCH_CREATED,
+    STATS_SEQ_TIMER_CREATED,
+    STATS_SEQ_INITIALIZED,
+    STATS_NR_SEQS
+} stats_seq_t;
+
+typedef struct stats_state {
+    stats_seq_t         seq;
+    domid_t             domid;
+    xc_interface        *xch;
+    struct xs_handle    *xsh;
+    xenevtchn_handle    *xeh;
+    xengnttab_handle    *xgh;
+    char                *path;
+    char                *monitor_id_key;
+    char                *event_channel_key;
+    char                *event_channel_token;
+    evtchn_port_t       local_port;
+    char                **name;
+    unsigned int        *type;
+    void                *values;
+    unsigned int        nr_stats;
+    unsigned int        nr_pages;
+    bool                bound;
+    timer_t             tid;
+    int                 tfd;
+} stats_state_t;
+
+static stats_state_t stats_state;
+
+static void
+stats_seq_next(stats_seq_t seq)
+{
+    assert(stats_state.seq < STATS_SEQ_INITIALIZED);
+
+    stats_state.seq++;
+    assert(stats_state.seq == seq);
+    
+    switch (stats_state.seq) {
+    case STATS_SEQ_CTRL_OPEN:
+        DBG(">CTRL_OPEN\n");
+        break;
+
+    case STATS_SEQ_STORE_OPEN:
+        DBG(">STORE_OPEN\n");
+        break;
+
+    case STATS_SEQ_EVTCHN_OPEN:
+        DBG(">EVTCHN_OPEN\n");
+        break;
+
+    case STATS_SEQ_GNTTAB_OPEN:
+        DBG(">GNTTAB_OPEN\n");
+        break;
+        
+    case STATS_SEQ_SET_FOUND:
+        DBG(">SET_FOUND\n");
+        break;
+        
+    case STATS_SEQ_SET_NOTIFIED:
+        DBG(">SET_NOTIFIED\n");
+        break;
+        
+    case STATS_SEQ_WATCH_CREATED:
+        DBG(">WATCH_CREATED\n");
+        break;
+        
+    case STATS_SEQ_TIMER_CREATED:
+        DBG(">TIMER_CREATED\n");
+        break;
+        
+    case STATS_SEQ_INITIALIZED:
+        DBG(">INITIALIZED\n");
+        break;
+
+    default:
+        assert(FALSE);
+        break;
+    }
+}
+
+static int
+stats_set_monitor_id(void)
+{
+    char *domid_str;
+    domid_t domid;
+    char *key;
+    char *value;
+    int rc;
+
+    domid_str = xs_read(stats_state.xsh, XBT_NULL,
+                        "domid", NULL);
+
+    if (domid_str == NULL)
+        goto fail1;
+
+    domid = (domid_t)strtol(domid_str, NULL, 0);
+
+    rc = asprintf(&key, "%s/monitor-id", stats_state.path);
+    if (rc < 0)
+        goto fail2;
+    
+    rc = asprintf(&value, "%u", domid);
+    if (rc < 0)
+        goto fail3;
+
+    DBG("%s -> %s\n", key, value);
+
+    if (!xs_write(stats_state.xsh, XBT_NULL, key, value, strlen(value)))
+        goto fail4;
+
+    free(value);
+
+    stats_state.monitor_id_key = key;
+
+    free(domid_str);
+
+    return 0;
+
+fail4:
+    DBG("fail4\n");
+
+    free(value);
+
+fail3:
+    DBG("fail3\n");
+
+    free(key);
+
+fail2:
+    DBG("fail2\n");
+
+    free(domid_str);
+
+fail1:
+    DBG("fail1\n");
+
+    return -1;
+}
+
+static void
+stats_clear_monitor_id(void)
+{
+    (void) xs_rm(stats_state.xsh, XBT_NULL, stats_state.monitor_id_key);
+
+    free(stats_state.monitor_id_key);
+}
+
+static int
+stats_create_watch(void)
+{
+    char *key;
+    char *token;
+    int rc;
+    
+    rc = asprintf(&key, "%s/event-channel", stats_state.path);
+    if (rc < 0)
+        goto fail1;
+
+    DBG("%s\n", key);
+    
+    rc = asprintf(&token, "%u:event-channel", getpid());
+    if (rc < 0)
+        goto fail2;
+
+    if (!xs_watch(stats_state.xsh, key, token))
+        goto fail3;
+
+    stats_state.event_channel_key = key;
+    stats_state.event_channel_token = token;
+
+    return 0;
+
+fail3:
+    DBG("fail3\n");
+
+    free(token);
+
+fail2:
+    DBG("fail2\n");
+
+    free(key);
+
+fail1:
+    DBG("fail1\n");
+
+    return -1;
+}
+
+static void
+stats_destroy_watch(void)
+{
+    (void) xs_unwatch(stats_state.xsh, stats_state.event_channel_key,
+                      stats_state.event_channel_token);
+
+    free(stats_state.event_channel_key);
+    free(stats_state.event_channel_token);
+}
+
+static int
+stats_parse_name_types(void *name_types, unsigned int nr_pages)
+{
+    struct xen_stats_name_type *name_type = name_types;
+    unsigned int max_nr_stats;
+    unsigned int i, nr_stats;
+    
+    max_nr_stats = (nr_pages * XC_PAGE_SIZE) /
+        sizeof(struct xen_stats_name_type);
+    
+    i = 0;
+    while (i < max_nr_stats) {
+        if (name_type[i].type == XEN_STATS_TYPE_INVALID)
+            break;
+        
+        i++;
+    }
+    nr_stats = i;
+
+    if (nr_stats == 0)
+        goto fail1;
+    
+    stats_state.name = calloc(nr_stats, sizeof (char *));
+    if (stats_state.name == NULL)
+        goto fail2;
+
+    stats_state.type = calloc(nr_stats, sizeof (unsigned int));
+    if (stats_state.type == NULL)
+        goto fail3;
+
+    for (i = 0; i < nr_stats; i++) {
+        int rc;
+        
+        rc = asprintf(&stats_state.name[i], "%s", name_type[i].name);
+        if (rc < 0)
+            goto fail4;
+        
+        stats_state.type[i] = name_type[i].type;
+    }
+
+    stats_state.nr_stats = nr_stats;
+    
+    return 0;
+
+fail4:
+    DBG("fail4\n");
+
+    while (i-- != 0)
+        free(stats_state.name[i]);
+
+    free(stats_state.type);
+    
+fail3:
+    DBG("fail3\n");
+
+    free(stats_state.name);
+    
+fail2:
+    DBG("fail2\n");
+
+fail1:
+    DBG("fail1\n");
+
+    return -1;
+}
+
+static void
+stats_free_name_types(void)
+{
+    unsigned int i = stats_state.nr_stats;
+    
+    while (i-- != 0)
+        free(stats_state.name[i]);
+
+    free(stats_state.type);
+    free(stats_state.name);
+}
+
+#define MAX_NR_PAGES 8
+
+static int
+stats_bind(unsigned int port)
+{
+    uint32_t name_ref[MAX_NR_PAGES];
+    uint32_t val_ref[MAX_NR_PAGES];
+    void *name_types;
+    unsigned int i, nr_pages;
+    int rc;
+   
+    rc = xenevtchn_bind_interdomain(stats_state.xeh, stats_state.domid,
+                                    port);
+    if (rc < 0)
+        goto fail1;
+
+    stats_state.local_port = rc;
+
+    DBG("(%u:%u) -> %u\n", stats_state.domid, port, stats_state.local_port);
+    
+    i = 0;
+    while (i < MAX_NR_PAGES) {
+        char *key;
+        char *ref_str;
+        uint32_t ref;
+
+        rc = asprintf(&key, "%s/name-ref%u", stats_state.path, i);
+        if (rc < 0)
+            goto fail2;
+
+        ref_str = xs_read(stats_state.xsh, XBT_NULL, key, NULL);
+        free(key);
+        
+        if (ref_str == NULL)
+            break;
+
+        ref = strtol(ref_str, NULL, 0);
+        free(ref_str);
+
+        DBG("name-ref%u = %u\n", i, ref);
+        
+        name_ref[i++] = ref;
+    }
+    nr_pages = i;
+
+    i = 0;
+    while (i < nr_pages) {
+        char *key;
+        char *ref_str;
+        uint32_t ref;
+
+        rc = asprintf(&key, "%s/val-ref%u", stats_state.path, i);
+        if (rc < 0)
+            goto fail3;
+
+        ref_str = xs_read(stats_state.xsh, XBT_NULL, key, NULL);
+        free(key);
+        
+        if (ref_str == NULL)
+            break;
+
+        ref = strtol(ref_str, NULL, 0);
+        free(ref_str);
+
+        DBG("val-ref%u = %u\n", i, ref);
+
+        val_ref[i++] = ref;
+    }
+    assert(i == nr_pages);
+
+    DBG("%u page(s)\n", nr_pages);
+        
+    name_types = xengnttab_map_domain_grant_refs(stats_state.xgh, nr_pages,
+                                                 stats_state.domid,
+                                                 name_ref,
+                                                 PROT_READ);
+    if (name_types == NULL)
+        goto fail4;
+
+    rc = stats_parse_name_types(name_types, nr_pages);
+    if (rc < 0)
+        goto fail5;
+    
+    stats_state.values = xengnttab_map_domain_grant_refs(stats_state.xgh,
+                                                         nr_pages,
+                                                         stats_state.domid,
+                                                         val_ref,
+                                                         PROT_READ);
+    if (stats_state.values == NULL)
+        goto fail6;
+
+    (void) xengnttab_unmap(stats_state.xgh, name_types, nr_pages);
+    
+    stats_state.nr_pages = nr_pages;
+    stats_state.bound = true;
+    
+    return 0;
+
+fail6:
+    DBG("fail6\n");
+
+    stats_free_name_types();
+
+fail5:
+    DBG("fail5\n");
+
+    (void) xengnttab_unmap(stats_state.xgh, name_types, nr_pages);
+    
+fail4:
+    DBG("fail4\n");
+
+fail3:
+    DBG("fail3\n");
+
+fail2:
+    DBG("fail2\n");
+
+    (void) xenevtchn_unbind(stats_state.xeh, stats_state.local_port);
+    
+fail1:
+    DBG("fail1\n");
+
+    return -1;
+}
+
+static void
+stats_unbind(void)
+{
+    unsigned int nr_pages;
+    
+    nr_pages = stats_state.nr_pages;
+    
+    (void) xengnttab_unmap(stats_state.xgh, stats_state.values, nr_pages);
+    
+    stats_free_name_types();
+    
+    (void) xenevtchn_unbind(stats_state.xeh, stats_state.local_port);
+    stats_state.bound = false;
+}
+
+static int
+stats_create_timer(void)
+{
+    int pfd[2];
+    int flags;
+    timer_t tid;
+    struct sigevent sigev;
+
+    memset(pfd, 0, sizeof (pfd));
+    if (pipe(pfd) < 0)
+        goto fail1;
+
+    flags = fcntl(pfd[0], F_GETFL);
+    fcntl(pfd[0], F_SETFL, flags | O_NONBLOCK);
+
+    flags = fcntl(pfd[1], F_GETFL);
+    fcntl(pfd[1], F_SETFL, flags | O_NONBLOCK);
+
+    sigev.sigev_notify = SIGEV_SIGNAL;
+    sigev.sigev_signo = SIGRTMIN;
+    sigev.sigev_value.sival_int = pfd[1];
+
+    if (timer_create(CLOCK_MONOTONIC, &sigev, &tid) < 0)
+        goto fail2;
+
+    stats_state.tid = tid;
+    stats_state.tfd = pfd[0];
+
+    return 0;
+
+fail2:
+    DBG("fail2\n");
+
+    close(pfd[1]);
+    close(pfd[0]);
+
+fail1:
+    DBG("fail1\n");
+
+    return -1;
+}
+
+static void
+stats_set_timer(unsigned int s)
+{
+    struct itimerspec it;
+    
+    it.it_interval.tv_sec = it.it_value.tv_sec = s;
+    it.it_interval.tv_nsec = it.it_value.tv_nsec = 0;
+
+    (void) timer_settime(stats_state.tid, 0, &it, NULL);
+}
+
+static void
+stats_destroy_timer(void)
+{
+    timer_delete(stats_state.tid);
+    close(stats_state.tfd);
+}
+
+static void
+stats_teardown(void)
+{
+    if (stats_state.seq == STATS_SEQ_INITIALIZED) {
+        DBG("<INITIALIZED\n");
+
+        if (stats_state.bound)
+            stats_unbind();
+
+        stats_state.seq = STATS_SEQ_TIMER_CREATED;
+    }
+
+    if (stats_state.seq == STATS_SEQ_TIMER_CREATED) {
+        DBG("<TIMER_CREATED\n");
+
+        stats_destroy_timer();
+        
+        stats_state.seq = STATS_SEQ_WATCH_CREATED;
+    }
+
+    if (stats_state.seq == STATS_SEQ_WATCH_CREATED) {
+        DBG("<WATCH_CREATED\n");
+
+        stats_destroy_watch();
+        
+        stats_state.seq = STATS_SEQ_SET_NOTIFIED;
+    }
+
+    if (stats_state.seq == STATS_SEQ_SET_NOTIFIED) {
+        DBG("<SET_NOTIFIED\n");
+
+        stats_clear_monitor_id();
+        
+        stats_state.seq = STATS_SEQ_SET_FOUND;
+    }
+
+    if (stats_state.seq == STATS_SEQ_SET_FOUND) {
+        DBG("<SET_FOUND\n");
+
+        free(stats_state.path);
+        
+        stats_state.seq = STATS_SEQ_GNTTAB_OPEN;
+    }
+
+    if (stats_state.seq == STATS_SEQ_GNTTAB_OPEN) {
+        DBG("<GNTTAB_OPEN\n");
+
+        (void) xengnttab_close(stats_state.xgh);
+        
+        stats_state.seq = STATS_SEQ_EVTCHN_OPEN;
+    }
+
+    if (stats_state.seq == STATS_SEQ_EVTCHN_OPEN) {
+        DBG("<EVTCHN_OPEN\n");
+
+        (void) xenevtchn_close(stats_state.xeh);
+        
+        stats_state.seq = STATS_SEQ_STORE_OPEN;
+    }
+
+    if (stats_state.seq == STATS_SEQ_STORE_OPEN) {
+        DBG("<GNTTAB_OPEN\n");
+
+        xs_close(stats_state.xsh);
+        
+        stats_state.seq = STATS_SEQ_CTRL_OPEN;
+    }
+    
+    if (stats_state.seq >= STATS_SEQ_CTRL_OPEN) {
+        DBG("<CTRL_OPEN\n");
+
+        (void) xc_interface_close(stats_state.xch);
+
+        stats_state.seq = STATS_SEQ_UNINITIALIZED;
+    }
+}
+
+static struct sigaction sigabrt_handler;
+
+static void
+stats_sigabrt(int num)
+{
+    DBG("%s\n", strsignal(num));
+
+    sigaction(SIGABRT, &sigabrt_handler, NULL);
+}
+
+static struct sigaction sigterm_handler;
+
+static void
+stats_sigterm(int num)
+{
+    DBG("%s\n", strsignal(num));
+
+    stats_teardown();
+
+    exit(0);
+}
+
+static struct sigaction sigusr1_handler;
+
+static void
+stats_sigusr1(int num)
+{
+    DBG("%s\n", strsignal(num));
+
+    sigaction(SIGHUP, &sigusr1_handler, NULL);
+}
+
+static struct sigaction sigrt_handler;
+
+static void
+stats_sigrt(int num, siginfo_t *si, void *arg)
+{
+    int     tfd;
+    char    buf = 'T';
+
+    sigaction(SIGRTMIN, &sigrt_handler, NULL);
+
+    tfd = (int)si->si_value.sival_int;
+    write(tfd, &buf, 1);
+}
+
+static char *
+stats_find_set(char *provider, char *set)
+{
+    char *path = NULL;
+    char **list = NULL;
+    unsigned int i, n;
+    bool found;
+    int rc;
+    
+    rc = asprintf(&path, "/local/domain/%u/stats", stats_state.domid);
+    if (rc < 0)
+        goto fail1;
+
+    list = xs_directory(stats_state.xsh, XBT_NULL, path, &n);
+    if (list == NULL)
+        goto fail2;
+
+    free(path);
+    path = NULL;
+
+    found = false;
+    for (i = 0; i < n; i++) {
+        if (strcmp(provider, list[i]) == 0) {
+            found = true;
+            break;
+        }
+    }
+
+    if (!found)
+        goto fail3;
+
+    free(list);
+    list = NULL;
+    
+    rc = asprintf(&path, "/local/domain/%u/stats/%s", stats_state.domid,
+                  provider);
+    if (rc < 0)
+        goto fail4;
+    
+    list = xs_directory(stats_state.xsh, XBT_NULL, path, &n);
+    if (list == NULL)
+        goto fail5;
+
+    free(path);
+    path = NULL;
+
+    found = false;
+    for (i = 0; i < n; i++) {
+        if (strcmp(set, list[i]) == 0) {
+            found = true;
+            break;
+        }
+    }
+
+    if (!found)
+        goto fail6;
+
+    free(list);
+    list = NULL;
+    
+    rc = asprintf(&path, "/local/domain/%u/stats/%s/%s", stats_state.domid,
+                  provider, set);
+    if (rc < 0)
+        goto fail7;
+
+    return path;
+    
+fail7:
+    DBG("fail7\n");
+
+fail6:
+    DBG("fail6\n");
+
+    free(list);
+    list = NULL;
+
+fail5:
+    DBG("fail5\n");
+
+    free(path);
+    path = NULL;
+
+fail4:
+    DBG("fail4\n");
+
+fail3:
+    DBG("fail3\n");
+
+    if (list != NULL) {
+        free(list);
+        list = NULL;
+    }
+
+fail2:
+    DBG("fail2\n");
+
+    if (list != NULL) {
+        free(path);
+        path = NULL;
+    }
+
+fail1:
+    DBG("fail1\n");
+
+    return NULL;
+}
+
+static int
+stats_initialize(domid_t domid, char *provider, char *set)
+{
+    int             rc;
+    xc_dominfo_t    dominfo;
+
+    stats_state.domid = domid;
+
+    stats_state.xch = xc_interface_open(NULL, NULL, 0);
+    if (stats_state.xch == NULL)
+        goto fail1;
+
+    stats_seq_next(STATS_SEQ_CTRL_OPEN);
+
+    rc = xc_domain_getinfo(stats_state.xch, stats_state.domid, 1, &dominfo);
+    if (rc < 0 || dominfo.domid != stats_state.domid)
+        goto fail2;
+
+    stats_state.xsh = xs_open(0);
+    if (stats_state.xsh == NULL)
+        goto fail3;
+
+    stats_seq_next(STATS_SEQ_STORE_OPEN);
+
+    stats_state.xeh = xenevtchn_open(NULL, 0);
+
+    if (stats_state.xeh == NULL)
+        goto fail4;
+
+    stats_seq_next(STATS_SEQ_EVTCHN_OPEN);
+
+    stats_state.xgh = xengnttab_open(NULL, 0);
+
+    if (stats_state.xgh == NULL)
+        goto fail5;
+
+    stats_seq_next(STATS_SEQ_GNTTAB_OPEN);
+
+    stats_state.path = stats_find_set(provider, set);
+
+    if (stats_state.path == NULL)
+        goto fail6;
+    
+    stats_seq_next(STATS_SEQ_SET_FOUND);
+
+    rc = stats_set_monitor_id();
+    if (rc < 0)
+        goto fail7;
+
+    stats_seq_next(STATS_SEQ_SET_NOTIFIED);
+
+    rc = stats_create_watch();
+    if (rc < 0)
+        goto fail8;
+
+    stats_seq_next(STATS_SEQ_WATCH_CREATED);
+
+    rc = stats_create_timer();
+    if (rc < 0)
+        goto fail9;
+
+    stats_seq_next(STATS_SEQ_TIMER_CREATED);
+
+    stats_seq_next(STATS_SEQ_INITIALIZED);
+
+    return 0;
+    
+fail9:
+    DBG("fail9\n");
+
+fail8:
+    DBG("fail8\n");
+
+fail7:
+    DBG("fail7\n");
+
+fail6:
+    DBG("fail6\n");
+
+fail5:
+    DBG("fail5\n");
+
+fail4:
+    DBG("fail4\n");
+
+fail3:
+    DBG("fail3\n");
+
+fail2:
+    DBG("fail2\n");
+
+fail1:
+    DBG("fail1\n");
+
+    warn("fail");
+    return -1;
+}
+
+static void
+stats_dump(void)
+{
+    struct xen_stats_value *value = stats_state.values;
+    unsigned int i;
+
+    for (i = 0; i < stats_state.nr_stats; i++) {
+        switch (stats_state.type[i]) {
+        case XEN_STATS_TYPE_S64:
+            printf("%s = %ld\n", stats_state.name[i],
+                   *(long *)value[i].buffer);
+            break;
+            
+        case XEN_STATS_TYPE_U64:
+            printf("%s = %lu\n", stats_state.name[i],
+                   *(unsigned long *)value[i].buffer);
+            break;
+            
+        case XEN_STATS_TYPE_DOUBLE:
+            printf("%s = %f\n", stats_state.name[i],
+                   *(double *)value[i].buffer);
+            break;
+            
+        case XEN_STATS_TYPE_ASCII:
+            printf("%s = %s\n", stats_state.name[i],
+                   value[i].buffer);
+            break;
+
+        default:
+            break;
+        }
+    }
+}
+
+static void
+stats_event(void)
+{
+    evtchn_port_t port;
+
+    port = xenevtchn_pending(stats_state.xeh);
+    if (port != stats_state.local_port)
+        return;
+
+    stats_dump();
+    
+    xenevtchn_unmask(stats_state.xeh, port);
+}
+
+static void
+stats_watch(void)
+{
+    char **vec;
+    unsigned int n;
+    
+    vec = xs_read_watch(stats_state.xsh, &n);
+    if (vec == NULL)
+        return;
+
+    assert(n == 2);
+
+    if (strcmp(vec[XS_WATCH_TOKEN], stats_state.event_channel_token) == 0) {
+        char *port_str;
+        
+        port_str = xs_read(stats_state.xsh, XBT_NULL,
+                           stats_state.event_channel_key, NULL);
+        if (port_str != NULL && !stats_state.bound) {
+            evtchn_port_t port;
+
+            port = (evtchn_port_t)strtol(port_str, NULL, 0);
+            
+            (void) stats_bind(port);
+        } else if (port_str == NULL && stats_state.bound) {
+            stats_unbind();
+        }
+    }
+
+    free(vec);
+}
+
+static void
+stats_tick()
+{
+    if (stats_state.bound)
+        xenevtchn_notify(stats_state.xeh, stats_state.local_port);
+}
+
+int
+main(int argc, char **argv, char **envp)
+{
+    char            *domain_str;
+    char            *provider_str;
+    char            *set_str;
+    char            *interval_str;
+    int             index;
+    domid_t         domid;
+    unsigned int    interval;
+    sigset_t        block;
+    sigset_t        unblock;
+    struct pollfd   pfd[3];
+    int             rc;
+
+    prog = basename(argv[0]);
+
+    domain_str = NULL;
+    provider_str = NULL;
+    set_str = NULL;
+    interval_str = NULL;
+
+    for (;;) {
+        char    c;
+
+        c = getopt_long(argc, argv, "", stats_option, &index);
+        if (c == -1)
+            break;
+
+        if (c != 0) {
+            usage();
+            /*NOTREACHED*/
+        }
+
+        DBG("--%s = '%s'\n", stats_option[index].name, optarg);
+
+        switch (index) {
+        case STATS_OPT_DOMAIN:
+            domain_str = optarg;
+            break;
+
+        case STATS_OPT_PROVIDER:
+            provider_str = optarg;
+            break;
+
+        case STATS_OPT_SET:
+            set_str = optarg;
+            break;
+
+        case STATS_OPT_INTERVAL:
+            interval_str = optarg;
+            break;
+
+        default:
+            assert(FALSE);
+            break;
+        }
+    }
+
+    if (domain_str == NULL ||
+        provider_str == NULL ||
+        set_str == NULL ||
+        interval_str == NULL) {
+        usage();
+        /*NOTREACHED*/
+    }
+
+    domid = (domid_t)strtol(domain_str, NULL, 0);
+    interval = (unsigned int)strtol(interval_str, NULL, 0);
+
+    sigfillset(&block);
+
+    memset(&sigterm_handler, 0, sizeof (struct sigaction));
+    sigterm_handler.sa_handler = stats_sigabrt;
+
+    sigaction(SIGABRT, &sigabrt_handler, NULL);
+    sigdelset(&block, SIGABRT);
+
+    sigprocmask(SIG_BLOCK, &block, NULL);
+
+    rc = stats_initialize(domid, provider_str, set_str);
+    if (rc < 0) {
+        stats_teardown();
+        exit(1);
+    }
+
+    sigemptyset(&unblock);
+
+    memset(&sigterm_handler, 0, sizeof (struct sigaction));
+    sigterm_handler.sa_handler = stats_sigterm;
+
+    sigaction(SIGTERM, &sigterm_handler, NULL);
+    sigaddset(&unblock, SIGTERM);
+
+    sigaction(SIGINT, &sigterm_handler, NULL);
+    sigaddset(&unblock, SIGINT);
+
+    sigaction(SIGHUP, &sigterm_handler, NULL);
+    sigaddset(&unblock, SIGHUP);
+
+    memset(&sigusr1_handler, 0, sizeof (struct sigaction));
+    sigusr1_handler.sa_handler = stats_sigusr1;
+
+    sigaction(SIGUSR1, &sigusr1_handler, NULL);
+    sigaddset(&unblock, SIGUSR1);
+
+    memset(&sigrt_handler, 0, sizeof (struct sigaction));
+    sigrt_handler.sa_flags = SA_SIGINFO;
+    sigrt_handler.sa_sigaction = stats_sigrt;
+
+    sigaction(SIGRTMIN, &sigrt_handler, NULL);
+    sigaddset(&unblock, SIGRTMIN);
+    
+    sigprocmask(SIG_UNBLOCK, &unblock, NULL);
+
+    pfd[0].fd = xenevtchn_fd(stats_state.xeh);
+    pfd[0].events = POLLIN | POLLERR | POLLHUP;
+    pfd[0].revents = 0;
+
+    pfd[1].fd = xs_fileno(stats_state.xsh);
+    pfd[1].events = POLLIN | POLLERR | POLLHUP;
+    pfd[1].revents = 0;
+
+    pfd[2].fd = stats_state.tfd;
+    pfd[2].events = POLLIN | POLLERR | POLLHUP;
+    pfd[2].revents = 0;
+
+    stats_set_timer(interval);
+    
+    for (;;) {
+        rc = poll(pfd, 3, 5000);
+
+        if (rc > 0) {
+            if (pfd[0].revents & POLLIN)
+                stats_event();
+            
+            if (pfd[1].revents & POLLIN)
+                stats_watch();
+            
+            if (pfd[2].revents & POLLIN) {
+                char buf;
+                
+                read(stats_state.tfd, &buf, 1);               
+                stats_tick();
+                stats_set_timer(interval);
+            }
+        } else if (rc < 0 && errno != EINTR) {
+            DBG("%s\n", strerror(errno));
+            break;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * c-tab-always-indent: nil
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */