examples: Move and install shell examples
[libvirt/ericb.git] / examples / domtop / domtop.c
blobe463e2873f424d69ea7c780aa1d441d270a4c2d3
1 /*
2 * domtop.c: Demo program showing how to calculate CPU usage
4 * Copyright (C) 2014 Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
21 #include <errno.h>
22 #include <getopt.h>
23 #include <libvirt/libvirt.h>
24 #include <libvirt/virterror.h>
25 #include <signal.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/time.h>
31 #include <unistd.h>
32 #include <inttypes.h>
34 static bool debug;
35 static bool run_top;
37 /* On mingw, there's a header file that poisons the well:
40 * CC domtop.o
41 *domtop.c:40:0: warning: "ERROR" redefined [enabled by default]
42 * #define ERROR(...) \
43 * ^
44 *In file included from /usr/i686-w64-mingw32/sys-root/mingw/include/windows.h:71:0,
45 * from /usr/i686-w64-mingw32/sys-root/mingw/include/winsock2.h:23,
46 * from ../../gnulib/lib/unistd.h:48,
47 * from domtop.c:35:
48 * /usr/i686-w64-mingw32/sys-root/mingw/include/wingdi.h:75:0: note: this is the location of the previous definition
49 * #define ERROR 0
51 #undef ERROR
52 #define ERROR(...) \
53 do { \
54 fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__); \
55 fprintf(stderr, __VA_ARGS__); \
56 fprintf(stderr, "\n"); \
57 } while (0)
59 #define DEBUG(...) \
60 do { \
61 if (!debug) \
62 break; \
63 fprintf(stderr, "DEBUG %s:%d : ", __FUNCTION__, __LINE__); \
64 fprintf(stderr, __VA_ARGS__); \
65 fprintf(stderr, "\n"); \
66 } while (0)
68 #define STREQ(a, b) (strcmp(a, b) == 0)
70 static void
71 print_usage(const char *progname)
73 const char *unified_progname;
75 if (!(unified_progname = strrchr(progname, '/')))
76 unified_progname = progname;
77 else
78 unified_progname++;
80 printf("\n%s [options] [domain name]\n\n"
81 " options:\n"
82 " -d | --debug enable debug messages\n"
83 " -h | --help print this help\n"
84 " -c | --connect=URI hypervisor connection URI\n"
85 " -D | --delay=X delay between updates in milliseconds "
86 "(default is 500ms)\n"
87 "\n"
88 "Print the cumulative usage of each host CPU.\n"
89 "Without any domain name specified the list of\n"
90 "all running domains is printed out.\n",
91 unified_progname);
94 static void
95 parse_argv(int argc, char *argv[],
96 const char **uri,
97 const char **dom_name,
98 unsigned int *milliseconds)
100 int arg;
101 unsigned long val;
102 char *p;
103 struct option opt[] = {
104 {"debug", no_argument, NULL, 'd'},
105 {"help", no_argument, NULL, 'h'},
106 {"connect", required_argument, NULL, 'c'},
107 {"delay", required_argument, NULL, 'D'},
108 {NULL, 0, NULL, 0}
111 while ((arg = getopt_long(argc, argv, "+:dhc:D:", opt, NULL)) != -1) {
112 switch (arg) {
113 case 'd':
114 debug = true;
115 break;
116 case 'h':
117 print_usage(argv[0]);
118 exit(EXIT_SUCCESS);
119 break;
120 case 'c':
121 *uri = optarg;
122 break;
123 case 'D':
124 /* strtoul man page suggests clearing errno prior to call */
125 errno = 0;
126 val = strtoul(optarg, &p, 10);
127 if (errno || *p || p == optarg) {
128 ERROR("Invalid number: '%s'", optarg);
129 exit(EXIT_FAILURE);
131 *milliseconds = val;
132 if (*milliseconds != val) {
133 ERROR("Integer overflow: %ld", val);
134 exit(EXIT_FAILURE);
136 break;
137 case ':':
138 ERROR("option '-%c' requires an argument", optopt);
139 exit(EXIT_FAILURE);
140 case '?':
141 if (optopt)
142 ERROR("unsupported option '-%c'. See --help.", optopt);
143 else
144 ERROR("unsupported option '%s'. See --help.", argv[optind - 1]);
145 exit(EXIT_FAILURE);
146 default:
147 ERROR("unknown option");
148 exit(EXIT_FAILURE);
152 if (argc > optind)
153 *dom_name = argv[optind];
156 static int
157 fetch_domains(virConnectPtr conn)
159 int num_domains, ret = -1;
160 virDomainPtr *domains = NULL;
161 ssize_t i;
162 const int list_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE;
164 DEBUG("Fetching list of running domains");
165 num_domains = virConnectListAllDomains(conn, &domains, list_flags);
167 DEBUG("num_domains=%d", num_domains);
168 if (num_domains < 0) {
169 ERROR("Unable to fetch list of running domains");
170 goto cleanup;
173 printf("Running domains:\n");
174 printf("----------------\n");
175 for (i = 0; i < num_domains; i++) {
176 virDomainPtr dom = domains[i];
177 const char *dom_name = virDomainGetName(dom);
178 printf("%s\n", dom_name);
179 virDomainFree(dom);
182 ret = 0;
183 cleanup:
184 free(domains);
185 return ret;
188 static void
189 print_cpu_usage(size_t cpu,
190 size_t ncpus,
191 unsigned long long then,
192 virTypedParameterPtr then_params,
193 size_t then_nparams,
194 unsigned long long now,
195 virTypedParameterPtr now_params,
196 size_t now_nparams)
198 size_t i, j;
199 size_t nparams = now_nparams;
200 bool delim = false;
202 if (then_nparams != now_nparams) {
203 /* this should not happen (TM) */
204 ERROR("parameters counts don't match");
205 return;
208 for (i = 0; i < ncpus; i++) {
209 size_t pos = 0;
210 double usage;
212 /* check if the vCPU is in the maps */
213 if (now_params[i * nparams].type == 0 ||
214 then_params[i * then_nparams].type == 0)
215 continue;
217 for (j = 0; j < nparams; j++) {
218 pos = i * nparams + j;
219 if (STREQ(then_params[pos].field, VIR_DOMAIN_CPU_STATS_CPUTIME) ||
220 STREQ(then_params[pos].field, VIR_DOMAIN_CPU_STATS_VCPUTIME))
221 break;
224 if (j == nparams) {
225 ERROR("unable to find %s", VIR_DOMAIN_CPU_STATS_CPUTIME);
226 return;
229 DEBUG("now_params=%" PRIu64 " then_params=%" PRIu64
230 " now=%" PRIu64 " then=%" PRIu64,
231 (uint64_t)now_params[pos].value.ul,
232 (uint64_t)then_params[pos].value.ul,
233 (uint64_t)now, (uint64_t)then);
235 /* @now_params and @then_params are in nanoseconds, @now and @then are
236 * in microseconds. In ideal world, we would translate them both into
237 * the same scale, divide one by another and multiply by factor of 100
238 * to get percentage. However, the count of floating point operations
239 * performed has a bad effect on the precision, so instead of dividing
240 * @now_params and @then_params by 1000 and then multiplying again by
241 * 100, we divide only once by 10 and get the same result. */
242 usage = (now_params[pos].value.ul - then_params[pos].value.ul) /
243 (now - then) / 10;
245 if (delim)
246 printf("\t");
247 /* mingw lacks %zu */
248 printf("CPU%u: %.2lf", (unsigned)(cpu + i), usage);
249 delim = true;
252 printf("\n");
255 static void
256 stop(int sig)
258 DEBUG("Exiting on signal %d\n", sig);
259 run_top = false;
262 static int
263 do_top(virConnectPtr conn,
264 const char *dom_name,
265 unsigned int milliseconds)
267 int ret = -1;
268 virDomainPtr dom;
269 int max_id = 0;
270 int nparams = 0, then_nparams = 0, now_nparams = 0;
271 virTypedParameterPtr then_params = NULL, now_params = NULL;
273 /* Lookup the domain */
274 if (!(dom = virDomainLookupByName(conn, dom_name))) {
275 ERROR("Unable to find domain '%s'", dom_name);
276 goto cleanup;
279 /* and see how many vCPUs can we fetch stats for */
280 if ((max_id = virDomainGetCPUStats(dom, NULL, 0, 0, 0, 0)) < 0) {
281 ERROR("Unable to get cpu stats");
282 goto cleanup;
285 /* how many stats can we get for a vCPU? */
286 if ((nparams = virDomainGetCPUStats(dom, NULL, 0, 0, 1, 0)) < 0) {
287 ERROR("Unable to get cpu stats");
288 goto cleanup;
291 if (!(now_params = calloc(nparams * max_id, sizeof(*now_params))) ||
292 !(then_params = calloc(nparams * max_id, sizeof(*then_params)))) {
293 ERROR("Unable to allocate memory");
294 goto cleanup;
297 /* The ideal program would use sigaction to set this handler, but
298 * this way is portable to mingw. */
299 signal(SIGTERM, stop);
300 signal(SIGINT, stop);
302 run_top = true;
303 while (run_top) {
304 struct timeval then, now;
306 /* Get current time */
307 if (gettimeofday(&then, NULL) < 0) {
308 ERROR("unable to get time");
309 goto cleanup;
312 /* And current stats */
313 if ((then_nparams = virDomainGetCPUStats(dom, then_params,
314 nparams, 0, max_id, 0)) < 0) {
315 ERROR("Unable to get cpu stats");
316 goto cleanup;
319 /* Now sleep some time */
320 usleep(milliseconds * 1000); /* usleep expects microseconds */
322 /* And get current time */
323 if (gettimeofday(&now, NULL) < 0) {
324 ERROR("unable to get time");
325 goto cleanup;
328 /* And current stats */
329 if ((now_nparams = virDomainGetCPUStats(dom, now_params,
330 nparams, 0, max_id, 0)) < 0) {
331 ERROR("Unable to get cpu stats");
332 goto cleanup;
335 print_cpu_usage(0, max_id,
336 then.tv_sec * 1000000 + then.tv_usec,
337 then_params, then_nparams,
338 now.tv_sec * 1000000 + now.tv_usec,
339 now_params, now_nparams);
341 virTypedParamsClear(now_params, now_nparams * max_id);
342 virTypedParamsClear(then_params, then_nparams * max_id);
345 ret = 0;
346 cleanup:
347 virTypedParamsFree(now_params, nparams * max_id);
348 virTypedParamsFree(then_params, nparams * max_id);
349 if (dom)
350 virDomainFree(dom);
351 return ret;
355 main(int argc, char *argv[])
357 int ret = EXIT_FAILURE;
358 virConnectPtr conn = NULL;
359 const char *uri = NULL;
360 const char *dom_name = NULL;
361 unsigned int milliseconds = 500; /* Sleep this long between two API calls */
362 const int connect_flags = 0; /* No connect flags for now */
364 parse_argv(argc, argv, &uri, &dom_name, &milliseconds);
366 DEBUG("Proceeding with uri=%s dom_name=%s milliseconds=%u",
367 uri, dom_name, milliseconds);
369 if (!(conn = virConnectOpenAuth(uri,
370 virConnectAuthPtrDefault,
371 connect_flags))) {
372 ERROR("Failed to connect to hypervisor");
373 goto cleanup;
376 DEBUG("Successfully connected");
378 if (!dom_name) {
379 if (fetch_domains(conn) == 0)
380 ret = EXIT_SUCCESS;
381 goto cleanup;
384 if (do_top(conn, dom_name, milliseconds) < 0)
385 goto cleanup;
387 ret = EXIT_SUCCESS;
388 cleanup:
389 if (conn) {
390 int tmp;
391 tmp = virConnectClose(conn);
392 if (tmp < 0) {
393 ERROR("Failed to disconnect from the hypervisor");
394 ret = EXIT_FAILURE;
395 } else if (tmp > 0) {
396 ERROR("One or more references were leaked after "
397 "disconnect from the hypervisor");
398 ret = EXIT_FAILURE;
399 } else {
400 DEBUG("Connection successfully closed");
403 return ret;