ia64/xen-unstable

view tools/xentrace/xentrace.c @ 17119:644ddc6514b1

xentrace: Fix --discard-buffers option
Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Feb 26 14:38:39 2008 +0000 (2008-02-26)
parents 88818d55e95a
children 803c964e3ede
line source
1 /******************************************************************************
2 * tools/xentrace/xentrace.c
3 *
4 * Tool for collecting trace buffer data from Xen.
5 *
6 * Copyright (C) 2004 by Intel Research Cambridge
7 *
8 * Author: Mark Williamson, mark.a.williamson@intel.com
9 * Date: February 2004
10 */
12 #include <time.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <sys/mman.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <signal.h>
22 #include <inttypes.h>
23 #include <string.h>
24 #include <getopt.h>
25 #include <assert.h>
26 #include <sys/poll.h>
28 #include <xen/xen.h>
29 #include <xen/trace.h>
31 #include <xenctrl.h>
33 #define PERROR(_m, _a...) \
34 do { \
35 int __saved_errno = errno; \
36 fprintf(stderr, "ERROR: " _m " (%d = %s)\n" , ## _a , \
37 __saved_errno, strerror(__saved_errno)); \
38 errno = __saved_errno; \
39 } while (0)
42 /***** Compile time configuration of defaults ********************************/
44 /* sleep for this long (milliseconds) between checking the trace buffers */
45 #define POLL_SLEEP_MILLIS 100
47 #define DEFAULT_TBUF_SIZE 20
48 /***** The code **************************************************************/
50 typedef struct settings_st {
51 char *outfile;
52 unsigned long poll_sleep; /* milliseconds to sleep between polls */
53 uint32_t evt_mask;
54 uint32_t cpu_mask;
55 unsigned long tbuf_size;
56 uint8_t discard:1;
57 } settings_t;
59 settings_t opts;
61 int interrupted = 0; /* gets set if we get a SIGHUP */
63 static int xc_handle = -1;
64 static int event_fd = -1;
65 static int virq_port = -1;
67 void close_handler(int signal)
68 {
69 interrupted = 1;
70 }
72 /**
73 * write_buffer - write a section of the trace buffer
74 * @cpu - source buffer CPU ID
75 * @start
76 * @size - size of write (may be less than total window size)
77 * @total_size - total size of the window (0 on 2nd write of wrapped windows)
78 * @out - output stream
79 *
80 * Outputs the trace buffer to a filestream, prepending the CPU and size
81 * of the buffer write.
82 */
83 void write_buffer(unsigned int cpu, unsigned char *start, int size,
84 int total_size, int outfd)
85 {
86 size_t written = 0;
88 /* Write a CPU_BUF record on each buffer "window" written. Wrapped
89 * windows may involve two writes, so only write the record on the
90 * first write. */
91 if ( total_size != 0 )
92 {
93 struct {
94 uint32_t header;
95 struct {
96 unsigned cpu;
97 unsigned byte_count;
98 } extra;
99 } rec;
101 rec.header = TRC_TRACE_CPU_CHANGE
102 | ((sizeof(rec.extra)/sizeof(uint32_t)) << TRACE_EXTRA_SHIFT);
103 rec.extra.cpu = cpu;
104 rec.extra.byte_count = total_size;
106 written = write(outfd, &rec, sizeof(rec));
108 if ( written != sizeof(rec) )
109 {
110 fprintf(stderr, "Cannot write cpu change (write returned %zd)\n",
111 written);
112 goto fail;
113 }
114 }
116 written = write(outfd, start, size);
117 if ( written != size )
118 {
119 fprintf(stderr, "Write failed! (size %d, returned %zd)\n",
120 size, written);
121 goto fail;
122 }
124 return;
126 fail:
127 PERROR("Failed to write trace data");
128 exit(EXIT_FAILURE);
129 }
131 static void get_tbufs(unsigned long *mfn, unsigned long *size)
132 {
133 int ret;
135 if(!opts.tbuf_size)
136 opts.tbuf_size = DEFAULT_TBUF_SIZE;
138 ret = xc_tbuf_enable(xc_handle, opts.tbuf_size, mfn, size);
140 if ( ret != 0 )
141 {
142 perror("Couldn't enable trace buffers");
143 exit(1);
144 }
145 }
147 /**
148 * map_tbufs - memory map Xen trace buffers into user space
149 * @tbufs_mfn: mfn of the trace buffers
150 * @num: number of trace buffers to map
151 * @size: size of each trace buffer
152 *
153 * Maps the Xen trace buffers them into process address space.
154 */
155 struct t_buf *map_tbufs(unsigned long tbufs_mfn, unsigned int num,
156 unsigned long size)
157 {
158 struct t_buf *tbufs_mapped;
160 tbufs_mapped = xc_map_foreign_range(xc_handle, DOMID_XEN,
161 size * num, PROT_READ | PROT_WRITE,
162 tbufs_mfn);
164 if ( tbufs_mapped == 0 )
165 {
166 PERROR("Failed to mmap trace buffers");
167 exit(EXIT_FAILURE);
168 }
170 return tbufs_mapped;
171 }
173 /**
174 * set_mask - set the cpu/event mask in HV
175 * @mask: the new mask
176 * @type: the new mask type,0-event mask, 1-cpu mask
177 *
178 */
179 void set_mask(uint32_t mask, int type)
180 {
181 int ret = 0;
183 if (type == 1) {
184 ret = xc_tbuf_set_cpu_mask(xc_handle, mask);
185 fprintf(stderr, "change cpumask to 0x%x\n", mask);
186 } else if (type == 0) {
187 ret = xc_tbuf_set_evt_mask(xc_handle, mask);
188 fprintf(stderr, "change evtmask to 0x%x\n", mask);
189 }
191 if ( ret != 0 )
192 {
193 PERROR("Failure to get trace buffer pointer from Xen and set the new mask");
194 exit(EXIT_FAILURE);
195 }
196 }
198 /**
199 * init_bufs_ptrs - initialises an array of pointers to the trace buffers
200 * @bufs_mapped: the userspace address where the trace buffers are mapped
201 * @num: number of trace buffers
202 * @size: trace buffer size
203 *
204 * Initialises an array of pointers to individual trace buffers within the
205 * mapped region containing all trace buffers.
206 */
207 struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
208 unsigned long size)
209 {
210 int i;
211 struct t_buf **user_ptrs;
213 user_ptrs = (struct t_buf **)calloc(num, sizeof(struct t_buf *));
214 if ( user_ptrs == NULL )
215 {
216 PERROR( "Failed to allocate memory for buffer pointers\n");
217 exit(EXIT_FAILURE);
218 }
220 /* initialise pointers to the trace buffers - given the size of a trace
221 * buffer and the value of bufs_maped, we can easily calculate these */
222 for ( i = 0; i<num; i++ )
223 user_ptrs[i] = (struct t_buf *)((unsigned long)bufs_mapped + size * i);
225 return user_ptrs;
226 }
229 /**
230 * init_rec_ptrs - initialises data area pointers to locations in user space
231 * @tbufs_mfn: base mfn of the trace buffer area
232 * @tbufs_mapped: user virtual address of base of trace buffer area
233 * @meta: array of user-space pointers to struct t_buf's of metadata
234 * @num: number of trace buffers
235 *
236 * Initialises data area pointers to the locations that data areas have been
237 * mapped in user space. Note that the trace buffer metadata contains machine
238 * pointers - the array returned allows more convenient access to them.
239 */
240 unsigned char **init_rec_ptrs(struct t_buf **meta, unsigned int num)
241 {
242 int i;
243 unsigned char **data;
245 data = calloc(num, sizeof(unsigned char *));
246 if ( data == NULL )
247 {
248 PERROR("Failed to allocate memory for data pointers\n");
249 exit(EXIT_FAILURE);
250 }
252 for ( i = 0; i < num; i++ )
253 data[i] = (unsigned char *)(meta[i] + 1);
255 return data;
256 }
258 /**
259 * get_num_cpus - get the number of logical CPUs
260 */
261 unsigned int get_num_cpus(void)
262 {
263 xc_physinfo_t physinfo = { 0 };
264 int ret;
266 ret = xc_physinfo(xc_handle, &physinfo);
268 if ( ret != 0 )
269 {
270 PERROR("Failure to get logical CPU count from Xen");
271 exit(EXIT_FAILURE);
272 }
274 return physinfo.nr_cpus;
275 }
277 /**
278 * event_init - setup to receive the VIRQ_TBUF event
279 */
280 void event_init(void)
281 {
282 int rc;
284 rc = xc_evtchn_open();
285 if (rc < 0) {
286 perror(xc_get_last_error()->message);
287 exit(EXIT_FAILURE);
288 }
289 event_fd = rc;
291 rc = xc_evtchn_bind_virq(event_fd, VIRQ_TBUF);
292 if (rc == -1) {
293 PERROR("failed to bind to VIRQ port");
294 exit(EXIT_FAILURE);
295 }
296 virq_port = rc;
297 }
299 /**
300 * wait_for_event_or_timeout - sleep for the specified number of milliseconds,
301 * or until an VIRQ_TBUF event occurs
302 */
303 void wait_for_event_or_timeout(unsigned long milliseconds)
304 {
305 int rc;
306 struct pollfd fd = { .fd = event_fd,
307 .events = POLLIN | POLLERR };
308 int port;
310 rc = poll(&fd, 1, milliseconds);
311 if (rc == -1) {
312 if (errno == EINTR)
313 return;
314 PERROR("poll exitted with an error");
315 exit(EXIT_FAILURE);
316 }
318 if (rc == 1) {
319 port = xc_evtchn_pending(event_fd);
320 if (port == -1) {
321 PERROR("failed to read port from evtchn");
322 exit(EXIT_FAILURE);
323 }
324 if (port != virq_port) {
325 fprintf(stderr,
326 "unexpected port returned from evtchn (got %d vs expected %d)\n",
327 port, virq_port);
328 exit(EXIT_FAILURE);
329 }
330 rc = xc_evtchn_unmask(event_fd, port);
331 if (rc == -1) {
332 PERROR("failed to write port to evtchn");
333 exit(EXIT_FAILURE);
334 }
335 }
336 }
339 /**
340 * monitor_tbufs - monitor the contents of tbufs and output to a file
341 * @logfile: the FILE * representing the file to log to
342 */
343 int monitor_tbufs(int outfd)
344 {
345 int i;
347 void *tbufs_mapped; /* pointer to where the tbufs are mapped */
348 struct t_buf **meta; /* pointers to the trace buffer metadata */
349 unsigned char **data; /* pointers to the trace buffer data areas
350 * where they are mapped into user space. */
351 unsigned long tbufs_mfn; /* mfn of the tbufs */
352 unsigned int num; /* number of trace buffers / logical CPUS */
353 unsigned long size; /* size of a single trace buffer */
355 unsigned long data_size;
357 /* prepare to listen for VIRQ_TBUF */
358 event_init();
360 /* get number of logical CPUs (and therefore number of trace buffers) */
361 num = get_num_cpus();
363 /* setup access to trace buffers */
364 get_tbufs(&tbufs_mfn, &size);
365 tbufs_mapped = map_tbufs(tbufs_mfn, num, size);
367 data_size = size - sizeof(struct t_buf);
369 /* build arrays of convenience ptrs */
370 meta = init_bufs_ptrs(tbufs_mapped, num, size);
371 data = init_rec_ptrs(meta, num);
373 if ( opts.discard )
374 for ( i = 0; i < num; i++ )
375 meta[i]->cons = meta[i]->prod;
377 /* now, scan buffers for events */
378 while ( !interrupted )
379 {
380 for ( i = 0; (i < num) && !interrupted; i++ )
381 {
382 unsigned long start_offset, end_offset, window_size, cons, prod;
384 /* Read window information only once. */
385 cons = meta[i]->cons;
386 prod = meta[i]->prod;
387 xen_rmb(); /* read prod, then read item. */
389 if ( cons == prod )
390 continue;
392 assert(cons < 2*data_size);
393 assert(prod < 2*data_size);
395 // NB: if (prod<cons), then (prod-cons)%data_size will not yield
396 // the correct answer because data_size is not a power of 2.
397 if ( prod < cons )
398 window_size = (prod + 2*data_size) - cons;
399 else
400 window_size = prod - cons;
401 assert(window_size > 0);
402 assert(window_size <= data_size);
404 start_offset = cons % data_size;
405 end_offset = prod % data_size;
407 if ( end_offset > start_offset )
408 {
409 /* If window does not wrap, write in one big chunk */
410 write_buffer(i, data[i]+start_offset,
411 window_size,
412 window_size,
413 outfd);
414 }
415 else
416 {
417 /* If wrapped, write in two chunks:
418 * - first, start to the end of the buffer
419 * - second, start of buffer to end of window
420 */
421 write_buffer(i, data[i] + start_offset,
422 data_size - start_offset,
423 window_size,
424 outfd);
425 write_buffer(i, data[i],
426 end_offset,
427 0,
428 outfd);
429 }
431 xen_mb(); /* read buffer, then update cons. */
432 meta[i]->cons = prod;
433 }
435 wait_for_event_or_timeout(opts.poll_sleep);
436 }
438 /* cleanup */
439 free(meta);
440 free(data);
441 /* don't need to munmap - cleanup is automatic */
442 close(outfd);
444 return 0;
445 }
448 /******************************************************************************
449 * Command line handling
450 *****************************************************************************/
452 #define xstr(x) str(x)
453 #define str(x) #x
455 const char *program_version = "xentrace v1.2";
456 const char *program_bug_address = "<mark.a.williamson@intel.com>";
458 void usage(void)
459 {
460 #define USAGE_STR \
461 "Usage: xentrace [OPTION...] [output file]\n" \
462 "Tool to capture Xen trace buffer data\n" \
463 "\n" \
464 " -c, --cpu-mask=c Set cpu-mask\n" \
465 " -e, --evt-mask=e Set evt-mask\n" \
466 " -s, --poll-sleep=p Set sleep time, p, in milliseconds between\n" \
467 " polling the trace buffer for new data\n" \
468 " (default " xstr(POLL_SLEEP_MILLIS) ").\n" \
469 " -S, --trace-buf-size=N Set trace buffer size in pages (default " \
470 xstr(DEFAULT_TBUF_SIZE) ").\n" \
471 " N.B. that the trace buffer cannot be resized.\n" \
472 " if it has already been set this boot cycle,\n" \
473 " this argument will be ignored.\n" \
474 " -D --discard-buffers Discard all records currently in the trace\n" \
475 " buffers before beginning.\n" \
476 " -?, --help Show this message\n" \
477 " -V, --version Print program version\n" \
478 "\n" \
479 "This tool is used to capture trace buffer data from Xen. The\n" \
480 "data is output in a binary format, in the following order:\n" \
481 "\n" \
482 " CPU(uint) TSC(uint64_t) EVENT(uint32_t) D1 D2 D3 D4 D5 (all uint32_t)\n" \
483 "\n" \
484 "The output should be parsed using the tool xentrace_format,\n" \
485 "which can produce human-readable output in ASCII format.\n"
487 printf(USAGE_STR);
488 printf("\nReport bugs to %s\n", program_bug_address);
490 exit(EXIT_FAILURE);
491 }
493 /* convert the argument string pointed to by arg to a long int representation */
494 long argtol(const char *restrict arg, int base)
495 {
496 char *endp;
497 long val;
499 errno = 0;
500 val = strtol(arg, &endp, base);
502 if (errno != 0) {
503 fprintf(stderr, "Invalid option argument: %s\n", arg);
504 fprintf(stderr, "Error: %s\n\n", strerror(errno));
505 usage();
506 } else if (endp == arg || *endp != '\0') {
507 fprintf(stderr, "Invalid option argument: %s\n\n", arg);
508 usage();
509 }
511 return val;
512 }
514 int parse_evtmask(char *arg)
515 {
516 /* search filtering class */
517 if (strcmp(arg, "gen") == 0){
518 opts.evt_mask |= TRC_GEN;
519 } else if(strcmp(arg, "sched") == 0){
520 opts.evt_mask |= TRC_SCHED;
521 } else if(strcmp(arg, "dom0op") == 0){
522 opts.evt_mask |= TRC_DOM0OP;
523 } else if(strcmp(arg, "hvm") == 0){
524 opts.evt_mask |= TRC_HVM;
525 } else if(strcmp(arg, "all") == 0){
526 opts.evt_mask |= TRC_ALL;
527 } else {
528 opts.evt_mask = argtol(arg, 0);
529 }
531 return 0;
532 }
534 /* parse command line arguments */
535 void parse_args(int argc, char **argv)
536 {
537 int option;
538 static struct option long_options[] = {
539 { "log-thresh", required_argument, 0, 't' },
540 { "poll-sleep", required_argument, 0, 's' },
541 { "cpu-mask", required_argument, 0, 'c' },
542 { "evt-mask", required_argument, 0, 'e' },
543 { "trace-buf-size", required_argument, 0, 'S' },
544 { "discard-buffers", no_argument, 0, 'D' },
545 { "help", no_argument, 0, '?' },
546 { "version", no_argument, 0, 'V' },
547 { 0, 0, 0, 0 }
548 };
550 while ( (option = getopt_long(argc, argv, "c:e:s:S:t:?V",
551 long_options, NULL)) != -1)
552 {
553 switch ( option )
554 {
555 case 's': /* set sleep time (given in milliseconds) */
556 opts.poll_sleep = argtol(optarg, 0);
557 break;
559 case 'c': /* set new cpu mask for filtering*/
560 opts.cpu_mask = argtol(optarg, 0);
561 break;
563 case 'e': /* set new event mask for filtering*/
564 parse_evtmask(optarg);
565 break;
567 case 'S': /* set tbuf size (given in pages) */
568 opts.tbuf_size = argtol(optarg, 0);
569 break;
571 case 'V': /* print program version */
572 printf("%s\n", program_version);
573 exit(EXIT_SUCCESS);
574 break;
576 case 'D': /* Discard traces currently in buffer */
577 opts.discard = 1;
578 break;
580 default:
581 usage();
582 }
583 }
585 /* get outfile (required last argument) */
586 if (optind != (argc-1))
587 usage();
589 opts.outfile = argv[optind];
590 }
593 /* *BSD has no O_LARGEFILE */
594 #ifndef O_LARGEFILE
595 #define O_LARGEFILE 0
596 #endif
598 int main(int argc, char **argv)
599 {
600 int outfd = 1, ret;
601 struct sigaction act;
603 opts.outfile = 0;
604 opts.poll_sleep = POLL_SLEEP_MILLIS;
605 opts.evt_mask = 0;
606 opts.cpu_mask = 0;
608 parse_args(argc, argv);
610 xc_handle = xc_interface_open();
611 if ( xc_handle < 0 )
612 {
613 perror(xc_get_last_error()->message);
614 exit(EXIT_FAILURE);
615 }
617 if ( opts.evt_mask != 0 )
618 set_mask(opts.evt_mask, 0);
620 if ( opts.cpu_mask != 0 )
621 set_mask(opts.cpu_mask, 1);
623 if ( opts.outfile )
624 outfd = open(opts.outfile,
625 O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE,
626 0644);
628 if ( outfd < 0 )
629 {
630 perror("Could not open output file");
631 exit(EXIT_FAILURE);
632 }
634 if ( isatty(outfd) )
635 {
636 fprintf(stderr, "Cannot output to a TTY, specify a log file.\n");
637 exit(EXIT_FAILURE);
638 }
640 /* ensure that if we get a signal, we'll do cleanup, then exit */
641 act.sa_handler = close_handler;
642 act.sa_flags = 0;
643 sigemptyset(&act.sa_mask);
644 sigaction(SIGHUP, &act, NULL);
645 sigaction(SIGTERM, &act, NULL);
646 sigaction(SIGINT, &act, NULL);
648 ret = monitor_tbufs(outfd);
650 return ret;
651 }
652 /*
653 * Local variables:
654 * mode: C
655 * c-set-style: "BSD"
656 * c-basic-offset: 4
657 * tab-width: 4
658 * indent-tabs-mode: nil
659 * End:
660 */