direct-io.hg

view tools/xentrace/xentrace.c @ 14146:0a9da94c904a

[TOOLS] Make xentrace help a little more informative.

Signed-off-by: George Dunlap <gdunlap@xensource.com>
author George Dunlap <gdunlap@xensource.com>
date Tue Feb 27 06:06:29 2007 -0500 (2007-02-27)
parents ffe7e8ef2c98
children 6c636bd3f874
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 <argp.h>
22 #include <signal.h>
23 #include <inttypes.h>
24 #include <string.h>
26 #include <xen/xen.h>
27 #include <xen/trace.h>
29 #include <xenctrl.h>
31 #define PERROR(_m, _a...) \
32 do { \
33 int __saved_errno = errno; \
34 fprintf(stderr, "ERROR: " _m " (%d = %s)\n" , ## _a , \
35 __saved_errno, strerror(__saved_errno)); \
36 errno = __saved_errno; \
37 } while (0)
39 extern FILE *stderr;
41 /***** Compile time configuration of defaults ********************************/
43 /* when we've got more records than this waiting, we log it to the output */
44 #define NEW_DATA_THRESH 1
46 /* sleep for this long (milliseconds) between checking the trace buffers */
47 #define POLL_SLEEP_MILLIS 100
49 #define DEFAULT_TBUF_SIZE 20
50 /***** The code **************************************************************/
52 typedef struct settings_st {
53 char *outfile;
54 struct timespec poll_sleep;
55 unsigned long new_data_thresh;
56 uint32_t evt_mask;
57 uint32_t cpu_mask;
58 unsigned long tbuf_size;
59 int discard:1;
60 } settings_t;
62 settings_t opts;
64 int interrupted = 0; /* gets set if we get a SIGHUP */
66 void close_handler(int signal)
67 {
68 interrupted = 1;
69 }
71 /**
72 * millis_to_timespec - convert a time in milliseconds to a struct timespec
73 * @millis: time interval in milliseconds
74 */
75 struct timespec millis_to_timespec(unsigned long millis)
76 {
77 struct timespec spec;
79 spec.tv_sec = millis / 1000;
80 spec.tv_nsec = (millis % 1000) * 1000;
82 return spec;
83 }
85 /**
86 * write_rec - output a trace record in binary format
87 * @cpu - source buffer CPU ID
88 * @rec - trace record to output
89 * @out - output stream
90 *
91 * Outputs the trace record to a filestream, prepending the CPU ID of the
92 * source trace buffer.
93 */
94 void write_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
95 {
96 size_t written = 0;
97 written += fwrite(&cpu, sizeof(cpu), 1, out);
98 written += fwrite(rec, sizeof(*rec), 1, out);
99 if ( written != 2 )
100 {
101 PERROR("Failed to write trace record");
102 exit(EXIT_FAILURE);
103 }
104 }
106 static void get_tbufs(unsigned long *mfn, unsigned long *size)
107 {
108 int xc_handle = xc_interface_open();
109 int ret;
111 if ( xc_handle < 0 )
112 {
113 exit(EXIT_FAILURE);
114 }
116 if(!opts.tbuf_size)
117 opts.tbuf_size = DEFAULT_TBUF_SIZE;
119 ret = xc_tbuf_enable(xc_handle, opts.tbuf_size, mfn, size);
121 if ( ret != 0 )
122 {
123 perror("Couldn't enable trace buffers");
124 exit(1);
125 }
127 xc_interface_close(xc_handle);
128 }
130 /**
131 * map_tbufs - memory map Xen trace buffers into user space
132 * @tbufs_mfn: mfn of the trace buffers
133 * @num: number of trace buffers to map
134 * @size: size of each trace buffer
135 *
136 * Maps the Xen trace buffers them into process address space.
137 */
138 struct t_buf *map_tbufs(unsigned long tbufs_mfn, unsigned int num,
139 unsigned long size)
140 {
141 int xc_handle;
142 struct t_buf *tbufs_mapped;
144 xc_handle = xc_interface_open();
146 if ( xc_handle < 0 )
147 {
148 exit(EXIT_FAILURE);
149 }
151 tbufs_mapped = xc_map_foreign_range(xc_handle, DOMID_XEN,
152 size * num, PROT_READ | PROT_WRITE,
153 tbufs_mfn);
155 xc_interface_close(xc_handle);
157 if ( tbufs_mapped == 0 )
158 {
159 PERROR("Failed to mmap trace buffers");
160 exit(EXIT_FAILURE);
161 }
163 return tbufs_mapped;
164 }
166 /**
167 * set_mask - set the cpu/event mask in HV
168 * @mask: the new mask
169 * @type: the new mask type,0-event mask, 1-cpu mask
170 *
171 */
172 void set_mask(uint32_t mask, int type)
173 {
174 int ret = 0;
175 int xc_handle = xc_interface_open(); /* for accessing control interface */
177 if (type == 1) {
178 ret = xc_tbuf_set_cpu_mask(xc_handle, mask);
179 fprintf(stderr, "change cpumask to 0x%x\n", mask);
180 } else if (type == 0) {
181 ret = xc_tbuf_set_evt_mask(xc_handle, mask);
182 fprintf(stderr, "change evtmask to 0x%x\n", mask);
183 }
185 xc_interface_close(xc_handle);
187 if ( ret != 0 )
188 {
189 PERROR("Failure to get trace buffer pointer from Xen and set the new mask");
190 exit(EXIT_FAILURE);
191 }
192 }
194 /**
195 * init_bufs_ptrs - initialises an array of pointers to the trace buffers
196 * @bufs_mapped: the userspace address where the trace buffers are mapped
197 * @num: number of trace buffers
198 * @size: trace buffer size
199 *
200 * Initialises an array of pointers to individual trace buffers within the
201 * mapped region containing all trace buffers.
202 */
203 struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
204 unsigned long size)
205 {
206 int i;
207 struct t_buf **user_ptrs;
209 user_ptrs = (struct t_buf **)calloc(num, sizeof(struct t_buf *));
210 if ( user_ptrs == NULL )
211 {
212 PERROR( "Failed to allocate memory for buffer pointers\n");
213 exit(EXIT_FAILURE);
214 }
216 /* initialise pointers to the trace buffers - given the size of a trace
217 * buffer and the value of bufs_maped, we can easily calculate these */
218 for ( i = 0; i<num; i++ )
219 user_ptrs[i] = (struct t_buf *)((unsigned long)bufs_mapped + size * i);
221 return user_ptrs;
222 }
225 /**
226 * init_rec_ptrs - initialises data area pointers to locations in user space
227 * @tbufs_mfn: base mfn of the trace buffer area
228 * @tbufs_mapped: user virtual address of base of trace buffer area
229 * @meta: array of user-space pointers to struct t_buf's of metadata
230 * @num: number of trace buffers
231 *
232 * Initialises data area pointers to the locations that data areas have been
233 * mapped in user space. Note that the trace buffer metadata contains machine
234 * pointers - the array returned allows more convenient access to them.
235 */
236 struct t_rec **init_rec_ptrs(struct t_buf **meta, unsigned int num)
237 {
238 int i;
239 struct t_rec **data;
241 data = calloc(num, sizeof(struct t_rec *));
242 if ( data == NULL )
243 {
244 PERROR("Failed to allocate memory for data pointers\n");
245 exit(EXIT_FAILURE);
246 }
248 for ( i = 0; i < num; i++ )
249 data[i] = (struct t_rec *)(meta[i] + 1);
251 return data;
252 }
254 /**
255 * get_num_cpus - get the number of logical CPUs
256 */
257 unsigned int get_num_cpus(void)
258 {
259 xc_physinfo_t physinfo;
260 int xc_handle = xc_interface_open();
261 int ret;
263 ret = xc_physinfo(xc_handle, &physinfo);
265 if ( ret != 0 )
266 {
267 PERROR("Failure to get logical CPU count from Xen");
268 exit(EXIT_FAILURE);
269 }
271 xc_interface_close(xc_handle);
273 return (physinfo.threads_per_core *
274 physinfo.cores_per_socket *
275 physinfo.sockets_per_node *
276 physinfo.nr_nodes);
277 }
280 /**
281 * monitor_tbufs - monitor the contents of tbufs and output to a file
282 * @logfile: the FILE * representing the file to log to
283 */
284 int monitor_tbufs(FILE *logfile)
285 {
286 int i;
288 void *tbufs_mapped; /* pointer to where the tbufs are mapped */
289 struct t_buf **meta; /* pointers to the trace buffer metadata */
290 struct t_rec **data; /* pointers to the trace buffer data areas
291 * where they are mapped into user space. */
292 unsigned long tbufs_mfn; /* mfn of the tbufs */
293 unsigned int num; /* number of trace buffers / logical CPUS */
294 unsigned long size; /* size of a single trace buffer */
296 int size_in_recs;
298 /* get number of logical CPUs (and therefore number of trace buffers) */
299 num = get_num_cpus();
301 /* setup access to trace buffers */
302 get_tbufs(&tbufs_mfn, &size);
303 tbufs_mapped = map_tbufs(tbufs_mfn, num, size);
305 size_in_recs = (size - sizeof(struct t_buf)) / sizeof(struct t_rec);
307 /* build arrays of convenience ptrs */
308 meta = init_bufs_ptrs(tbufs_mapped, num, size);
309 data = init_rec_ptrs(meta, num);
311 if(opts.discard) {
312 for ( i = 0; (i < num) ; i++ )
313 {
314 meta[i]->cons = meta[i]->prod;
315 }
316 }
318 /* now, scan buffers for events */
319 while ( !interrupted )
320 {
321 for ( i = 0; (i < num) && !interrupted; i++ )
322 {
323 while ( meta[i]->cons != meta[i]->prod )
324 {
325 rmb(); /* read prod, then read item. */
326 write_rec(i, data[i] + meta[i]->cons % size_in_recs, logfile);
327 mb(); /* read item, then update cons. */
328 meta[i]->cons++;
329 }
330 }
332 nanosleep(&opts.poll_sleep, NULL);
333 }
335 /* cleanup */
336 free(meta);
337 free(data);
338 /* don't need to munmap - cleanup is automatic */
339 fclose(logfile);
341 return 0;
342 }
345 /******************************************************************************
346 * Various declarations / definitions GNU argp needs to do its work
347 *****************************************************************************/
349 int parse_evtmask(char *arg, struct argp_state *state)
350 {
351 settings_t *setup = (settings_t *)state->input;
352 char *inval;
354 /* search filtering class */
355 if (strcmp(arg, "gen") == 0){
356 setup->evt_mask |= TRC_GEN;
357 } else if(strcmp(arg, "sched") == 0){
358 setup->evt_mask |= TRC_SCHED;
359 } else if(strcmp(arg, "dom0op") == 0){
360 setup->evt_mask |= TRC_DOM0OP;
361 } else if(strcmp(arg, "hvm") == 0){
362 setup->evt_mask |= TRC_HVM;
363 } else if(strcmp(arg, "all") == 0){
364 setup->evt_mask |= TRC_ALL;
365 } else {
366 setup->evt_mask = strtol(arg, &inval, 0);
367 if ( inval == arg )
368 argp_usage(state);
369 }
371 return 0;
373 }
375 /* command parser for GNU argp - see GNU docs for more info */
376 error_t cmd_parser(int key, char *arg, struct argp_state *state)
377 {
378 settings_t *setup = (settings_t *)state->input;
380 switch ( key )
381 {
382 case 't': /* set new records threshold for logging */
383 {
384 char *inval;
385 setup->new_data_thresh = strtol(arg, &inval, 0);
386 if ( inval == arg )
387 argp_usage(state);
388 }
389 break;
391 case 's': /* set sleep time (given in milliseconds) */
392 {
393 char *inval;
394 setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0));
395 if ( inval == arg )
396 argp_usage(state);
397 }
398 break;
400 case 'c': /* set new cpu mask for filtering*/
401 {
402 char *inval;
403 setup->cpu_mask = strtol(arg, &inval, 0);
404 if ( inval == arg )
405 argp_usage(state);
406 }
407 break;
409 case 'e': /* set new event mask for filtering*/
410 {
411 parse_evtmask(arg, state);
412 }
413 break;
415 case 'S': /* set tbuf size (given in pages) */
416 {
417 char *inval;
418 setup->tbuf_size = strtol(arg, &inval, 0);
419 if ( inval == arg )
420 argp_usage(state);
421 }
422 break;
424 case 'D': /* Discard traces currently in the buffer before beginning */
425 {
426 opts.discard=1;
427 }
428 break;
430 case ARGP_KEY_ARG:
431 {
432 if ( state->arg_num == 0 )
433 setup->outfile = arg;
434 else
435 argp_usage(state);
436 }
437 break;
439 default:
440 return ARGP_ERR_UNKNOWN;
441 }
443 return 0;
444 }
446 #define xstr(x) str(x)
447 #define str(x) #x
449 const struct argp_option cmd_opts[] =
450 {
451 { .name = "log-thresh", .key='t', .arg="l",
452 .doc =
453 "Set number, l, of new records required to trigger a write to output "
454 "(default " xstr(NEW_DATA_THRESH) ")." },
456 { .name = "poll-sleep", .key='s', .arg="p",
457 .doc =
458 "Set sleep time, p, in milliseconds between polling the trace buffer "
459 "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
461 { .name = "cpu-mask", .key='c', .arg="c",
462 .doc =
463 "Set cpu-mask." },
465 { .name = "evt-mask", .key='e', .arg="e",
466 .doc =
467 "Set trace event mask. This can accept a numerical (including hex) "
468 " argument or a symbolic name. Symbolic names include: gen, sched, "
469 "dom0op, hvm, and all." },
471 { .name = "trace-buf-size", .key='S', .arg="N",
472 .doc =
473 "Set trace buffer size in pages (default " xstr(DEFAULT_TBUF_SIZE) "). "
474 "N.B. that the trace buffer cannot be resized. If it has "
475 "already been set this boot cycle, this argument will be ignored." },
477 { .name = "discard-buffers", .key='D', .arg=NULL,
478 .flags=OPTION_ARG_OPTIONAL,
479 .doc = "Discard all records currently in the trace buffers before "
480 " beginning." },
482 {0}
483 };
485 const struct argp parser_def =
486 {
487 .options = cmd_opts,
488 .parser = cmd_parser,
489 .args_doc = "[output file]",
490 .doc =
491 "Tool to capure Xen trace buffer data"
492 "\v"
493 "This tool is used to capture trace buffer data from Xen. The data is "
494 "output in a binary format, in the following order:\n\n"
495 " CPU(uint) TSC(uint64_t) EVENT(uint32_t) D1 D2 D3 D4 D5 "
496 "(all uint32_t)\n\n"
497 "The output should be parsed using the tool xentrace_format, which can "
498 "produce human-readable output in ASCII format."
499 };
502 const char *argp_program_version = "xentrace v1.1";
503 const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
506 int main(int argc, char **argv)
507 {
508 int outfd = 1, ret;
509 FILE *logfile;
510 struct sigaction act;
512 opts.outfile = 0;
513 opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
514 opts.new_data_thresh = NEW_DATA_THRESH;
515 opts.evt_mask = 0;
516 opts.cpu_mask = 0;
518 argp_parse(&parser_def, argc, argv, 0, 0, &opts);
520 if (opts.evt_mask != 0) {
521 set_mask(opts.evt_mask, 0);
522 }
524 if (opts.cpu_mask != 0) {
525 set_mask(opts.cpu_mask, 1);
526 }
528 if ( opts.outfile )
529 outfd = open(opts.outfile, O_WRONLY | O_CREAT | O_LARGEFILE, 0644);
531 if(outfd < 0)
532 {
533 perror("Could not open output file");
534 exit(EXIT_FAILURE);
535 }
537 if(isatty(outfd))
538 {
539 fprintf(stderr, "Cannot output to a TTY, specify a log file.\n");
540 exit(EXIT_FAILURE);
541 }
543 logfile = fdopen(outfd, "w");
545 /* ensure that if we get a signal, we'll do cleanup, then exit */
546 act.sa_handler = close_handler;
547 act.sa_flags = 0;
548 sigemptyset(&act.sa_mask);
549 sigaction(SIGHUP, &act, NULL);
550 sigaction(SIGTERM, &act, NULL);
551 sigaction(SIGINT, &act, NULL);
553 ret = monitor_tbufs(logfile);
555 return ret;
556 }