ia64/xen-unstable

view tools/xenstat/libxenstat/src/xenstat.c @ 6440:9404574350ce

Cache the Xen version in the xenstat_handle, since it should never change.
Use a collector and new flag for Xen version information, to avoid the
extra hypercall if the caller does not want the information.
Reference the xenstat_handle in the xenstat_node for ease of access
later, and update collectors accordingly.
Signed-off-by: Josh Triplett <josht@us.ibm.com>
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Fri Aug 26 08:48:59 2005 +0000 (2005-08-26)
parents af7251014caf
children 28ffa7296a44
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 "xenstat.h"
25 #include "version.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 int page_size;
36 FILE *procnetdev;
37 char xen_version[VERSION_SIZE]; /* xen version running on this node */
38 };
40 struct xenstat_node {
41 xenstat_handle *handle;
42 unsigned int flags;
43 unsigned long long cpu_hz;
44 unsigned int num_cpus;
45 unsigned long long tot_mem;
46 unsigned long long free_mem;
47 unsigned int num_domains;
48 xenstat_domain *domains; /* Array of length num_domains */
49 };
51 struct xenstat_domain {
52 unsigned int id;
53 unsigned int state;
54 unsigned long long cpu_ns;
55 unsigned int num_vcpus;
56 xenstat_vcpu *vcpus; /* Array of length num_vcpus */
57 unsigned long long cur_mem; /* Current memory reservation */
58 unsigned long long max_mem; /* Total memory allowed */
59 unsigned int ssid;
60 unsigned int num_networks;
61 xenstat_network *networks; /* Array of length num_networks */
62 };
64 struct xenstat_vcpu {
65 unsigned long long ns;
66 };
68 struct xenstat_network {
69 unsigned int id;
70 /* Received */
71 unsigned long long rbytes;
72 unsigned long long rpackets;
73 unsigned long long rerrs;
74 unsigned long long rdrop;
75 /* Transmitted */
76 unsigned long long tbytes;
77 unsigned long long tpackets;
78 unsigned long long terrs;
79 unsigned long long tdrop;
80 };
82 /*
83 * Data-collection types
84 */
85 /* Called to collect the information for the node and all the domains on
86 * it. When called, the domain information has already been collected. */
87 typedef int (*xenstat_collect_func)(xenstat_node * node);
88 /* Called to free the information collected by the collect function. The free
89 * function will only be called on a xenstat_node if that node includes
90 * information collected by the corresponding collector. */
91 typedef void (*xenstat_free_func)(xenstat_node * node);
92 /* Called to free any information stored in the handle. Note the lack of a
93 * matching init function; the collect functions should initialize on first
94 * use. Also, the uninit function must handle the case that the collector has
95 * never been initialized. */
96 typedef void (*xenstat_uninit_func)(xenstat_handle * handle);
97 typedef struct xenstat_collector {
98 unsigned int flag;
99 xenstat_collect_func collect;
100 xenstat_free_func free;
101 xenstat_uninit_func uninit;
102 } xenstat_collector;
104 static int xenstat_collect_vcpus(xenstat_node * node);
105 static int xenstat_collect_networks(xenstat_node * node);
106 static int xenstat_collect_xen_version(xenstat_node * node);
107 static void xenstat_free_vcpus(xenstat_node * node);
108 static void xenstat_free_networks(xenstat_node * node);
109 static void xenstat_free_xen_version(xenstat_node * node);
110 static void xenstat_uninit_vcpus(xenstat_handle * handle);
111 static void xenstat_uninit_networks(xenstat_handle * handle);
112 static void xenstat_uninit_xen_version(xenstat_handle * handle);
114 static xenstat_collector collectors[] = {
115 { XENSTAT_VCPU, xenstat_collect_vcpus,
116 xenstat_free_vcpus, xenstat_uninit_vcpus },
117 { XENSTAT_NETWORK, xenstat_collect_networks,
118 xenstat_free_networks, xenstat_uninit_networks },
119 { XENSTAT_XEN_VERSION, xenstat_collect_xen_version,
120 xenstat_free_xen_version, xenstat_uninit_xen_version }
121 };
123 #define NUM_COLLECTORS (sizeof(collectors)/sizeof(xenstat_collector))
125 /*
126 * libxenstat API
127 */
128 xenstat_handle *xenstat_init()
129 {
130 xenstat_handle *handle;
132 handle = (xenstat_handle *) calloc(1, sizeof(xenstat_handle));
133 if (handle == NULL)
134 return NULL;
136 #if defined(PAGESIZE)
137 handle->page_size = PAGESIZE;
138 #elif defined(PAGE_SIZE)
139 handle->page_size = PAGE_SIZE;
140 #else
141 handle->page_size = sysconf(_SC_PAGE_SIZE);
142 if (handle->page_size < 0) {
143 perror("Failed to retrieve page size.");
144 free(handle);
145 return NULL;
146 }
147 #endif
149 handle->xihandle = xi_init();
150 if (handle->xihandle == NULL) {
151 perror("xi_init");
152 free(handle);
153 return NULL;
154 }
156 return handle;
157 }
159 void xenstat_uninit(xenstat_handle * handle)
160 {
161 unsigned int i;
162 if (handle) {
163 for (i = 0; i < NUM_COLLECTORS; i++)
164 collectors[i].uninit(handle);
165 xi_uninit(handle->xihandle);
166 free(handle);
167 }
168 }
170 xenstat_node *xenstat_get_node(xenstat_handle * handle, unsigned int flags)
171 {
172 #define DOMAIN_CHUNK_SIZE 256
173 xenstat_node *node;
174 dom0_physinfo_t physinfo;
175 dom0_getdomaininfo_t domaininfo[DOMAIN_CHUNK_SIZE];
176 unsigned int num_domains, new_domains;
177 unsigned int i;
179 /* Create the node */
180 node = (xenstat_node *) calloc(1, sizeof(xenstat_node));
181 if (node == NULL)
182 return NULL;
184 /* Store the handle in the node for later access */
185 node->handle = handle;
187 /* Get information about the physical system */
188 if (xi_get_physinfo(handle->xihandle, &physinfo) < 0) {
189 free(node);
190 return NULL;
191 }
193 node->cpu_hz = ((unsigned long long)physinfo.cpu_khz) * 1000ULL;
194 node->num_cpus =
195 (physinfo.threads_per_core * physinfo.cores_per_socket *
196 physinfo.sockets_per_node * physinfo.nr_nodes);
197 node->tot_mem = ((unsigned long long)physinfo.total_pages)
198 * handle->page_size;
199 node->free_mem = ((unsigned long long)physinfo.free_pages)
200 * handle->page_size;
202 /* malloc(0) is not portable, so allocate a single domain. This will
203 * be resized below. */
204 node->domains = malloc(sizeof(xenstat_domain));
205 if (node->domains == NULL) {
206 free(node);
207 return NULL;
208 }
210 num_domains = 0;
211 do {
212 xenstat_domain *domain;
214 new_domains = xi_get_domaininfolist(handle->xihandle,
215 domaininfo, num_domains,
216 DOMAIN_CHUNK_SIZE);
218 node->domains = realloc(node->domains,
219 (num_domains + new_domains)
220 * sizeof(xenstat_domain));
221 if (node->domains == NULL) {
222 free(node);
223 return NULL;
224 }
226 domain = node->domains + num_domains;
228 for (i = 0; i < new_domains; i++) {
229 /* Fill in domain using domaininfo[i] */
230 domain->id = domaininfo[i].domain;
231 domain->state = domaininfo[i].flags;
232 domain->cpu_ns = domaininfo[i].cpu_time;
233 domain->num_vcpus = domaininfo[i].n_vcpu;
234 domain->vcpus = NULL;
235 domain->cur_mem =
236 ((unsigned long long)domaininfo[i].tot_pages)
237 * handle->page_size;
238 domain->max_mem =
239 domaininfo[i].max_pages == UINT_MAX
240 ? (unsigned long long)-1
241 : (unsigned long long)(domaininfo[i].max_pages
242 * handle->page_size);
243 domain->ssid = domaininfo[i].ssidref;
244 domain->num_networks = 0;
245 domain->networks = NULL;
247 domain++;
248 }
249 num_domains += new_domains;
250 } while (new_domains == DOMAIN_CHUNK_SIZE);
251 node->num_domains = num_domains;
253 /* Run all the extra data collectors requested */
254 node->flags = 0;
255 for (i = 0; i < NUM_COLLECTORS; i++) {
256 if ((flags & collectors[i].flag) == collectors[i].flag) {
257 node->flags |= collectors[i].flag;
258 if(collectors[i].collect(node) == 0) {
259 xenstat_free_node(node);
260 return NULL;
261 }
262 }
263 }
265 return node;
266 }
268 void xenstat_free_node(xenstat_node * node)
269 {
270 int i;
272 if (node) {
273 if (node->domains) {
274 for (i = 0; i < NUM_COLLECTORS; i++)
275 if((node->flags & collectors[i].flag)
276 == collectors[i].flag)
277 collectors[i].free(node);
278 free(node->domains);
279 }
280 free(node);
281 }
282 }
284 xenstat_domain *xenstat_node_domain(xenstat_node * node, unsigned int domid)
285 {
286 unsigned int i;
288 /* FIXME: binary search */
289 /* Find the appropriate domain entry in the node struct. */
290 for (i = 0; i < node->num_domains; i++) {
291 if (node->domains[i].id == domid)
292 return &(node->domains[i]);
293 }
294 return NULL;
295 }
297 xenstat_domain *xenstat_node_domain_by_index(xenstat_node * node,
298 unsigned int index)
299 {
300 if (0 <= index && index < node->num_domains)
301 return &(node->domains[index]);
302 return NULL;
303 }
305 const char *xenstat_node_xen_version(xenstat_node * node)
306 {
307 return node->handle->xen_version;
308 }
310 unsigned long long xenstat_node_tot_mem(xenstat_node * node)
311 {
312 return node->tot_mem;
313 }
315 unsigned long long xenstat_node_free_mem(xenstat_node * node)
316 {
317 return node->free_mem;
318 }
320 unsigned int xenstat_node_num_domains(xenstat_node * node)
321 {
322 return node->num_domains;
323 }
325 unsigned int xenstat_node_num_cpus(xenstat_node * node)
326 {
327 return node->num_cpus;
328 }
330 /* Get information about the CPU speed */
331 unsigned long long xenstat_node_cpu_hz(xenstat_node * node)
332 {
333 return node->cpu_hz;
334 }
336 /* Get the domain ID for this domain */
337 unsigned xenstat_domain_id(xenstat_domain * domain)
338 {
339 return domain->id;
340 }
342 /* Get information about how much CPU time has been used */
343 unsigned long long xenstat_domain_cpu_ns(xenstat_domain * domain)
344 {
345 return domain->cpu_ns;
346 }
348 /* Find the number of VCPUs allocated to a domain */
349 unsigned int xenstat_domain_num_vcpus(xenstat_domain * domain)
350 {
351 return domain->num_vcpus;
352 }
354 xenstat_vcpu *xenstat_domain_vcpu(xenstat_domain * domain, unsigned int vcpu)
355 {
356 if (0 <= vcpu && vcpu < domain->num_vcpus)
357 return &(domain->vcpus[vcpu]);
358 return NULL;
359 }
361 /* Find the current memory reservation for this domain */
362 unsigned long long xenstat_domain_cur_mem(xenstat_domain * domain)
363 {
364 return domain->cur_mem;
365 }
367 /* Find the maximum memory reservation for this domain */
368 unsigned long long xenstat_domain_max_mem(xenstat_domain * domain)
369 {
370 return domain->max_mem;
371 }
373 /* Find the domain's SSID */
374 unsigned int xenstat_domain_ssid(xenstat_domain * domain)
375 {
376 return domain->ssid;
377 }
379 /* Get domain states */
380 unsigned int xenstat_domain_dying(xenstat_domain * domain)
381 {
382 return (domain->state & DOMFLAGS_DYING) == DOMFLAGS_DYING;
383 }
385 unsigned int xenstat_domain_crashed(xenstat_domain * domain)
386 {
387 return ((domain->state & DOMFLAGS_SHUTDOWN) == DOMFLAGS_SHUTDOWN)
388 && (((domain->state >> DOMFLAGS_SHUTDOWNSHIFT)
389 & DOMFLAGS_SHUTDOWNMASK) == SHUTDOWN_crash);
390 }
392 unsigned int xenstat_domain_shutdown(xenstat_domain * domain)
393 {
394 return ((domain->state & DOMFLAGS_SHUTDOWN) == DOMFLAGS_SHUTDOWN)
395 && (((domain->state >> DOMFLAGS_SHUTDOWNSHIFT)
396 & DOMFLAGS_SHUTDOWNMASK) != SHUTDOWN_crash);
397 }
399 unsigned int xenstat_domain_paused(xenstat_domain * domain)
400 {
401 return (domain->state & DOMFLAGS_PAUSED) == DOMFLAGS_PAUSED;
402 }
404 unsigned int xenstat_domain_blocked(xenstat_domain * domain)
405 {
406 return (domain->state & DOMFLAGS_BLOCKED) == DOMFLAGS_BLOCKED;
407 }
409 unsigned int xenstat_domain_running(xenstat_domain * domain)
410 {
411 return (domain->state & DOMFLAGS_RUNNING) == DOMFLAGS_RUNNING;
412 }
414 /* Get the number of networks for a given domain */
415 unsigned int xenstat_domain_num_networks(xenstat_domain * domain)
416 {
417 return domain->num_networks;
418 }
420 /* Get the network handle to obtain network stats */
421 xenstat_network *xenstat_domain_network(xenstat_domain * domain,
422 unsigned int network)
423 {
424 if (domain->networks && 0 <= network && network < domain->num_networks)
425 return &(domain->networks[network]);
426 return NULL;
427 }
429 /*
430 * VCPU functions
431 */
432 /* Collect information about VCPUs */
433 static int xenstat_collect_vcpus(xenstat_node * node)
434 {
435 unsigned int i, vcpu;
436 /* Fill in VCPU information */
437 for (i = 0; i < node->num_domains; i++) {
438 node->domains[i].vcpus = malloc(node->domains[i].num_vcpus
439 * sizeof(xenstat_vcpu));
440 if (node->domains[i].vcpus == NULL)
441 return 0;
443 for (vcpu = 0; vcpu < node->domains[i].num_vcpus; vcpu++) {
444 /* FIXME: need to be using a more efficient mechanism*/
445 long long vcpu_time;
446 vcpu_time = xi_get_vcpu_usage(node->handle->xihandle,
447 node->domains[i].id,
448 vcpu);
449 if (vcpu_time < 0)
450 return 0;
451 node->domains[i].vcpus[vcpu].ns = vcpu_time;
452 }
453 }
454 return 1;
455 }
457 /* Free VCPU information */
458 static void xenstat_free_vcpus(xenstat_node * node)
459 {
460 unsigned int i;
461 for (i = 0; i < node->num_domains; i++)
462 free(node->domains[i].vcpus);
463 }
465 /* Free VCPU information in handle - nothing to do */
466 static void xenstat_uninit_vcpus(xenstat_handle * handle)
467 {
468 }
470 /* Get VCPU usage */
471 unsigned long long xenstat_vcpu_ns(xenstat_vcpu * vcpu)
472 {
473 return vcpu->ns;
474 }
476 /*
477 * Network functions
478 */
480 /* Expected format of /proc/net/dev */
481 static const char PROCNETDEV_HEADER[] =
482 "Inter-| Receive |"
483 " Transmit\n"
484 " face |bytes packets errs drop fifo frame compressed multicast|"
485 "bytes packets errs drop fifo colls carrier compressed\n";
487 /* Collect information about networks */
488 static int xenstat_collect_networks(xenstat_node * node)
489 {
490 /* Open and validate /proc/net/dev if we haven't already */
491 if (node->handle->procnetdev == NULL) {
492 char header[sizeof(PROCNETDEV_HEADER)];
493 node->handle->procnetdev = fopen("/proc/net/dev", "r");
494 if (node->handle->procnetdev == NULL) {
495 perror("Error opening /proc/net/dev");
496 return 1;
497 }
499 /* Validate the format of /proc/net/dev */
500 if (fread(header, sizeof(PROCNETDEV_HEADER) - 1, 1,
501 node->handle->procnetdev) != 1) {
502 perror("Error reading /proc/net/dev header");
503 return 1;
504 }
505 header[sizeof(PROCNETDEV_HEADER) - 1] = '\0';
506 if (strcmp(header, PROCNETDEV_HEADER) != 0) {
507 fprintf(stderr,
508 "Unexpected /proc/net/dev format\n");
509 return 1;
510 }
511 }
513 /* Fill in networks */
514 /* FIXME: optimize this */
515 fseek(node->handle->procnetdev, sizeof(PROCNETDEV_HEADER) - 1,
516 SEEK_SET);
517 while (1) {
518 xenstat_domain *domain;
519 xenstat_network net;
520 unsigned int domid;
521 int ret = fscanf(node->handle->procnetdev,
522 "vif%u.%u:%llu%llu%llu%llu%*u%*u%*u%*u"
523 "%llu%llu%llu%llu%*u%*u%*u%*u\n",
524 &domid, &net.id,
525 &net.tbytes, &net.tpackets, &net.terrs,
526 &net.tdrop,
527 &net.rbytes, &net.rpackets, &net.rerrs,
528 &net.rdrop);
529 if (ret == EOF)
530 break;
531 if (ret != 10) {
532 unsigned int c;
533 do {
534 c = fgetc(node->handle->procnetdev);
535 } while (c != '\n' && c != EOF);
536 if (c == EOF)
537 break;
538 continue;
539 }
541 /* FIXME: this does a search for the domid */
542 domain = xenstat_node_domain(node, domid);
543 if (domain == NULL) {
544 fprintf(stderr,
545 "Found interface vif%u.%u but domain %u"
546 " does not exist.\n", domid, net.id,
547 domid);
548 continue;
549 }
550 if (domain->networks == NULL) {
551 domain->num_networks = 1;
552 domain->networks = malloc(sizeof(xenstat_network));
553 } else {
554 domain->num_networks++;
555 domain->networks =
556 realloc(domain->networks,
557 domain->num_networks *
558 sizeof(xenstat_network));
559 }
560 if (domain->networks == NULL)
561 return 1;
562 domain->networks[domain->num_networks - 1] = net;
563 }
565 return 1;
566 }
568 /* Free network information */
569 static void xenstat_free_networks(xenstat_node * node)
570 {
571 unsigned int i;
572 for (i = 0; i < node->num_domains; i++)
573 free(node->domains[i].networks);
574 }
576 /* Free network information in handle */
577 static void xenstat_uninit_networks(xenstat_handle * handle)
578 {
579 if(handle->procnetdev)
580 fclose(handle->procnetdev);
581 }
583 /* Get the network ID */
584 unsigned int xenstat_network_id(xenstat_network * network)
585 {
586 return network->id;
587 }
589 /* Get the number of receive bytes */
590 unsigned long long xenstat_network_rbytes(xenstat_network * network)
591 {
592 return network->rbytes;
593 }
595 /* Get the number of receive packets */
596 unsigned long long xenstat_network_rpackets(xenstat_network * network)
597 {
598 return network->rpackets;
599 }
601 /* Get the number of receive errors */
602 unsigned long long xenstat_network_rerrs(xenstat_network * network)
603 {
604 return network->rerrs;
605 }
607 /* Get the number of receive drops */
608 unsigned long long xenstat_network_rdrop(xenstat_network * network)
609 {
610 return network->rdrop;
611 }
613 /* Get the number of transmit bytes */
614 unsigned long long xenstat_network_tbytes(xenstat_network * network)
615 {
616 return network->tbytes;
617 }
619 /* Get the number of transmit packets */
620 unsigned long long xenstat_network_tpackets(xenstat_network * network)
621 {
622 return network->tpackets;
623 }
625 /* Get the number of transmit errors */
626 unsigned long long xenstat_network_terrs(xenstat_network * network)
627 {
628 return network->terrs;
629 }
631 /* Get the number of transmit dropped packets */
632 unsigned long long xenstat_network_tdrop(xenstat_network * network)
633 {
634 return network->tdrop;
635 }
637 /*
638 * Xen version functions
639 */
641 /* Collect Xen version information */
642 static int xenstat_collect_xen_version(xenstat_node * node)
643 {
644 long vnum = 0;
645 xen_extraversion_t version;
647 /* Collect Xen version information if not already collected */
648 if (node->handle->xen_version[0] == '\0') {
649 /* Get the Xen version number and extraversion string */
650 if (xi_get_xen_version(node->handle->xihandle,
651 &vnum, &version) < 0)
652 return 0;
653 /* Format the version information as a string and store it */
654 snprintf(node->handle->xen_version, VERSION_SIZE, "%ld.%ld%s",
655 ((vnum >> 16) & 0xFFFF), vnum & 0xFFFF, version);
656 }
658 return 1;
659 }
661 /* Free Xen version information in node - nothing to do */
662 static void xenstat_free_xen_version(xenstat_node * node)
663 {
664 }
666 /* Free Xen version information in handle - nothing to do */
667 static void xenstat_uninit_xen_version(xenstat_handle * handle)
668 {
669 }