ia64/xen-unstable

view tools/xentrace/xentrace.c @ 6392:bca2fd76995e

Add EXT3 as built-in filesystem for x86/64 domU.
author kaf24@firebug.cl.cam.ac.uk
date Wed Aug 24 15:21:48 2005 +0000 (2005-08-24)
parents 522bc50588ed
children 8799d14bef77 9312a3e8a6f8 cc5f88b719d0 fa0754a9f64f 112d44270733
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>
24 #include "xc_private.h"
26 typedef struct { int counter; } atomic_t;
27 #define _atomic_read(v) ((v).counter)
29 #include <xen/trace.h>
31 extern FILE *stderr;
33 /***** Compile time configuration of defaults ********************************/
35 /* when we've got more records than this waiting, we log it to the output */
36 #define NEW_DATA_THRESH 1
38 /* sleep for this long (milliseconds) between checking the trace buffers */
39 #define POLL_SLEEP_MILLIS 100
42 /***** The code **************************************************************/
44 typedef struct settings_st {
45 char *outfile;
46 struct timespec poll_sleep;
47 unsigned long new_data_thresh;
48 u32 evt_mask;
49 u32 cpu_mask;
50 } settings_t;
52 settings_t opts;
54 int interrupted = 0; /* gets set if we get a SIGHUP */
56 void close_handler(int signal)
57 {
58 interrupted = 1;
59 }
61 /**
62 * millis_to_timespec - convert a time in milliseconds to a struct timespec
63 * @millis: time interval in milliseconds
64 */
65 struct timespec millis_to_timespec(unsigned long millis)
66 {
67 struct timespec spec;
69 spec.tv_sec = millis / 1000;
70 spec.tv_nsec = (millis % 1000) * 1000;
72 return spec;
73 }
75 /**
76 * write_rec - output a trace record in binary format
77 * @cpu - source buffer CPU ID
78 * @rec - trace record to output
79 * @out - output stream
80 *
81 * Outputs the trace record to a filestream, prepending the CPU ID of the
82 * source trace buffer.
83 */
84 void write_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
85 {
86 size_t written = 0;
87 written += fwrite(&cpu, sizeof(cpu), 1, out);
88 written += fwrite(rec, sizeof(*rec), 1, out);
89 if ( written != 2 )
90 {
91 PERROR("Failed to write trace record");
92 exit(EXIT_FAILURE);
93 }
94 }
96 /**
97 * get_tbufs - get pointer to and size of the trace buffers
98 * @mfn: location to store mfn of the trace buffers to
99 * @size: location to store the size of a trace buffer to
100 *
101 * Gets the machine address of the trace pointer area and the size of the
102 * per CPU buffers.
103 */
104 void get_tbufs(unsigned long *mfn, unsigned long *size)
105 {
106 int ret;
107 dom0_op_t op; /* dom0 op we'll build */
108 int xc_handle = xc_interface_open(); /* for accessing control interface */
110 op.cmd = DOM0_TBUFCONTROL;
111 op.interface_version = DOM0_INTERFACE_VERSION;
112 op.u.tbufcontrol.op = DOM0_TBUF_GET_INFO;
114 ret = do_dom0_op(xc_handle, &op);
116 xc_interface_close(xc_handle);
118 if ( ret != 0 )
119 {
120 PERROR("Failure to get trace buffer pointer from Xen");
121 exit(EXIT_FAILURE);
122 }
124 *mfn = op.u.tbufcontrol.buffer_mfn;
125 *size = op.u.tbufcontrol.size;
126 }
128 /**
129 * map_tbufs - memory map Xen trace buffers into user space
130 * @tbufs_mfn: mfn of the trace buffers
131 * @num: number of trace buffers to map
132 * @size: size of each trace buffer
133 *
134 * Maps the Xen trace buffers them into process address space.
135 */
136 struct t_buf *map_tbufs(unsigned long tbufs_mfn, unsigned int num,
137 unsigned long size)
138 {
139 int xc_handle; /* file descriptor for /proc/xen/privcmd */
140 struct t_buf *tbufs_mapped;
142 xc_handle = xc_interface_open();
144 if ( xc_handle < 0 )
145 {
146 PERROR("Open /proc/xen/privcmd when mapping trace buffers\n");
147 exit(EXIT_FAILURE);
148 }
150 tbufs_mapped = xc_map_foreign_range(xc_handle, 0 /* Dom 0 ID */,
151 size * num, PROT_READ,
152 tbufs_mfn);
154 xc_interface_close(xc_handle);
156 if ( tbufs_mapped == 0 )
157 {
158 PERROR("Failed to mmap trace buffers");
159 exit(EXIT_FAILURE);
160 }
162 return tbufs_mapped;
163 }
165 /**
166 * set_mask - set the cpu/event mask in HV
167 * @mask: the new mask
168 * @type: the new mask type,0-event mask, 1-cpu mask
169 *
170 */
171 void set_mask(u32 mask, int type)
172 {
173 int ret;
174 dom0_op_t op; /* dom0 op we'll build */
175 int xc_handle = xc_interface_open(); /* for accessing control interface */
177 op.cmd = DOM0_TBUFCONTROL;
178 op.interface_version = DOM0_INTERFACE_VERSION;
179 if (type == 1) { /* cpu mask */
180 op.u.tbufcontrol.op = DOM0_TBUF_SET_CPU_MASK;
181 op.u.tbufcontrol.cpu_mask = mask;
182 fprintf(stderr, "change cpumask to 0x%x\n", mask);
183 }else if (type == 0) { /* event mask */
184 op.u.tbufcontrol.op = DOM0_TBUF_SET_EVT_MASK;
185 op.u.tbufcontrol.evt_mask = mask;
186 fprintf(stderr, "change evtmask to 0x%x\n", mask);
187 }
189 ret = do_dom0_op(xc_handle, &op);
191 xc_interface_close(xc_handle);
193 if ( ret != 0 )
194 {
195 PERROR("Failure to get trace buffer pointer from Xen and set the new mask");
196 exit(EXIT_FAILURE);
197 }
199 }
201 /**
202 * init_bufs_ptrs - initialises an array of pointers to the trace buffers
203 * @bufs_mapped: the userspace address where the trace buffers are mapped
204 * @num: number of trace buffers
205 * @size: trace buffer size
206 *
207 * Initialises an array of pointers to individual trace buffers within the
208 * mapped region containing all trace buffers.
209 */
210 struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
211 unsigned long size)
212 {
213 int i;
214 struct t_buf **user_ptrs;
216 user_ptrs = (struct t_buf **)calloc(num, sizeof(struct t_buf *));
217 if ( user_ptrs == NULL )
218 {
219 PERROR( "Failed to allocate memory for buffer pointers\n");
220 exit(EXIT_FAILURE);
221 }
223 /* initialise pointers to the trace buffers - given the size of a trace
224 * buffer and the value of bufs_maped, we can easily calculate these */
225 for ( i = 0; i<num; i++ )
226 user_ptrs[i] = (struct t_buf *)((unsigned long)bufs_mapped + size * i);
228 return user_ptrs;
229 }
232 /**
233 * init_rec_ptrs - initialises data area pointers to locations in user space
234 * @tbufs_mfn: base mfn of the trace buffer area
235 * @tbufs_mapped: user virtual address of base of trace buffer area
236 * @meta: array of user-space pointers to struct t_buf's of metadata
237 * @num: number of trace buffers
238 *
239 * Initialises data area pointers to the locations that data areas have been
240 * mapped in user space. Note that the trace buffer metadata contains machine
241 * pointers - the array returned allows more convenient access to them.
242 */
243 struct t_rec **init_rec_ptrs(unsigned long tbufs_mfn,
244 struct t_buf *tbufs_mapped,
245 struct t_buf **meta,
246 unsigned int num)
247 {
248 int i;
249 struct t_rec **data;
251 data = calloc(num, sizeof(struct t_rec *));
252 if ( data == NULL )
253 {
254 PERROR("Failed to allocate memory for data pointers\n");
255 exit(EXIT_FAILURE);
256 }
258 for ( i = 0; i < num; i++ )
259 data[i] = (struct t_rec *)(meta[i]->rec_addr - (tbufs_mfn<<XC_PAGE_SHIFT) /* XXX */
260 + (unsigned long)tbufs_mapped);
262 return data;
263 }
265 /**
266 * init_tail_idxs - initialise an array of tail indexes
267 * @bufs: array of pointers to trace buffer metadata
268 * @num: number of trace buffers
269 *
270 * The tail indexes indicate where we're read to so far in the data array of a
271 * trace buffer. Each entry in this table corresponds to the tail index for a
272 * particular trace buffer.
273 */
274 unsigned long *init_tail_idxs(struct t_buf **bufs, unsigned int num)
275 {
276 int i;
277 unsigned long *tails = calloc(num, sizeof(unsigned int));
279 if ( tails == NULL )
280 {
281 PERROR("Failed to allocate memory for tail pointers\n");
282 exit(EXIT_FAILURE);
283 }
285 for ( i = 0; i<num; i++ )
286 tails[i] = _atomic_read(bufs[i]->rec_idx);
288 return tails;
289 }
291 /**
292 * get_num_cpus - get the number of logical CPUs
293 */
294 unsigned int get_num_cpus()
295 {
296 dom0_op_t op;
297 int xc_handle = xc_interface_open();
298 int ret;
300 op.cmd = DOM0_PHYSINFO;
301 op.interface_version = DOM0_INTERFACE_VERSION;
303 ret = do_dom0_op(xc_handle, &op);
305 if ( ret != 0 )
306 {
307 PERROR("Failure to get logical CPU count from Xen");
308 exit(EXIT_FAILURE);
309 }
311 xc_interface_close(xc_handle);
313 return (op.u.physinfo.threads_per_core *
314 op.u.physinfo.cores_per_socket *
315 op.u.physinfo.sockets_per_node *
316 op.u.physinfo.nr_nodes);
317 }
320 /**
321 * monitor_tbufs - monitor the contents of tbufs and output to a file
322 * @logfile: the FILE * representing the file to log to
323 */
324 int monitor_tbufs(FILE *logfile)
325 {
326 int i;
328 void *tbufs_mapped; /* pointer to where the tbufs are mapped */
329 struct t_buf **meta; /* pointers to the trace buffer metadata */
330 struct t_rec **data; /* pointers to the trace buffer data areas
331 * where they are mapped into user space. */
332 unsigned long *cons; /* store tail indexes for the trace buffers */
333 unsigned long tbufs_mfn; /* mfn of the tbufs */
334 unsigned int num; /* number of trace buffers / logical CPUS */
335 unsigned long size; /* size of a single trace buffer */
337 int size_in_recs;
339 /* get number of logical CPUs (and therefore number of trace buffers) */
340 num = get_num_cpus();
342 /* setup access to trace buffers */
343 get_tbufs(&tbufs_mfn, &size);
344 tbufs_mapped = map_tbufs(tbufs_mfn, num, size);
346 size_in_recs = (size - sizeof(struct t_buf)) / sizeof(struct t_rec);
348 /* build arrays of convenience ptrs */
349 meta = init_bufs_ptrs (tbufs_mapped, num, size);
350 data = init_rec_ptrs (tbufs_mfn, tbufs_mapped, meta, num);
351 cons = init_tail_idxs (meta, num);
353 /* now, scan buffers for events */
354 while ( !interrupted )
355 {
356 for ( i = 0; ( i < num ) && !interrupted; i++ )
357 while( cons[i] != _atomic_read(meta[i]->rec_idx) )
358 {
359 write_rec(i, data[i] + cons[i], logfile);
360 cons[i] = (cons[i] + 1) % size_in_recs;
361 }
363 nanosleep(&opts.poll_sleep, NULL);
364 }
366 /* cleanup */
367 free(meta);
368 free(data);
369 free(cons);
370 /* don't need to munmap - cleanup is automatic */
371 fclose(logfile);
373 return 0;
374 }
377 /******************************************************************************
378 * Various declarations / definitions GNU argp needs to do its work
379 *****************************************************************************/
381 int parse_evtmask(char *arg, struct argp_state *state)
382 {
383 settings_t *setup = (settings_t *)state->input;
384 char *inval;
386 /* search filtering class */
387 if (strcmp(arg, "gen") == 0){
388 setup->evt_mask |= TRC_GEN;
389 } else if(strcmp(arg, "sched") == 0){
390 setup->evt_mask |= TRC_SCHED;
391 } else if(strcmp(arg, "dom0op") == 0){
392 setup->evt_mask |= TRC_DOM0OP;
393 } else if(strcmp(arg, "vmx") == 0){
394 setup->evt_mask |= TRC_VMX;
395 } else if(strcmp(arg, "all") == 0){
396 setup->evt_mask |= TRC_ALL;
397 } else {
398 setup->evt_mask = strtol(arg, &inval, 0);
399 if ( inval == arg )
400 argp_usage(state);
401 }
403 return 0;
405 }
407 /* command parser for GNU argp - see GNU docs for more info */
408 error_t cmd_parser(int key, char *arg, struct argp_state *state)
409 {
410 settings_t *setup = (settings_t *)state->input;
412 switch ( key )
413 {
414 case 't': /* set new records threshold for logging */
415 {
416 char *inval;
417 setup->new_data_thresh = strtol(arg, &inval, 0);
418 if ( inval == arg )
419 argp_usage(state);
420 }
421 break;
423 case 's': /* set sleep time (given in milliseconds) */
424 {
425 char *inval;
426 setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0));
427 if ( inval == arg )
428 argp_usage(state);
429 }
430 break;
432 case 'c': /* set new cpu mask for filtering*/
433 {
434 char *inval;
435 setup->cpu_mask = strtol(arg, &inval, 0);
436 if ( inval == arg )
437 argp_usage(state);
438 }
439 break;
441 case 'e': /* set new event mask for filtering*/
442 {
443 parse_evtmask(arg, state);
444 }
445 break;
447 case ARGP_KEY_ARG:
448 {
449 if ( state->arg_num == 0 )
450 setup->outfile = arg;
451 else
452 argp_usage(state);
453 }
454 break;
456 default:
457 return ARGP_ERR_UNKNOWN;
458 }
460 return 0;
461 }
463 #define xstr(x) str(x)
464 #define str(x) #x
466 const struct argp_option cmd_opts[] =
467 {
468 { .name = "log-thresh", .key='t', .arg="l",
469 .doc =
470 "Set number, l, of new records required to trigger a write to output "
471 "(default " xstr(NEW_DATA_THRESH) ")." },
473 { .name = "poll-sleep", .key='s', .arg="p",
474 .doc =
475 "Set sleep time, p, in milliseconds between polling the trace buffer "
476 "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
478 { .name = "cpu-mask", .key='c', .arg="c",
479 .doc =
480 "set cpu-mask " },
482 { .name = "evt-mask", .key='e', .arg="e",
483 .doc =
484 "set evt-mask " },
486 {0}
487 };
489 const struct argp parser_def =
490 {
491 .options = cmd_opts,
492 .parser = cmd_parser,
493 .args_doc = "[output file]",
494 .doc =
495 "Tool to capure Xen trace buffer data"
496 "\v"
497 "This tool is used to capture trace buffer data from Xen. The data is "
498 "output in a binary format, in the following order:\n\n"
499 " CPU(uint) TSC(u64) EVENT(u32) D1 D2 D3 D4 D5 (all u32)\n\n"
500 "The output should be parsed using the tool xentrace_format, which can "
501 "produce human-readable output in ASCII format."
502 };
505 const char *argp_program_version = "xentrace v1.1";
506 const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
509 int main(int argc, char **argv)
510 {
511 int outfd = 1, ret;
512 FILE *logfile;
513 struct sigaction act;
515 opts.outfile = 0;
516 opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
517 opts.new_data_thresh = NEW_DATA_THRESH;
518 opts.evt_mask = 0;
519 opts.cpu_mask = 0;
521 argp_parse(&parser_def, argc, argv, 0, 0, &opts);
523 if (opts.evt_mask != 0) {
524 set_mask(opts.evt_mask, 0);
525 }
527 if (opts.cpu_mask != 0) {
528 set_mask(opts.evt_mask, 1);
529 }
531 if ( opts.outfile )
532 outfd = open(opts.outfile, O_WRONLY | O_CREAT);
534 if(outfd < 0)
535 {
536 perror("Could not open output file");
537 exit(EXIT_FAILURE);
538 }
540 if(isatty(outfd))
541 {
542 fprintf(stderr, "Cannot output to a TTY, specify a log file.\n");
543 exit(EXIT_FAILURE);
544 }
546 logfile = fdopen(outfd, "w");
548 /* ensure that if we get a signal, we'll do cleanup, then exit */
549 act.sa_handler = close_handler;
550 act.sa_flags = 0;
551 sigemptyset(&act.sa_mask);
552 sigaction(SIGHUP, &act, NULL);
553 sigaction(SIGTERM, &act, NULL);
554 sigaction(SIGINT, &act, NULL);
556 ret = monitor_tbufs(logfile);
558 return ret;
559 }