ia64/xen-unstable

view tools/xenstat/libxenstat/src/xenstat.c @ 7555:889afaf6ca25

Free domain names in xenstat_free_node, fixing small memory leak.

Thanks to Anthony Liguori for spotting the leak.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Sun Oct 30 13:23:21 2005 +0100 (2005-10-30)
parents a793b7a53605
children c794c98c416c
line source
1 /* libxenstat: statistics-collection library for Xen
2 * Copyright (C) International Business Machines Corp., 2005
3 * Authors: Josh Triplett <josht@us.ibm.com>
4 * Judy Fischbach <jfisch@us.ibm.com>
5 * David Hendricks <dhendrix@us.ibm.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 */
18 #include <limits.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <xen-interface.h>
24 #include <xs.h>
25 #include "xenstat.h"
27 /*
28 * Types
29 */
30 #define SHORT_ASC_LEN 5 /* length of 65535 */
31 #define VERSION_SIZE (2 * SHORT_ASC_LEN + 1 + sizeof(xen_extraversion_t) + 1)
33 struct xenstat_handle {
34 xi_handle *xihandle;
35 struct xs_handle *xshandle; /* xenstore handle */
36 int page_size;
37 FILE *procnetdev;
38 char xen_version[VERSION_SIZE]; /* xen version running on this node */
39 };
41 struct xenstat_node {
42 xenstat_handle *handle;
43 unsigned int flags;
44 unsigned long long cpu_hz;
45 unsigned int num_cpus;
46 unsigned long long tot_mem;
47 unsigned long long free_mem;
48 unsigned int num_domains;
49 xenstat_domain *domains; /* Array of length num_domains */
50 };
52 struct xenstat_domain {
53 unsigned int id;
54 char *name;
55 unsigned int state;
56 unsigned long long cpu_ns;
57 unsigned int num_vcpus; /* No. vcpus configured for domain */
58 xenstat_vcpu *vcpus; /* Array of length num_vcpus */
59 unsigned long long cur_mem; /* Current memory reservation */
60 unsigned long long max_mem; /* Total memory allowed */
61 unsigned int ssid;
62 unsigned int num_networks;
63 xenstat_network *networks; /* Array of length num_networks */
64 };
66 struct xenstat_vcpu {
67 unsigned int online;
68 unsigned long long ns;
69 };
71 struct xenstat_network {
72 unsigned int id;
73 /* Received */
74 unsigned long long rbytes;
75 unsigned long long rpackets;
76 unsigned long long rerrs;
77 unsigned long long rdrop;
78 /* Transmitted */
79 unsigned long long tbytes;
80 unsigned long long tpackets;
81 unsigned long long terrs;
82 unsigned long long tdrop;
83 };
85 /*
86 * Data-collection types
87 */
88 /* Called to collect the information for the node and all the domains on
89 * it. When called, the domain information has already been collected. */
90 typedef int (*xenstat_collect_func)(xenstat_node * node);
91 /* Called to free the information collected by the collect function. The free
92 * function will only be called on a xenstat_node if that node includes
93 * information collected by the corresponding collector. */
94 typedef void (*xenstat_free_func)(xenstat_node * node);
95 /* Called to free any information stored in the handle. Note the lack of a
96 * matching init function; the collect functions should initialize on first
97 * use. Also, the uninit function must handle the case that the collector has
98 * never been initialized. */
99 typedef void (*xenstat_uninit_func)(xenstat_handle * handle);
100 typedef struct xenstat_collector {
101 unsigned int flag;
102 xenstat_collect_func collect;
103 xenstat_free_func free;
104 xenstat_uninit_func uninit;
105 } xenstat_collector;
107 static int xenstat_collect_vcpus(xenstat_node * node);
108 static int xenstat_collect_networks(xenstat_node * node);
109 static int xenstat_collect_xen_version(xenstat_node * node);
110 static void xenstat_free_vcpus(xenstat_node * node);
111 static void xenstat_free_networks(xenstat_node * node);
112 static void xenstat_free_xen_version(xenstat_node * node);
113 static void xenstat_uninit_vcpus(xenstat_handle * handle);
114 static void xenstat_uninit_networks(xenstat_handle * handle);
115 static void xenstat_uninit_xen_version(xenstat_handle * handle);
116 static char *xenstat_get_domain_name(xenstat_handle * handle, unsigned int domain_id);
118 static xenstat_collector collectors[] = {
119 { XENSTAT_VCPU, xenstat_collect_vcpus,
120 xenstat_free_vcpus, xenstat_uninit_vcpus },
121 { XENSTAT_NETWORK, xenstat_collect_networks,
122 xenstat_free_networks, xenstat_uninit_networks },
123 { XENSTAT_XEN_VERSION, xenstat_collect_xen_version,
124 xenstat_free_xen_version, xenstat_uninit_xen_version }
125 };
127 #define NUM_COLLECTORS (sizeof(collectors)/sizeof(xenstat_collector))
129 /*
130 * libxenstat API
131 */
132 xenstat_handle *xenstat_init()
133 {
134 xenstat_handle *handle;
136 handle = (xenstat_handle *) calloc(1, sizeof(xenstat_handle));
137 if (handle == NULL)
138 return NULL;
140 #if defined(PAGESIZE)
141 handle->page_size = PAGESIZE;
142 #elif defined(PAGE_SIZE)
143 handle->page_size = PAGE_SIZE;
144 #else
145 handle->page_size = sysconf(_SC_PAGE_SIZE);
146 if (handle->page_size < 0) {
147 perror("Failed to retrieve page size.");
148 free(handle);
149 return NULL;
150 }
151 #endif
153 handle->xihandle = xi_init();
154 if (handle->xihandle == NULL) {
155 perror("xi_init");
156 free(handle);
157 return NULL;
158 }
160 handle->xshandle = xs_daemon_open_readonly(); /* open handle to xenstore*/
161 if (handle->xshandle == NULL) {
162 perror("unable to open xenstore\n");
163 free(handle);
164 return NULL;
165 }
167 return handle;
168 }
170 void xenstat_uninit(xenstat_handle * handle)
171 {
172 unsigned int i;
173 if (handle) {
174 for (i = 0; i < NUM_COLLECTORS; i++)
175 collectors[i].uninit(handle);
176 xi_uninit(handle->xihandle);
177 xs_daemon_close(handle->xshandle);
178 free(handle);
179 }
180 }
182 xenstat_node *xenstat_get_node(xenstat_handle * handle, unsigned int flags)
183 {
184 #define DOMAIN_CHUNK_SIZE 256
185 xenstat_node *node;
186 dom0_physinfo_t physinfo;
187 dom0_getdomaininfo_t domaininfo[DOMAIN_CHUNK_SIZE];
188 unsigned int num_domains, new_domains;
189 unsigned int i;
191 /* Create the node */
192 node = (xenstat_node *) calloc(1, sizeof(xenstat_node));
193 if (node == NULL)
194 return NULL;
196 /* Store the handle in the node for later access */
197 node->handle = handle;
199 /* Get information about the physical system */
200 if (xi_get_physinfo(handle->xihandle, &physinfo) < 0) {
201 free(node);
202 return NULL;
203 }
205 node->cpu_hz = ((unsigned long long)physinfo.cpu_khz) * 1000ULL;
206 node->num_cpus =
207 (physinfo.threads_per_core * physinfo.cores_per_socket *
208 physinfo.sockets_per_node * physinfo.nr_nodes);
209 node->tot_mem = ((unsigned long long)physinfo.total_pages)
210 * handle->page_size;
211 node->free_mem = ((unsigned long long)physinfo.free_pages)
212 * handle->page_size;
214 /* malloc(0) is not portable, so allocate a single domain. This will
215 * be resized below. */
216 node->domains = malloc(sizeof(xenstat_domain));
217 if (node->domains == NULL) {
218 free(node);
219 return NULL;
220 }
222 num_domains = 0;
223 do {
224 xenstat_domain *domain;
226 new_domains = xi_get_domaininfolist(handle->xihandle,
227 domaininfo, num_domains,
228 DOMAIN_CHUNK_SIZE);
230 node->domains = realloc(node->domains,
231 (num_domains + new_domains)
232 * sizeof(xenstat_domain));
233 if (node->domains == NULL) {
234 free(node);
235 return NULL;
236 }
238 domain = node->domains + num_domains;
240 for (i = 0; i < new_domains; i++) {
241 /* Fill in domain using domaininfo[i] */
242 domain->id = domaininfo[i].domain;
243 domain->name = xenstat_get_domain_name(handle, domaininfo[i].domain);
244 domain->state = domaininfo[i].flags;
245 domain->cpu_ns = domaininfo[i].cpu_time;
246 domain->num_vcpus = (domaininfo[i].max_vcpu_id+1);
247 domain->vcpus = NULL;
248 domain->cur_mem =
249 ((unsigned long long)domaininfo[i].tot_pages)
250 * handle->page_size;
251 domain->max_mem =
252 domaininfo[i].max_pages == UINT_MAX
253 ? (unsigned long long)-1
254 : (unsigned long long)(domaininfo[i].max_pages
255 * handle->page_size);
256 domain->ssid = domaininfo[i].ssidref;
257 domain->num_networks = 0;
258 domain->networks = NULL;
260 domain++;
261 }
262 num_domains += new_domains;
263 } while (new_domains == DOMAIN_CHUNK_SIZE);
264 node->num_domains = num_domains;
266 /* Run all the extra data collectors requested */
267 node->flags = 0;
268 for (i = 0; i < NUM_COLLECTORS; i++) {
269 if ((flags & collectors[i].flag) == collectors[i].flag) {
270 node->flags |= collectors[i].flag;
271 if(collectors[i].collect(node) == 0) {
272 xenstat_free_node(node);
273 return NULL;
274 }
275 }
276 }
278 return node;
279 }
281 void xenstat_free_node(xenstat_node * node)
282 {
283 int i;
285 if (node) {
286 if (node->domains) {
287 for (i = 0; i < node->num_domains; i++) {
288 if (node->domains[i].name)
289 free(node->domains[i].name);
290 }
292 for (i = 0; i < NUM_COLLECTORS; i++)
293 if((node->flags & collectors[i].flag)
294 == collectors[i].flag)
295 collectors[i].free(node);
296 free(node->domains);
297 }
298 free(node);
299 }
300 }
302 xenstat_domain *xenstat_node_domain(xenstat_node * node, unsigned int domid)
303 {
304 unsigned int i;
306 /* FIXME: binary search */
307 /* Find the appropriate domain entry in the node struct. */
308 for (i = 0; i < node->num_domains; i++) {
309 if (node->domains[i].id == domid)
310 return &(node->domains[i]);
311 }
312 return NULL;
313 }
315 xenstat_domain *xenstat_node_domain_by_index(xenstat_node * node,
316 unsigned int index)
317 {
318 if (0 <= index && index < node->num_domains)
319 return &(node->domains[index]);
320 return NULL;
321 }
323 const char *xenstat_node_xen_version(xenstat_node * node)
324 {
325 return node->handle->xen_version;
326 }
328 unsigned long long xenstat_node_tot_mem(xenstat_node * node)
329 {
330 return node->tot_mem;
331 }
333 unsigned long long xenstat_node_free_mem(xenstat_node * node)
334 {
335 return node->free_mem;
336 }
338 unsigned int xenstat_node_num_domains(xenstat_node * node)
339 {
340 return node->num_domains;
341 }
343 unsigned int xenstat_node_num_cpus(xenstat_node * node)
344 {
345 return node->num_cpus;
346 }
348 /* Get information about the CPU speed */
349 unsigned long long xenstat_node_cpu_hz(xenstat_node * node)
350 {
351 return node->cpu_hz;
352 }
354 /* Get the domain ID for this domain */
355 unsigned xenstat_domain_id(xenstat_domain * domain)
356 {
357 return domain->id;
358 }
360 /* Get the domain name for the domain */
361 char *xenstat_domain_name(xenstat_domain * domain)
362 {
363 return domain->name;
364 }
366 /* Get information about how much CPU time has been used */
367 unsigned long long xenstat_domain_cpu_ns(xenstat_domain * domain)
368 {
369 return domain->cpu_ns;
370 }
372 /* Find the number of VCPUs for a domain */
373 unsigned int xenstat_domain_num_vcpus(xenstat_domain * domain)
374 {
375 return domain->num_vcpus;
376 }
378 xenstat_vcpu *xenstat_domain_vcpu(xenstat_domain * domain, unsigned int vcpu)
379 {
380 if (0 <= vcpu && vcpu < domain->num_vcpus)
381 return &(domain->vcpus[vcpu]);
382 return NULL;
383 }
385 /* Find the current memory reservation for this domain */
386 unsigned long long xenstat_domain_cur_mem(xenstat_domain * domain)
387 {
388 return domain->cur_mem;
389 }
391 /* Find the maximum memory reservation for this domain */
392 unsigned long long xenstat_domain_max_mem(xenstat_domain * domain)
393 {
394 return domain->max_mem;
395 }
397 /* Find the domain's SSID */
398 unsigned int xenstat_domain_ssid(xenstat_domain * domain)
399 {
400 return domain->ssid;
401 }
403 /* Get domain states */
404 unsigned int xenstat_domain_dying(xenstat_domain * domain)
405 {
406 return (domain->state & DOMFLAGS_DYING) == DOMFLAGS_DYING;
407 }
409 unsigned int xenstat_domain_crashed(xenstat_domain * domain)
410 {
411 return ((domain->state & DOMFLAGS_SHUTDOWN) == DOMFLAGS_SHUTDOWN)
412 && (((domain->state >> DOMFLAGS_SHUTDOWNSHIFT)
413 & DOMFLAGS_SHUTDOWNMASK) == SHUTDOWN_crash);
414 }
416 unsigned int xenstat_domain_shutdown(xenstat_domain * domain)
417 {
418 return ((domain->state & DOMFLAGS_SHUTDOWN) == DOMFLAGS_SHUTDOWN)
419 && (((domain->state >> DOMFLAGS_SHUTDOWNSHIFT)
420 & DOMFLAGS_SHUTDOWNMASK) != SHUTDOWN_crash);
421 }
423 unsigned int xenstat_domain_paused(xenstat_domain * domain)
424 {
425 return (domain->state & DOMFLAGS_PAUSED) == DOMFLAGS_PAUSED;
426 }
428 unsigned int xenstat_domain_blocked(xenstat_domain * domain)
429 {
430 return (domain->state & DOMFLAGS_BLOCKED) == DOMFLAGS_BLOCKED;
431 }
433 unsigned int xenstat_domain_running(xenstat_domain * domain)
434 {
435 return (domain->state & DOMFLAGS_RUNNING) == DOMFLAGS_RUNNING;
436 }
438 /* Get the number of networks for a given domain */
439 unsigned int xenstat_domain_num_networks(xenstat_domain * domain)
440 {
441 return domain->num_networks;
442 }
444 /* Get the network handle to obtain network stats */
445 xenstat_network *xenstat_domain_network(xenstat_domain * domain,
446 unsigned int network)
447 {
448 if (domain->networks && 0 <= network && network < domain->num_networks)
449 return &(domain->networks[network]);
450 return NULL;
451 }
453 /*
454 * VCPU functions
455 */
456 /* Collect information about VCPUs */
457 static int xenstat_collect_vcpus(xenstat_node * node)
458 {
459 unsigned int i, vcpu;
461 /* Fill in VCPU information */
462 for (i = 0; i < node->num_domains; i++) {
463 node->domains[i].vcpus = malloc(node->domains[i].num_vcpus
464 * sizeof(xenstat_vcpu));
465 if (node->domains[i].vcpus == NULL)
466 return 0;
468 for (vcpu = 0; vcpu < node->domains[i].num_vcpus; vcpu++) {
469 /* FIXME: need to be using a more efficient mechanism*/
470 dom0_getvcpuinfo_t info;
472 if (xi_get_domain_vcpu_info(node->handle->xihandle,
473 node->domains[i].id, vcpu, &info) != 0)
474 return 0;
476 node->domains[i].vcpus[vcpu].online = info.online;
477 node->domains[i].vcpus[vcpu].ns = info.cpu_time;
478 }
479 }
480 return 1;
481 }
483 /* Free VCPU information */
484 static void xenstat_free_vcpus(xenstat_node * node)
485 {
486 unsigned int i;
487 for (i = 0; i < node->num_domains; i++)
488 free(node->domains[i].vcpus);
489 }
491 /* Free VCPU information in handle - nothing to do */
492 static void xenstat_uninit_vcpus(xenstat_handle * handle)
493 {
494 }
496 /* Get VCPU online status */
497 unsigned int xenstat_vcpu_online(xenstat_vcpu * vcpu)
498 {
499 return vcpu->online;
500 }
502 /* Get VCPU usage */
503 unsigned long long xenstat_vcpu_ns(xenstat_vcpu * vcpu)
504 {
505 return vcpu->ns;
506 }
508 /*
509 * Network functions
510 */
512 /* Expected format of /proc/net/dev */
513 static const char PROCNETDEV_HEADER[] =
514 "Inter-| Receive |"
515 " Transmit\n"
516 " face |bytes packets errs drop fifo frame compressed multicast|"
517 "bytes packets errs drop fifo colls carrier compressed\n";
519 /* Collect information about networks */
520 static int xenstat_collect_networks(xenstat_node * node)
521 {
522 /* Open and validate /proc/net/dev if we haven't already */
523 if (node->handle->procnetdev == NULL) {
524 char header[sizeof(PROCNETDEV_HEADER)];
525 node->handle->procnetdev = fopen("/proc/net/dev", "r");
526 if (node->handle->procnetdev == NULL) {
527 perror("Error opening /proc/net/dev");
528 return 0;
529 }
531 /* Validate the format of /proc/net/dev */
532 if (fread(header, sizeof(PROCNETDEV_HEADER) - 1, 1,
533 node->handle->procnetdev) != 1) {
534 perror("Error reading /proc/net/dev header");
535 return 0;
536 }
537 header[sizeof(PROCNETDEV_HEADER) - 1] = '\0';
538 if (strcmp(header, PROCNETDEV_HEADER) != 0) {
539 fprintf(stderr,
540 "Unexpected /proc/net/dev format\n");
541 return 0;
542 }
543 }
545 /* Fill in networks */
546 /* FIXME: optimize this */
547 fseek(node->handle->procnetdev, sizeof(PROCNETDEV_HEADER) - 1,
548 SEEK_SET);
549 while (1) {
550 xenstat_domain *domain;
551 xenstat_network net;
552 unsigned int domid;
553 int ret = fscanf(node->handle->procnetdev,
554 "vif%u.%u:%llu%llu%llu%llu%*u%*u%*u%*u"
555 "%llu%llu%llu%llu%*u%*u%*u%*u\n",
556 &domid, &net.id,
557 &net.tbytes, &net.tpackets, &net.terrs,
558 &net.tdrop,
559 &net.rbytes, &net.rpackets, &net.rerrs,
560 &net.rdrop);
561 if (ret == EOF)
562 break;
563 if (ret != 10) {
564 unsigned int c;
565 do {
566 c = fgetc(node->handle->procnetdev);
567 } while (c != '\n' && c != EOF);
568 if (c == EOF)
569 break;
570 continue;
571 }
573 /* FIXME: this does a search for the domid */
574 domain = xenstat_node_domain(node, domid);
575 if (domain == NULL) {
576 fprintf(stderr,
577 "Found interface vif%u.%u but domain %u"
578 " does not exist.\n", domid, net.id,
579 domid);
580 continue;
581 }
582 if (domain->networks == NULL) {
583 domain->num_networks = 1;
584 domain->networks = malloc(sizeof(xenstat_network));
585 } else {
586 domain->num_networks++;
587 domain->networks =
588 realloc(domain->networks,
589 domain->num_networks *
590 sizeof(xenstat_network));
591 }
592 if (domain->networks == NULL)
593 return 0;
594 domain->networks[domain->num_networks - 1] = net;
595 }
597 return 1;
598 }
600 /* Free network information */
601 static void xenstat_free_networks(xenstat_node * node)
602 {
603 unsigned int i;
604 for (i = 0; i < node->num_domains; i++)
605 free(node->domains[i].networks);
606 }
608 /* Free network information in handle */
609 static void xenstat_uninit_networks(xenstat_handle * handle)
610 {
611 if(handle->procnetdev)
612 fclose(handle->procnetdev);
613 }
615 /* Get the network ID */
616 unsigned int xenstat_network_id(xenstat_network * network)
617 {
618 return network->id;
619 }
621 /* Get the number of receive bytes */
622 unsigned long long xenstat_network_rbytes(xenstat_network * network)
623 {
624 return network->rbytes;
625 }
627 /* Get the number of receive packets */
628 unsigned long long xenstat_network_rpackets(xenstat_network * network)
629 {
630 return network->rpackets;
631 }
633 /* Get the number of receive errors */
634 unsigned long long xenstat_network_rerrs(xenstat_network * network)
635 {
636 return network->rerrs;
637 }
639 /* Get the number of receive drops */
640 unsigned long long xenstat_network_rdrop(xenstat_network * network)
641 {
642 return network->rdrop;
643 }
645 /* Get the number of transmit bytes */
646 unsigned long long xenstat_network_tbytes(xenstat_network * network)
647 {
648 return network->tbytes;
649 }
651 /* Get the number of transmit packets */
652 unsigned long long xenstat_network_tpackets(xenstat_network * network)
653 {
654 return network->tpackets;
655 }
657 /* Get the number of transmit errors */
658 unsigned long long xenstat_network_terrs(xenstat_network * network)
659 {
660 return network->terrs;
661 }
663 /* Get the number of transmit dropped packets */
664 unsigned long long xenstat_network_tdrop(xenstat_network * network)
665 {
666 return network->tdrop;
667 }
669 /*
670 * Xen version functions
671 */
673 /* Collect Xen version information */
674 static int xenstat_collect_xen_version(xenstat_node * node)
675 {
676 long vnum = 0;
677 xen_extraversion_t version;
679 /* Collect Xen version information if not already collected */
680 if (node->handle->xen_version[0] == '\0') {
681 /* Get the Xen version number and extraversion string */
682 if (xi_get_xen_version(node->handle->xihandle,
683 &vnum, &version) < 0)
684 return 0;
685 /* Format the version information as a string and store it */
686 snprintf(node->handle->xen_version, VERSION_SIZE, "%ld.%ld%s",
687 ((vnum >> 16) & 0xFFFF), vnum & 0xFFFF, version);
688 }
690 return 1;
691 }
693 /* Free Xen version information in node - nothing to do */
694 static void xenstat_free_xen_version(xenstat_node * node)
695 {
696 }
698 /* Free Xen version information in handle - nothing to do */
699 static void xenstat_uninit_xen_version(xenstat_handle * handle)
700 {
701 }
703 static char *xenstat_get_domain_name(xenstat_handle *handle, unsigned int domain_id)
704 {
705 char path[80];
706 char *name;
707 unsigned int *len;
708 struct xs_transaction_handle *xstranshandle;
710 snprintf(path, sizeof(path),"/local/domain/%i/name", domain_id);
712 xstranshandle = xs_transaction_start(handle->xshandle);
713 if (xstranshandle == NULL) {
714 perror("Unable to get transcation handle from xenstore\n");
715 exit(1); /* Change this */
716 }
718 name = (char *) xs_read(handle->xshandle, xstranshandle, path, len);
720 xs_transaction_end(handle->xshandle, xstranshandle, false);
722 return name;
723 }