direct-io.hg

view tools/xentrace/xentrace.c @ 11422:0419253c81de

Fix inverted sense of getRequiredAvailableMemory and
getRequiredInitialReservation on x86 HVM.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Tue Sep 05 16:23:11 2006 +0100 (2006-09-05)
parents f681ffc9b01a
children 6efc22cb9c84
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 } settings_t;
61 settings_t opts;
63 int interrupted = 0; /* gets set if we get a SIGHUP */
65 void close_handler(int signal)
66 {
67 interrupted = 1;
68 }
70 /**
71 * millis_to_timespec - convert a time in milliseconds to a struct timespec
72 * @millis: time interval in milliseconds
73 */
74 struct timespec millis_to_timespec(unsigned long millis)
75 {
76 struct timespec spec;
78 spec.tv_sec = millis / 1000;
79 spec.tv_nsec = (millis % 1000) * 1000;
81 return spec;
82 }
84 /**
85 * write_rec - output a trace record in binary format
86 * @cpu - source buffer CPU ID
87 * @rec - trace record to output
88 * @out - output stream
89 *
90 * Outputs the trace record to a filestream, prepending the CPU ID of the
91 * source trace buffer.
92 */
93 void write_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
94 {
95 size_t written = 0;
96 written += fwrite(&cpu, sizeof(cpu), 1, out);
97 written += fwrite(rec, sizeof(*rec), 1, out);
98 if ( written != 2 )
99 {
100 PERROR("Failed to write trace record");
101 exit(EXIT_FAILURE);
102 }
103 }
105 static void get_tbufs(unsigned long *mfn, unsigned long *size)
106 {
107 int xc_handle = xc_interface_open();
108 int ret;
110 if ( xc_handle < 0 )
111 {
112 exit(EXIT_FAILURE);
113 }
115 if(!opts.tbuf_size)
116 opts.tbuf_size = DEFAULT_TBUF_SIZE;
118 ret = xc_tbuf_enable(xc_handle, opts.tbuf_size, mfn, size);
120 if ( ret != 0 )
121 {
122 perror("Couldn't enable trace buffers");
123 exit(1);
124 }
126 xc_interface_close(xc_handle);
127 }
129 /**
130 * map_tbufs - memory map Xen trace buffers into user space
131 * @tbufs_mfn: mfn of the trace buffers
132 * @num: number of trace buffers to map
133 * @size: size of each trace buffer
134 *
135 * Maps the Xen trace buffers them into process address space.
136 */
137 struct t_buf *map_tbufs(unsigned long tbufs_mfn, unsigned int num,
138 unsigned long size)
139 {
140 int xc_handle;
141 struct t_buf *tbufs_mapped;
143 xc_handle = xc_interface_open();
145 if ( xc_handle < 0 )
146 {
147 exit(EXIT_FAILURE);
148 }
150 tbufs_mapped = xc_map_foreign_range(xc_handle, DOMID_XEN,
151 size * num, PROT_READ | PROT_WRITE,
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(uint32_t mask, int type)
172 {
173 int ret = 0;
174 int xc_handle = xc_interface_open(); /* for accessing control interface */
176 if (type == 1) {
177 ret = xc_tbuf_set_cpu_mask(xc_handle, mask);
178 fprintf(stderr, "change cpumask to 0x%x\n", mask);
179 } else if (type == 0) {
180 ret = xc_tbuf_set_evt_mask(xc_handle, mask);
181 fprintf(stderr, "change evtmask to 0x%x\n", mask);
182 }
184 xc_interface_close(xc_handle);
186 if ( ret != 0 )
187 {
188 PERROR("Failure to get trace buffer pointer from Xen and set the new mask");
189 exit(EXIT_FAILURE);
190 }
191 }
193 /**
194 * init_bufs_ptrs - initialises an array of pointers to the trace buffers
195 * @bufs_mapped: the userspace address where the trace buffers are mapped
196 * @num: number of trace buffers
197 * @size: trace buffer size
198 *
199 * Initialises an array of pointers to individual trace buffers within the
200 * mapped region containing all trace buffers.
201 */
202 struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
203 unsigned long size)
204 {
205 int i;
206 struct t_buf **user_ptrs;
208 user_ptrs = (struct t_buf **)calloc(num, sizeof(struct t_buf *));
209 if ( user_ptrs == NULL )
210 {
211 PERROR( "Failed to allocate memory for buffer pointers\n");
212 exit(EXIT_FAILURE);
213 }
215 /* initialise pointers to the trace buffers - given the size of a trace
216 * buffer and the value of bufs_maped, we can easily calculate these */
217 for ( i = 0; i<num; i++ )
218 user_ptrs[i] = (struct t_buf *)((unsigned long)bufs_mapped + size * i);
220 return user_ptrs;
221 }
224 /**
225 * init_rec_ptrs - initialises data area pointers to locations in user space
226 * @tbufs_mfn: base mfn of the trace buffer area
227 * @tbufs_mapped: user virtual address of base of trace buffer area
228 * @meta: array of user-space pointers to struct t_buf's of metadata
229 * @num: number of trace buffers
230 *
231 * Initialises data area pointers to the locations that data areas have been
232 * mapped in user space. Note that the trace buffer metadata contains machine
233 * pointers - the array returned allows more convenient access to them.
234 */
235 struct t_rec **init_rec_ptrs(struct t_buf **meta, unsigned int num)
236 {
237 int i;
238 struct t_rec **data;
240 data = calloc(num, sizeof(struct t_rec *));
241 if ( data == NULL )
242 {
243 PERROR("Failed to allocate memory for data pointers\n");
244 exit(EXIT_FAILURE);
245 }
247 for ( i = 0; i < num; i++ )
248 data[i] = (struct t_rec *)(meta[i] + 1);
250 return data;
251 }
253 /**
254 * get_num_cpus - get the number of logical CPUs
255 */
256 unsigned int get_num_cpus(void)
257 {
258 xc_physinfo_t physinfo;
259 int xc_handle = xc_interface_open();
260 int ret;
262 ret = xc_physinfo(xc_handle, &physinfo);
264 if ( ret != 0 )
265 {
266 PERROR("Failure to get logical CPU count from Xen");
267 exit(EXIT_FAILURE);
268 }
270 xc_interface_close(xc_handle);
272 return (physinfo.threads_per_core *
273 physinfo.cores_per_socket *
274 physinfo.sockets_per_node *
275 physinfo.nr_nodes);
276 }
279 /**
280 * monitor_tbufs - monitor the contents of tbufs and output to a file
281 * @logfile: the FILE * representing the file to log to
282 */
283 int monitor_tbufs(FILE *logfile)
284 {
285 int i;
287 void *tbufs_mapped; /* pointer to where the tbufs are mapped */
288 struct t_buf **meta; /* pointers to the trace buffer metadata */
289 struct t_rec **data; /* pointers to the trace buffer data areas
290 * where they are mapped into user space. */
291 unsigned long tbufs_mfn; /* mfn of the tbufs */
292 unsigned int num; /* number of trace buffers / logical CPUS */
293 unsigned long size; /* size of a single trace buffer */
295 int size_in_recs;
297 /* get number of logical CPUs (and therefore number of trace buffers) */
298 num = get_num_cpus();
300 /* setup access to trace buffers */
301 get_tbufs(&tbufs_mfn, &size);
302 tbufs_mapped = map_tbufs(tbufs_mfn, num, size);
304 size_in_recs = (size - sizeof(struct t_buf)) / sizeof(struct t_rec);
306 /* build arrays of convenience ptrs */
307 meta = init_bufs_ptrs(tbufs_mapped, num, size);
308 data = init_rec_ptrs(meta, num);
310 /* now, scan buffers for events */
311 while ( !interrupted )
312 {
313 for ( i = 0; (i < num) && !interrupted; i++ )
314 {
315 while ( meta[i]->cons != meta[i]->prod )
316 {
317 rmb(); /* read prod, then read item. */
318 write_rec(i, data[i] + meta[i]->cons % size_in_recs, logfile);
319 mb(); /* read item, then update cons. */
320 meta[i]->cons++;
321 }
322 }
324 nanosleep(&opts.poll_sleep, NULL);
325 }
327 /* cleanup */
328 free(meta);
329 free(data);
330 /* don't need to munmap - cleanup is automatic */
331 fclose(logfile);
333 return 0;
334 }
337 /******************************************************************************
338 * Various declarations / definitions GNU argp needs to do its work
339 *****************************************************************************/
341 int parse_evtmask(char *arg, struct argp_state *state)
342 {
343 settings_t *setup = (settings_t *)state->input;
344 char *inval;
346 /* search filtering class */
347 if (strcmp(arg, "gen") == 0){
348 setup->evt_mask |= TRC_GEN;
349 } else if(strcmp(arg, "sched") == 0){
350 setup->evt_mask |= TRC_SCHED;
351 } else if(strcmp(arg, "dom0op") == 0){
352 setup->evt_mask |= TRC_DOM0OP;
353 } else if(strcmp(arg, "vmx") == 0){
354 setup->evt_mask |= TRC_VMX;
355 } else if(strcmp(arg, "all") == 0){
356 setup->evt_mask |= TRC_ALL;
357 } else {
358 setup->evt_mask = strtol(arg, &inval, 0);
359 if ( inval == arg )
360 argp_usage(state);
361 }
363 return 0;
365 }
367 /* command parser for GNU argp - see GNU docs for more info */
368 error_t cmd_parser(int key, char *arg, struct argp_state *state)
369 {
370 settings_t *setup = (settings_t *)state->input;
372 switch ( key )
373 {
374 case 't': /* set new records threshold for logging */
375 {
376 char *inval;
377 setup->new_data_thresh = strtol(arg, &inval, 0);
378 if ( inval == arg )
379 argp_usage(state);
380 }
381 break;
383 case 's': /* set sleep time (given in milliseconds) */
384 {
385 char *inval;
386 setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0));
387 if ( inval == arg )
388 argp_usage(state);
389 }
390 break;
392 case 'c': /* set new cpu mask for filtering*/
393 {
394 char *inval;
395 setup->cpu_mask = strtol(arg, &inval, 0);
396 if ( inval == arg )
397 argp_usage(state);
398 }
399 break;
401 case 'e': /* set new event mask for filtering*/
402 {
403 parse_evtmask(arg, state);
404 }
405 break;
407 case 'S': /* set tbuf size (given in pages) */
408 {
409 char *inval;
410 setup->tbuf_size = strtol(arg, &inval, 0);
411 if ( inval == arg )
412 argp_usage(state);
413 }
414 break;
416 case ARGP_KEY_ARG:
417 {
418 if ( state->arg_num == 0 )
419 setup->outfile = arg;
420 else
421 argp_usage(state);
422 }
423 break;
425 default:
426 return ARGP_ERR_UNKNOWN;
427 }
429 return 0;
430 }
432 #define xstr(x) str(x)
433 #define str(x) #x
435 const struct argp_option cmd_opts[] =
436 {
437 { .name = "log-thresh", .key='t', .arg="l",
438 .doc =
439 "Set number, l, of new records required to trigger a write to output "
440 "(default " xstr(NEW_DATA_THRESH) ")." },
442 { .name = "poll-sleep", .key='s', .arg="p",
443 .doc =
444 "Set sleep time, p, in milliseconds between polling the trace buffer "
445 "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
447 { .name = "cpu-mask", .key='c', .arg="c",
448 .doc =
449 "set cpu-mask " },
451 { .name = "evt-mask", .key='e', .arg="e",
452 .doc =
453 "set evt-mask " },
455 { .name = "trace-buf-size", .key='S', .arg="N",
456 .doc =
457 "Set trace buffer size in pages (default " xstr(DEFAULT_TBUF_SIZE) "). "
458 "N.B. that the trace buffer cannot be resized. If it has "
459 "already been set this boot cycle, this argument will be ignored." },
461 {0}
462 };
464 const struct argp parser_def =
465 {
466 .options = cmd_opts,
467 .parser = cmd_parser,
468 .args_doc = "[output file]",
469 .doc =
470 "Tool to capure Xen trace buffer data"
471 "\v"
472 "This tool is used to capture trace buffer data from Xen. The data is "
473 "output in a binary format, in the following order:\n\n"
474 " CPU(uint) TSC(uint64_t) EVENT(uint32_t) D1 D2 D3 D4 D5 "
475 "(all uint32_t)\n\n"
476 "The output should be parsed using the tool xentrace_format, which can "
477 "produce human-readable output in ASCII format."
478 };
481 const char *argp_program_version = "xentrace v1.1";
482 const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
485 int main(int argc, char **argv)
486 {
487 int outfd = 1, ret;
488 FILE *logfile;
489 struct sigaction act;
491 opts.outfile = 0;
492 opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
493 opts.new_data_thresh = NEW_DATA_THRESH;
494 opts.evt_mask = 0;
495 opts.cpu_mask = 0;
497 argp_parse(&parser_def, argc, argv, 0, 0, &opts);
499 if (opts.evt_mask != 0) {
500 set_mask(opts.evt_mask, 0);
501 }
503 if (opts.cpu_mask != 0) {
504 set_mask(opts.cpu_mask, 1);
505 }
507 if ( opts.outfile )
508 outfd = open(opts.outfile, O_WRONLY | O_CREAT | O_LARGEFILE, 0644);
510 if(outfd < 0)
511 {
512 perror("Could not open output file");
513 exit(EXIT_FAILURE);
514 }
516 if(isatty(outfd))
517 {
518 fprintf(stderr, "Cannot output to a TTY, specify a log file.\n");
519 exit(EXIT_FAILURE);
520 }
522 logfile = fdopen(outfd, "w");
524 /* ensure that if we get a signal, we'll do cleanup, then exit */
525 act.sa_handler = close_handler;
526 act.sa_flags = 0;
527 sigemptyset(&act.sa_mask);
528 sigaction(SIGHUP, &act, NULL);
529 sigaction(SIGTERM, &act, NULL);
530 sigaction(SIGINT, &act, NULL);
532 ret = monitor_tbufs(logfile);
534 return ret;
535 }