ia64/xen-unstable

view tools/xenstat/libxenstat/src/xenstat.c @ 6377:63cc61fafb28

Add a newline to the fscanf format string used to parse /proc/net/dev entries, to avoid an additional read and bad-entry-skip just to eat the newline.
author josht@us.ibm.com
date Tue Aug 23 00:28:50 2005 +0100 (2005-08-23)
parents 6893bc5cc225
children 79df8d5fc424
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"
26 /*
27 * Types
28 */
29 struct xenstat_handle {
30 xi_handle *xihandle;
31 int page_size;
32 FILE *procnetdev;
33 };
35 struct xenstat_node {
36 unsigned int flags;
37 unsigned long long cpu_hz;
38 unsigned int num_cpus;
39 unsigned long long tot_mem;
40 unsigned long long free_mem;
41 unsigned int num_domains;
42 xenstat_domain *domains; /* Array of length num_domains */
43 };
45 struct xenstat_domain {
46 unsigned int id;
47 unsigned int state;
48 unsigned long long cpu_ns;
49 unsigned int num_vcpus;
50 xenstat_vcpu *vcpus; /* Array of length num_vcpus */
51 unsigned long long cur_mem; /* Current memory reservation */
52 unsigned long long max_mem; /* Total memory allowed */
53 unsigned int ssid;
54 unsigned int num_networks;
55 xenstat_network *networks; /* Array of length num_networks */
56 };
58 struct xenstat_vcpu {
59 unsigned long long ns;
60 };
62 struct xenstat_network {
63 unsigned int id;
64 /* Received */
65 unsigned long long rbytes;
66 unsigned long long rpackets;
67 unsigned long long rerrs;
68 unsigned long long rdrop;
69 /* Transmitted */
70 unsigned long long tbytes;
71 unsigned long long tpackets;
72 unsigned long long terrs;
73 unsigned long long tdrop;
74 };
76 /*
77 * Data-collection types
78 */
79 /* Called to collect the information for the node and all the domains on
80 * it. When called, the domain information has already been collected. */
81 typedef int (*xenstat_collect_func)(xenstat_handle * handle,
82 xenstat_node * node);
83 /* Called to free the information collected by the collect function. The free
84 * function will only be called on a xenstat_node if that node includes
85 * information collected by the corresponding collector. */
86 typedef void (*xenstat_free_func)(xenstat_node * node);
87 /* Called to free any information stored in the handle. Note the lack of a
88 * matching init function; the collect functions should initialize on first
89 * use. Also, the uninit function must handle the case that the collector has
90 * never been initialized. */
91 typedef void (*xenstat_uninit_func)(xenstat_handle * handle);
92 typedef struct xenstat_collector {
93 unsigned int flag;
94 xenstat_collect_func collect;
95 xenstat_free_func free;
96 xenstat_uninit_func uninit;
97 } xenstat_collector;
99 static int xenstat_collect_vcpus(xenstat_handle * handle,
100 xenstat_node * node);
101 static int xenstat_collect_networks(xenstat_handle * handle,
102 xenstat_node * node);
103 static void xenstat_free_vcpus(xenstat_node * node);
104 static void xenstat_free_networks(xenstat_node * node);
105 static void xenstat_uninit_vcpus(xenstat_handle * handle);
106 static void xenstat_uninit_networks(xenstat_handle * handle);
108 static xenstat_collector collectors[] = {
109 { XENSTAT_VCPU, xenstat_collect_vcpus,
110 xenstat_free_vcpus, xenstat_uninit_vcpus },
111 { XENSTAT_NETWORK, xenstat_collect_networks,
112 xenstat_free_networks, xenstat_uninit_networks }
113 };
115 #define NUM_COLLECTORS (sizeof(collectors)/sizeof(xenstat_collector))
117 /*
118 * libxenstat API
119 */
120 xenstat_handle *xenstat_init()
121 {
122 xenstat_handle *handle;
124 handle = (xenstat_handle *) calloc(1, sizeof(xenstat_handle));
125 if (handle == NULL)
126 return NULL;
128 #if defined(PAGESIZE)
129 handle->page_size = PAGESIZE;
130 #elif defined(PAGE_SIZE)
131 handle->page_size = PAGE_SIZE;
132 #else
133 handle->page_size = sysconf(_SC_PAGE_SIZE);
134 if (handle->page_size < 0) {
135 perror("Failed to retrieve page size.");
136 free(handle);
137 return NULL;
138 }
139 #endif
141 handle->xihandle = xi_init();
142 if (handle->xihandle == NULL) {
143 perror("xi_init");
144 free(handle);
145 return NULL;
146 }
148 return handle;
149 }
151 void xenstat_uninit(xenstat_handle * handle)
152 {
153 unsigned int i;
154 if (handle) {
155 for (i = 0; i < NUM_COLLECTORS; i++)
156 collectors[i].uninit(handle);
157 xi_uninit(handle->xihandle);
158 free(handle);
159 }
160 }
162 xenstat_node *xenstat_get_node(xenstat_handle * handle, unsigned int flags)
163 {
164 #define DOMAIN_CHUNK_SIZE 256
165 xenstat_node *node;
166 dom0_physinfo_t physinfo;
167 dom0_getdomaininfo_t domaininfo[DOMAIN_CHUNK_SIZE];
168 unsigned int num_domains, new_domains;
169 unsigned int i;
171 /* Create the node */
172 node = (xenstat_node *) calloc(1, sizeof(xenstat_node));
173 if (node == NULL)
174 return NULL;
176 /* Get information about the physical system */
177 if (xi_get_physinfo(handle->xihandle, &physinfo) < 0) {
178 free(node);
179 return NULL;
180 }
182 node->cpu_hz = ((unsigned long long)physinfo.cpu_khz) * 1000ULL;
183 node->num_cpus =
184 (physinfo.threads_per_core * physinfo.cores_per_socket *
185 physinfo.sockets_per_node * physinfo.nr_nodes);
186 node->tot_mem = ((unsigned long long)physinfo.total_pages)
187 * handle->page_size;
188 node->free_mem = ((unsigned long long)physinfo.free_pages)
189 * handle->page_size;
191 /* malloc(0) is not portable, so allocate a single domain. This will
192 * be resized below. */
193 node->domains = malloc(sizeof(xenstat_domain));
194 if (node->domains == NULL) {
195 free(node);
196 return NULL;
197 }
199 num_domains = 0;
200 do {
201 xenstat_domain *domain;
203 new_domains = xi_get_domaininfolist(handle->xihandle,
204 domaininfo, num_domains,
205 DOMAIN_CHUNK_SIZE);
207 node->domains = realloc(node->domains,
208 (num_domains + new_domains)
209 * sizeof(xenstat_domain));
210 if (node->domains == NULL) {
211 free(node);
212 return NULL;
213 }
215 domain = node->domains + num_domains;
217 for (i = 0; i < new_domains; i++) {
218 /* Fill in domain using domaininfo[i] */
219 domain->id = domaininfo[i].domain;
220 domain->state = domaininfo[i].flags;
221 domain->cpu_ns = domaininfo[i].cpu_time;
222 domain->num_vcpus = domaininfo[i].n_vcpu;
223 domain->vcpus = NULL;
224 domain->cur_mem =
225 ((unsigned long long)domaininfo[i].tot_pages)
226 * handle->page_size;
227 domain->max_mem =
228 domaininfo[i].max_pages == UINT_MAX
229 ? (unsigned long long)-1
230 : (unsigned long long)(domaininfo[i].max_pages
231 * handle->page_size);
232 domain->ssid = domaininfo[i].ssidref;
233 domain->num_networks = 0;
234 domain->networks = NULL;
236 domain++;
237 }
238 num_domains += new_domains;
239 } while (new_domains == DOMAIN_CHUNK_SIZE);
240 node->num_domains = num_domains;
242 /* Run all the extra data collectors requested */
243 node->flags = 0;
244 for (i = 0; i < NUM_COLLECTORS; i++) {
245 if ((flags & collectors[i].flag) == collectors[i].flag) {
246 node->flags |= collectors[i].flag;
247 if(collectors[i].collect(handle, node) == 0) {
248 xenstat_free_node(node);
249 return NULL;
250 }
251 }
252 }
254 return node;
255 }
257 void xenstat_free_node(xenstat_node * node)
258 {
259 int i;
261 if (node) {
262 if (node->domains) {
263 for (i = 0; i < NUM_COLLECTORS; i++)
264 if((node->flags & collectors[i].flag)
265 == collectors[i].flag)
266 collectors[i].free(node);
267 free(node->domains);
268 }
269 free(node);
270 }
271 }
273 xenstat_domain *xenstat_node_domain(xenstat_node * node, unsigned int domid)
274 {
275 unsigned int i;
277 /* FIXME: binary search */
278 /* Find the appropriate domain entry in the node struct. */
279 for (i = 0; i < node->num_domains; i++) {
280 if (node->domains[i].id == domid)
281 return &(node->domains[i]);
282 }
283 return NULL;
284 }
286 xenstat_domain *xenstat_node_domain_by_index(xenstat_node * node,
287 unsigned int index)
288 {
289 if (0 <= index && index < node->num_domains)
290 return &(node->domains[index]);
291 return NULL;
292 }
294 unsigned long long xenstat_node_tot_mem(xenstat_node * node)
295 {
296 return node->tot_mem;
297 }
299 unsigned long long xenstat_node_free_mem(xenstat_node * node)
300 {
301 return node->free_mem;
302 }
304 unsigned int xenstat_node_num_domains(xenstat_node * node)
305 {
306 return node->num_domains;
307 }
309 unsigned int xenstat_node_num_cpus(xenstat_node * node)
310 {
311 return node->num_cpus;
312 }
314 /* Get information about the CPU speed */
315 unsigned long long xenstat_node_cpu_hz(xenstat_node * node)
316 {
317 return node->cpu_hz;
318 }
320 /* Get the domain ID for this domain */
321 unsigned xenstat_domain_id(xenstat_domain * domain)
322 {
323 return domain->id;
324 }
326 /* Get information about how much CPU time has been used */
327 unsigned long long xenstat_domain_cpu_ns(xenstat_domain * domain)
328 {
329 return domain->cpu_ns;
330 }
332 /* Find the number of VCPUs allocated to a domain */
333 unsigned int xenstat_domain_num_vcpus(xenstat_domain * domain)
334 {
335 return domain->num_vcpus;
336 }
338 xenstat_vcpu *xenstat_domain_vcpu(xenstat_domain * domain, unsigned int vcpu)
339 {
340 if (0 <= vcpu && vcpu < domain->num_vcpus)
341 return &(domain->vcpus[vcpu]);
342 return NULL;
343 }
345 /* Find the current memory reservation for this domain */
346 unsigned long long xenstat_domain_cur_mem(xenstat_domain * domain)
347 {
348 return domain->cur_mem;
349 }
351 /* Find the maximum memory reservation for this domain */
352 unsigned long long xenstat_domain_max_mem(xenstat_domain * domain)
353 {
354 return domain->max_mem;
355 }
357 /* Find the domain's SSID */
358 unsigned int xenstat_domain_ssid(xenstat_domain * domain)
359 {
360 return domain->ssid;
361 }
363 /* Get domain states */
364 unsigned int xenstat_domain_dying(xenstat_domain * domain)
365 {
366 return (domain->state & DOMFLAGS_DYING) == DOMFLAGS_DYING;
367 }
369 unsigned int xenstat_domain_crashed(xenstat_domain * domain)
370 {
371 return ((domain->state & DOMFLAGS_SHUTDOWN) == DOMFLAGS_SHUTDOWN)
372 && (((domain->state >> DOMFLAGS_SHUTDOWNSHIFT)
373 & DOMFLAGS_SHUTDOWNMASK) == SHUTDOWN_crash);
374 }
376 unsigned int xenstat_domain_shutdown(xenstat_domain * domain)
377 {
378 return ((domain->state & DOMFLAGS_SHUTDOWN) == DOMFLAGS_SHUTDOWN)
379 && (((domain->state >> DOMFLAGS_SHUTDOWNSHIFT)
380 & DOMFLAGS_SHUTDOWNMASK) != SHUTDOWN_crash);
381 }
383 unsigned int xenstat_domain_paused(xenstat_domain * domain)
384 {
385 return (domain->state & DOMFLAGS_PAUSED) == DOMFLAGS_PAUSED;
386 }
388 unsigned int xenstat_domain_blocked(xenstat_domain * domain)
389 {
390 return (domain->state & DOMFLAGS_BLOCKED) == DOMFLAGS_BLOCKED;
391 }
393 unsigned int xenstat_domain_running(xenstat_domain * domain)
394 {
395 return (domain->state & DOMFLAGS_RUNNING) == DOMFLAGS_RUNNING;
396 }
398 /* Get the number of networks for a given domain */
399 unsigned int xenstat_domain_num_networks(xenstat_domain * domain)
400 {
401 return domain->num_networks;
402 }
404 /* Get the network handle to obtain network stats */
405 xenstat_network *xenstat_domain_network(xenstat_domain * domain,
406 unsigned int network)
407 {
408 if (domain->networks && 0 <= network && network < domain->num_networks)
409 return &(domain->networks[network]);
410 return NULL;
411 }
413 /*
414 * VCPU functions
415 */
416 /* Collect information about VCPUs */
417 static int xenstat_collect_vcpus(xenstat_handle * handle, xenstat_node * node)
418 {
419 unsigned int i, vcpu;
420 /* Fill in VCPU information */
421 for (i = 0; i < node->num_domains; i++) {
422 node->domains[i].vcpus = malloc(node->domains[i].num_vcpus
423 * sizeof(xenstat_vcpu));
424 if (node->domains[i].vcpus == NULL)
425 return 0;
427 for (vcpu = 0; vcpu < node->domains[i].num_vcpus; vcpu++) {
428 /* FIXME: need to be using a more efficient mechanism*/
429 long long vcpu_time;
430 vcpu_time =
431 xi_get_vcpu_usage(handle->xihandle,
432 node->domains[i].id,
433 vcpu);
434 if (vcpu_time < 0)
435 return 0;
436 node->domains[i].vcpus[vcpu].ns = vcpu_time;
437 }
438 }
439 return 1;
440 }
442 /* Free VCPU information */
443 static void xenstat_free_vcpus(xenstat_node * node)
444 {
445 unsigned int i;
446 for (i = 0; i < node->num_domains; i++)
447 free(node->domains[i].vcpus);
448 }
450 /* Free VCPU information in handle - nothing to do */
451 static void xenstat_uninit_vcpus(xenstat_handle * handle)
452 {
453 }
455 /* Get VCPU usage */
456 unsigned long long xenstat_vcpu_ns(xenstat_vcpu * vcpu)
457 {
458 return vcpu->ns;
459 }
461 /*
462 * Network functions
463 */
465 /* Expected format of /proc/net/dev */
466 static const char PROCNETDEV_HEADER[] =
467 "Inter-| Receive |"
468 " Transmit\n"
469 " face |bytes packets errs drop fifo frame compressed multicast|"
470 "bytes packets errs drop fifo colls carrier compressed\n";
472 /* Collect information about networks */
473 static int xenstat_collect_networks(xenstat_handle * handle,
474 xenstat_node * node)
475 {
476 /* Open and validate /proc/net/dev if we haven't already */
477 if (handle->procnetdev == NULL) {
478 char header[sizeof(PROCNETDEV_HEADER)];
479 handle->procnetdev = fopen("/proc/net/dev", "r");
480 if (handle->procnetdev == NULL) {
481 perror("Error opening /proc/net/dev");
482 return 1;
483 }
485 /* Validate the format of /proc/net/dev */
486 if (fread(header, sizeof(PROCNETDEV_HEADER) - 1, 1,
487 handle->procnetdev) != 1) {
488 perror("Error reading /proc/net/dev header");
489 return 1;
490 }
491 header[sizeof(PROCNETDEV_HEADER) - 1] = '\0';
492 if (strcmp(header, PROCNETDEV_HEADER) != 0) {
493 fprintf(stderr,
494 "Unexpected /proc/net/dev format\n");
495 return 1;
496 }
497 }
499 /* Fill in networks */
500 /* FIXME: optimize this */
501 fseek(handle->procnetdev, sizeof(PROCNETDEV_HEADER) - 1, SEEK_SET);
502 while (1) {
503 xenstat_domain *domain;
504 xenstat_network net;
505 unsigned int domid;
506 int ret = fscanf(handle->procnetdev,
507 "vif%u.%u:%llu%llu%llu%llu%*u%*u%*u%*u"
508 "%llu%llu%llu%llu%*u%*u%*u%*u\n",
509 &domid, &net.id,
510 &net.tbytes, &net.tpackets, &net.terrs,
511 &net.tdrop,
512 &net.rbytes, &net.rpackets, &net.rerrs,
513 &net.rdrop);
514 if (ret == EOF)
515 break;
516 if (ret != 10) {
517 unsigned int c;
518 do {
519 c = fgetc(handle->procnetdev);
520 } while (c != '\n' && c != EOF);
521 if (c == EOF)
522 break;
523 continue;
524 }
526 /* FIXME: this does a search for the domid */
527 domain = xenstat_node_domain(node, domid);
528 if (domain == NULL) {
529 fprintf(stderr,
530 "Found interface vif%u.%u but domain %u"
531 " does not exist.\n", domid, net.id,
532 domid);
533 continue;
534 }
535 if (domain->networks == NULL) {
536 domain->num_networks = 1;
537 domain->networks = malloc(sizeof(xenstat_network));
538 } else {
539 domain->num_networks++;
540 domain->networks =
541 realloc(domain->networks,
542 domain->num_networks *
543 sizeof(xenstat_network));
544 }
545 if (domain->networks == NULL)
546 return 1;
547 domain->networks[domain->num_networks - 1] = net;
548 }
550 return 1;
551 }
553 /* Free network information */
554 static void xenstat_free_networks(xenstat_node * node)
555 {
556 unsigned int i;
557 for (i = 0; i < node->num_domains; i++)
558 free(node->domains[i].networks);
559 }
561 /* Free network information in handle */
562 static void xenstat_uninit_networks(xenstat_handle * handle)
563 {
564 if(handle->procnetdev)
565 fclose(handle->procnetdev);
566 }
568 /* Get the network ID */
569 unsigned int xenstat_network_id(xenstat_network * network)
570 {
571 return network->id;
572 }
574 /* Get the number of receive bytes */
575 unsigned long long xenstat_network_rbytes(xenstat_network * network)
576 {
577 return network->rbytes;
578 }
580 /* Get the number of receive packets */
581 unsigned long long xenstat_network_rpackets(xenstat_network * network)
582 {
583 return network->rpackets;
584 }
586 /* Get the number of receive errors */
587 unsigned long long xenstat_network_rerrs(xenstat_network * network)
588 {
589 return network->rerrs;
590 }
592 /* Get the number of receive drops */
593 unsigned long long xenstat_network_rdrop(xenstat_network * network)
594 {
595 return network->rdrop;
596 }
598 /* Get the number of transmit bytes */
599 unsigned long long xenstat_network_tbytes(xenstat_network * network)
600 {
601 return network->tbytes;
602 }
604 /* Get the number of transmit packets */
605 unsigned long long xenstat_network_tpackets(xenstat_network * network)
606 {
607 return network->tpackets;
608 }
610 /* Get the number of transmit errors */
611 unsigned long long xenstat_network_terrs(xenstat_network * network)
612 {
613 return network->terrs;
614 }
616 /* Get the number of transmit dropped packets */
617 unsigned long long xenstat_network_tdrop(xenstat_network * network)
618 {
619 return network->tdrop;
620 }