diff --git a/.gitignore b/.gitignore
index 2d4d4013a6ebbb98b22dbf7eb06a5b2d5eb9f086..90fee91cc4fd585c4f9c7b2f9998f5b3d21151d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,6 +75,7 @@
/examples/dominfo/info1
/examples/domsuspend/suspend
/examples/dommigrate/dommigrate
+/examples/domtop/domtop
/examples/hellolibvirt/hellolibvirt
/examples/openauth/openauth
/gnulib/lib/*
diff --git a/Makefile.am b/Makefile.am
index a374e1a15631a543e923acb7897b8584c24c1d98..4aafe946cbc6c1f2d3970954d89cc9948b98932f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,7 +24,7 @@ SUBDIRS = . gnulib/lib include src daemon tools docs gnulib/tests \
examples/dominfo examples/domsuspend examples/apparmor \
examples/xml/nwfilter examples/openauth examples/systemtap \
tools/wireshark examples/dommigrate \
- examples/lxcconvert
+ examples/lxcconvert examples/domtop
ACLOCAL_AMFLAGS = -I m4
diff --git a/cfg.mk b/cfg.mk
index c3d89a019d710a671d3fee5d46c128f33b81f8cc..0559eddbbfffcb03da8dec013abb2307769e2f47 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -1078,7 +1078,7 @@ exclude_file_name_regexp--sc_prohibit_sprintf = \
exclude_file_name_regexp--sc_prohibit_strncpy = ^src/util/virstring\.c$$
exclude_file_name_regexp--sc_prohibit_strtol = \
- ^(src/util/.*|examples/domsuspend/suspend)\.c$$
+ ^(src/util/.*|examples/dom.*/.*)\.c$$
exclude_file_name_regexp--sc_prohibit_xmlGetProp = ^src/util/virxml\.c$$
diff --git a/configure.ac b/configure.ac
index 8001e24b9cc00aa027874e4ffcbf0c1de730e5ca..f37c716ae50353b9fbf22d74c5b681c14ce38ee1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2755,6 +2755,7 @@ AC_CONFIG_FILES([\
examples/domsuspend/Makefile \
examples/dominfo/Makefile \
examples/dommigrate/Makefile \
+ examples/domtop/Makefile \
examples/openauth/Makefile \
examples/hellolibvirt/Makefile \
examples/systemtap/Makefile \
diff --git a/examples/domtop/Makefile.am b/examples/domtop/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..c5cb6c73b4fed180ba286bf3554dec822135b854
--- /dev/null
+++ b/examples/domtop/Makefile.am
@@ -0,0 +1,27 @@
+## Process this file with automake to produce Makefile.in
+
+## Copyright (C) 2014 Red Hat, Inc.
+##
+## This library is free software; you can redistribute it and/or
+## modify it under the terms of the GNU Lesser General Public
+## License as published by the Free Software Foundation; either
+## version 2.1 of the License, or (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public
+## License along with this library. If not, see
+## .
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include
+LDADDS = $(STATIC_BINARIES) $(WARN_CFLAGS) $(top_builddir)/src/libvirt.la \
+ $(COVERAGE_LDFLAGS)
+
+noinst_PROGRAMS=domtop
+
+domtop_SOURCES=domtop.c
+domtop_LDFLAGS=
+domtop_LDADD= $(LDADDS)
diff --git a/examples/domtop/domtop.c b/examples/domtop/domtop.c
new file mode 100644
index 0000000000000000000000000000000000000000..8118793a067256177e8c9c2c79cd884fa7c23f8e
--- /dev/null
+++ b/examples/domtop/domtop.c
@@ -0,0 +1,399 @@
+/*
+ * domtop.c: Demo program showing how to calculate CPU usage
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ *
+ * Author: Michal Privoznik
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static bool debug;
+static bool run_top;
+
+#define ERROR(...) \
+do { \
+ fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+} while (0)
+
+#define DEBUG(...) \
+do { \
+ if (!debug) \
+ break; \
+ fprintf(stderr, "DEBUG %s:%d : ", __FUNCTION__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+} while (0)
+
+#define STREQ(a, b) (strcmp(a, b) == 0)
+
+static void
+print_usage(const char *progname)
+{
+ const char *unified_progname;
+
+ if (!(unified_progname = strrchr(progname, '/')))
+ unified_progname = progname;
+ else
+ unified_progname++;
+
+ printf("\n%s [options] [domain name]\n\n"
+ " options:\n"
+ " -d | --debug enable debug messages\n"
+ " -h | --help print this help\n"
+ " -c | --connect=URI hypervisor connection URI\n"
+ " -D | --delay=X delay between updates in milliseconds "
+ "(default is 500ms)\n"
+ "\n"
+ "Print the cumulative usage of each host CPU.\n"
+ "Without any domain name specified the list of\n"
+ "all running domains is printed out.\n",
+ unified_progname);
+}
+
+static int
+parse_argv(int argc, char *argv[],
+ const char **uri,
+ const char **dom_name,
+ unsigned int *milliseconds)
+{
+ int ret = -1;
+ int arg;
+ unsigned long val;
+ char *p;
+ struct option opt[] = {
+ {"debug", no_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {"connect", required_argument, NULL, 'c'},
+ {"delay", required_argument, NULL, 'D'},
+ {NULL, 0, NULL, 0}
+ };
+
+ while ((arg = getopt_long(argc, argv, "+:dhc:D:", opt, NULL)) != -1) {
+ switch (arg) {
+ case 'd':
+ debug = true;
+ break;
+ case 'h':
+ print_usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'c':
+ *uri = optarg;
+ break;
+ case 'D':
+ /* strtoul man page suggests clearing errno prior to call */
+ errno = 0;
+ val = strtoul(optarg, &p, 10);
+ if (errno || *p || p == optarg) {
+ ERROR("Invalid number: '%s'", optarg);
+ exit(EXIT_FAILURE);
+ }
+ *milliseconds = val;
+ if (*milliseconds != val) {
+ ERROR("Integer overflow: %ld", val);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case ':':
+ ERROR("option '-%c' requires an argument", optopt);
+ exit(EXIT_FAILURE);
+ case '?':
+ if (optopt)
+ ERROR("unsupported option '-%c'. See --help.", optopt);
+ else
+ ERROR("unsupported option '%s'. See --help.", argv[optind - 1]);
+ exit(EXIT_FAILURE);
+ default:
+ ERROR("unknown option");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (argc > optind)
+ *dom_name = argv[optind];
+
+ ret = 0;
+ cleanup:
+ return ret;
+}
+
+static int
+fetch_domains(virConnectPtr conn)
+{
+ int num_domains, ret = -1;
+ virDomainPtr *domains = NULL;
+ size_t i;
+ const int list_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE;
+
+ DEBUG("Fetching list of running domains");
+ num_domains = virConnectListAllDomains(conn, &domains, list_flags);
+
+ DEBUG("num_domains=%d", num_domains);
+ if (num_domains < 0) {
+ ERROR("Unable to fetch list of running domains");
+ goto cleanup;
+ }
+
+ printf("Running domains:\n");
+ printf("----------------\n");
+ for (i = 0; i < num_domains; i++) {
+ virDomainPtr dom = domains[i];
+ const char *dom_name = virDomainGetName(dom);
+ printf("%s\n", dom_name);
+ virDomainFree(dom);
+ }
+
+ ret = 0;
+ cleanup:
+ free(domains);
+ return ret;
+}
+
+static void
+print_cpu_usage(const char *dom_name,
+ size_t cpu,
+ size_t ncpus,
+ unsigned long long then,
+ virTypedParameterPtr then_params,
+ size_t then_nparams,
+ unsigned long long now,
+ virTypedParameterPtr now_params,
+ size_t now_nparams)
+{
+ size_t i, j, k;
+ size_t nparams = now_nparams;
+ bool delim = false;
+
+ if (then_nparams != now_nparams) {
+ /* this should not happen (TM) */
+ ERROR("parameters counts don't match");
+ return;
+ }
+
+ for (i = 0; i < ncpus; i++) {
+ size_t pos;
+ double usage;
+
+ /* check if the vCPU is in the maps */
+ if (now_params[i * nparams].type == 0 ||
+ then_params[i * then_nparams].type == 0)
+ continue;
+
+ for (j = 0; j < nparams; j++) {
+ pos = i * nparams + j;
+ if (STREQ(then_params[pos].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
+ STREQ(then_params[pos].field, VIR_DOMAIN_CPU_STATS_VCPUTIME))
+ break;
+ }
+
+ if (j == nparams) {
+ ERROR("unable to find %s", VIR_DOMAIN_CPU_STATS_CPUTIME);
+ return;
+ }
+
+ DEBUG("now_params=%llu then_params=%llu now=%llu then=%llu",
+ now_params[pos].value.ul, then_params[pos].value.ul, now, then);
+
+ /* @now_params and @then_params are in nanoseconds, @now and @then are
+ * in microseconds. In ideal world, we would translate them both into
+ * the same scale, divide one by another and multiply by factor of 100
+ * to get percentage. However, the count of floating point operations
+ * performed has a bad effect on the precision, so instead of dividing
+ * @now_params and @then_params by 1000 and then multiplying again by
+ * 100, we divide only once by 10 and get the same result. */
+ usage = (now_params[pos].value.ul - then_params[pos].value.ul) /
+ (now - then) / 10;
+
+ if (delim)
+ printf("\t");
+ printf("CPU%zu: %.2lf", cpu + i, usage);
+ delim = true;
+ }
+
+ printf("\n");
+}
+
+static void
+stop(int sig)
+{
+ DEBUG("Exiting on signal %d\n", sig);
+ run_top = false;
+}
+
+static int
+do_top(virConnectPtr conn,
+ const char *dom_name,
+ unsigned int milliseconds)
+{
+ int ret = -1;
+ virDomainPtr dom;
+ int max_id;
+ int nparams = 0, then_nparams = 0, now_nparams = 0;
+ virTypedParameterPtr then_params = NULL, now_params = NULL;
+ struct sigaction action_stop;
+
+ memset(&action_stop, 0, sizeof(action_stop));
+ action_stop.sa_handler = stop;
+
+ /* Lookup the domain */
+ if (!(dom = virDomainLookupByName(conn, dom_name))) {
+ ERROR("Unable to find domain '%s'", dom_name);
+ goto cleanup;
+ }
+
+ /* and see how many vCPUs can we fetch stats for */
+ if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, 0)) < 0) {
+ ERROR("Unable to get cpu stats");
+ goto cleanup;
+ }
+
+ /* how many stats can we get for a vCPU? */
+ if ((nparams = virDomainGetCPUStats(dom, NULL, 0, 0, 1, 0)) < 0) {
+ ERROR("Unable to get cpu stats");
+ goto cleanup;
+ }
+
+ if (!(now_params = calloc(nparams * max_id, sizeof(*now_params))) ||
+ !(then_params = calloc(nparams * max_id, sizeof(*then_params)))) {
+ ERROR("Unable to allocate memory");
+ goto cleanup;
+ }
+
+ sigaction(SIGTERM, &action_stop, NULL);
+ sigaction(SIGINT, &action_stop, NULL);
+
+ run_top = true;
+ while (run_top) {
+ struct timeval then, now;
+
+ /* Get current time */
+ if (gettimeofday(&then, NULL) < 0) {
+ ERROR("unable to get time");
+ goto cleanup;
+ }
+
+ /* And current stats */
+ if ((then_nparams = virDomainGetCPUStats(dom, then_params,
+ nparams, 0, max_id, 0)) < 0) {
+ ERROR("Unable to get cpu stats");
+ goto cleanup;
+ }
+
+ /* Now sleep some time */
+ usleep(milliseconds * 1000); /* usleep expects microseconds */
+
+ /* And get current time */
+ if (gettimeofday(&now, NULL) < 0) {
+ ERROR("unable to get time");
+ goto cleanup;
+ }
+
+ /* And current stats */
+ if ((now_nparams = virDomainGetCPUStats(dom, now_params,
+ nparams, 0, max_id, 0)) < 0) {
+ ERROR("Unable to get cpu stats");
+ goto cleanup;
+ }
+
+ print_cpu_usage(dom_name, 0, max_id,
+ then.tv_sec * 1000000 + then.tv_usec,
+ then_params, then_nparams,
+ now.tv_sec * 1000000 + now.tv_usec,
+ now_params, now_nparams);
+
+ virTypedParamsClear(now_params, now_nparams * max_id);
+ virTypedParamsClear(then_params, then_nparams * max_id);
+ }
+
+ ret = 0;
+ cleanup:
+ if (max_id > 0) {
+ if (now_nparams > 0)
+ virTypedParamsFree(now_params, now_nparams * max_id);
+ if (then_nparams > 0)
+ virTypedParamsFree(then_params, then_nparams * max_id);
+ }
+ if (dom)
+ virDomainFree(dom);
+ return ret;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ret = EXIT_FAILURE;
+ virConnectPtr conn = NULL;
+ const char *uri = NULL;
+ const char *dom_name = NULL;
+ unsigned int milliseconds = 500; /* Sleep this long between two API calls */
+ const int connect_flags = 0; /* No connect flags for now */
+
+ if (parse_argv(argc, argv, &uri, &dom_name, &milliseconds) < 0)
+ goto cleanup;
+
+ DEBUG("Proceeding with uri=%s dom_name=%s milliseconds=%u",
+ uri, dom_name, milliseconds);
+
+ if (!(conn = virConnectOpenAuth(uri,
+ virConnectAuthPtrDefault,
+ connect_flags))) {
+ ERROR("Failed to connect to hypervisor");
+ goto cleanup;
+ }
+
+ DEBUG("Successfully connected");
+
+ if (!dom_name) {
+ if (fetch_domains(conn) == 0)
+ ret = EXIT_SUCCESS;
+ goto cleanup;
+ }
+
+ if (do_top(conn, dom_name, milliseconds) < 0)
+ goto cleanup;
+
+ ret = EXIT_SUCCESS;
+ cleanup:
+ if (conn) {
+ int tmp;
+ tmp = virConnectClose(conn);
+ if (tmp < 0) {
+ ERROR("Failed to disconnect from the hypervisor");
+ ret = EXIT_FAILURE;
+ } else if (tmp > 0) {
+ ERROR("One or more references were leaked after "
+ "disconnect from the hypervisor");
+ ret = EXIT_FAILURE;
+ } else {
+ DEBUG("Connection successfully closed");
+ }
+ }
+ return ret;
+}
diff --git a/libvirt.spec.in b/libvirt.spec.in
index 472fa4bf5079fde0d3d96445133ce78c8fabdab5..76e57aa93441c0d15643a11e74e7ff681555b2e5 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -1505,7 +1505,7 @@ rm -fr %{buildroot}
# on RHEL 5, thus we need to expand it here.
make install DESTDIR=%{?buildroot} SYSTEMD_UNIT_DIR=%{_unitdir}
-for i in object-events dominfo domsuspend hellolibvirt openauth xml/nwfilter systemtap dommigrate
+for i in object-events dominfo domsuspend hellolibvirt openauth xml/nwfilter systemtap dommigrate domtop
do
(cd examples/$i ; make clean ; rm -rf .deps .libs Makefile Makefile.in)
done