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/>.
23 #include <libvirt/libvirt.h>
24 #include <libvirt/virterror.h>
37 /* On mingw, there's a header file that poisons the well:
41 *domtop.c:40:0: warning: "ERROR" redefined [enabled by default]
42 * #define ERROR(...) \
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,
48 * /usr/i686-w64-mingw32/sys-root/mingw/include/wingdi.h:75:0: note: this is the location of the previous definition
54 fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__); \
55 fprintf(stderr, __VA_ARGS__); \
56 fprintf(stderr, "\n"); \
63 fprintf(stderr, "DEBUG %s:%d : ", __FUNCTION__, __LINE__); \
64 fprintf(stderr, __VA_ARGS__); \
65 fprintf(stderr, "\n"); \
68 #define STREQ(a, b) (strcmp(a, b) == 0)
71 print_usage(const char *progname
)
73 const char *unified_progname
;
75 if (!(unified_progname
= strrchr(progname
, '/')))
76 unified_progname
= progname
;
80 printf("\n%s [options] [domain name]\n\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"
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",
95 parse_argv(int argc
, char *argv
[],
97 const char **dom_name
,
98 unsigned int *milliseconds
)
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'},
111 while ((arg
= getopt_long(argc
, argv
, "+:dhc:D:", opt
, NULL
)) != -1) {
117 print_usage(argv
[0]);
124 /* strtoul man page suggests clearing errno prior to call */
126 val
= strtoul(optarg
, &p
, 10);
127 if (errno
|| *p
|| p
== optarg
) {
128 ERROR("Invalid number: '%s'", optarg
);
132 if (*milliseconds
!= val
) {
133 ERROR("Integer overflow: %ld", val
);
138 ERROR("option '-%c' requires an argument", optopt
);
142 ERROR("unsupported option '-%c'. See --help.", optopt
);
144 ERROR("unsupported option '%s'. See --help.", argv
[optind
- 1]);
147 ERROR("unknown option");
153 *dom_name
= argv
[optind
];
157 fetch_domains(virConnectPtr conn
)
159 int num_domains
, ret
= -1;
160 virDomainPtr
*domains
= NULL
;
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");
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
);
189 print_cpu_usage(size_t cpu
,
191 unsigned long long then
,
192 virTypedParameterPtr then_params
,
194 unsigned long long now
,
195 virTypedParameterPtr now_params
,
199 size_t nparams
= now_nparams
;
202 if (then_nparams
!= now_nparams
) {
203 /* this should not happen (TM) */
204 ERROR("parameters counts don't match");
208 for (i
= 0; i
< ncpus
; i
++) {
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)
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
))
225 ERROR("unable to find %s", VIR_DOMAIN_CPU_STATS_CPUTIME
);
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
) /
247 /* mingw lacks %zu */
248 printf("CPU%u: %.2lf", (unsigned)(cpu
+ i
), usage
);
258 DEBUG("Exiting on signal %d\n", sig
);
263 do_top(virConnectPtr conn
,
264 const char *dom_name
,
265 unsigned int milliseconds
)
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
);
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");
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");
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");
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
);
304 struct timeval then
, now
;
306 /* Get current time */
307 if (gettimeofday(&then
, NULL
) < 0) {
308 ERROR("unable to get time");
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");
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");
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");
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
);
347 virTypedParamsFree(now_params
, nparams
* max_id
);
348 virTypedParamsFree(then_params
, nparams
* max_id
);
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
,
372 ERROR("Failed to connect to hypervisor");
376 DEBUG("Successfully connected");
379 if (fetch_domains(conn
) == 0)
384 if (do_top(conn
, dom_name
, milliseconds
) < 0)
391 tmp
= virConnectClose(conn
);
393 ERROR("Failed to disconnect from the hypervisor");
395 } else if (tmp
> 0) {
396 ERROR("One or more references were leaked after "
397 "disconnect from the hypervisor");
400 DEBUG("Connection successfully closed");