ia64/xen-unstable

view tools/xentrace/xentrace.c @ 6946:e703abaf6e3d

Add behaviour to the remove methods to remove the transaction's path itself. This allows us to write Remove(path) to remove the specified path rather than having to slice the path ourselves.
author emellor@ewan
date Sun Sep 18 14:42:13 2005 +0100 (2005-09-18)
parents 3233e7ecfa9f
children 06d84bf87159
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.cpu_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 }