ia64/xen-unstable

view tools/xentrace/xentrace.c @ 9447:dfbf0939350c

This patch is mainly a cleanup of vmx related xentrace code. One minor
xentrace bug is fixed.

Signed-off-by: Xiaowei Yang <xiaowei.yang@intel.com>
Signed-off-by: Yunfeng Zhao <yunfeng.zhao@intel.com>
author kaf24@firebug.cl.cam.ac.uk
date Fri Mar 24 10:59:31 2006 +0100 (2006-03-24)
parents d4e433d615b0
children 24dbb153ab39
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 #include <xen/trace.h>
28 extern FILE *stderr;
30 /***** Compile time configuration of defaults ********************************/
32 /* when we've got more records than this waiting, we log it to the output */
33 #define NEW_DATA_THRESH 1
35 /* sleep for this long (milliseconds) between checking the trace buffers */
36 #define POLL_SLEEP_MILLIS 100
39 /***** The code **************************************************************/
41 typedef struct settings_st {
42 char *outfile;
43 struct timespec poll_sleep;
44 unsigned long new_data_thresh;
45 uint32_t evt_mask;
46 uint32_t cpu_mask;
47 } settings_t;
49 settings_t opts;
51 int interrupted = 0; /* gets set if we get a SIGHUP */
53 void close_handler(int signal)
54 {
55 interrupted = 1;
56 }
58 /**
59 * millis_to_timespec - convert a time in milliseconds to a struct timespec
60 * @millis: time interval in milliseconds
61 */
62 struct timespec millis_to_timespec(unsigned long millis)
63 {
64 struct timespec spec;
66 spec.tv_sec = millis / 1000;
67 spec.tv_nsec = (millis % 1000) * 1000;
69 return spec;
70 }
72 /**
73 * write_rec - output a trace record in binary format
74 * @cpu - source buffer CPU ID
75 * @rec - trace record to output
76 * @out - output stream
77 *
78 * Outputs the trace record to a filestream, prepending the CPU ID of the
79 * source trace buffer.
80 */
81 void write_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
82 {
83 size_t written = 0;
84 written += fwrite(&cpu, sizeof(cpu), 1, out);
85 written += fwrite(rec, sizeof(*rec), 1, out);
86 if ( written != 2 )
87 {
88 PERROR("Failed to write trace record");
89 exit(EXIT_FAILURE);
90 }
91 }
93 /**
94 * get_tbufs - get pointer to and size of the trace buffers
95 * @mfn: location to store mfn of the trace buffers to
96 * @size: location to store the size of a trace buffer to
97 *
98 * Gets the machine address of the trace pointer area and the size of the
99 * per CPU buffers.
100 */
101 void get_tbufs(unsigned long *mfn, unsigned long *size)
102 {
103 int ret;
104 dom0_op_t op; /* dom0 op we'll build */
105 int xc_handle = xc_interface_open(); /* for accessing control interface */
107 op.cmd = DOM0_TBUFCONTROL;
108 op.interface_version = DOM0_INTERFACE_VERSION;
109 op.u.tbufcontrol.op = DOM0_TBUF_GET_INFO;
111 ret = do_dom0_op(xc_handle, &op);
113 xc_interface_close(xc_handle);
115 if ( ret != 0 )
116 {
117 PERROR("Failure to get trace buffer pointer from Xen");
118 exit(EXIT_FAILURE);
119 }
121 *mfn = op.u.tbufcontrol.buffer_mfn;
122 *size = op.u.tbufcontrol.size;
123 }
125 /**
126 * map_tbufs - memory map Xen trace buffers into user space
127 * @tbufs_mfn: mfn of the trace buffers
128 * @num: number of trace buffers to map
129 * @size: size of each trace buffer
130 *
131 * Maps the Xen trace buffers them into process address space.
132 */
133 struct t_buf *map_tbufs(unsigned long tbufs_mfn, unsigned int num,
134 unsigned long size)
135 {
136 int xc_handle; /* file descriptor for /proc/xen/privcmd */
137 struct t_buf *tbufs_mapped;
139 xc_handle = xc_interface_open();
141 if ( xc_handle < 0 )
142 {
143 PERROR("Open /proc/xen/privcmd when mapping trace buffers\n");
144 exit(EXIT_FAILURE);
145 }
147 tbufs_mapped = xc_map_foreign_range(xc_handle, DOMID_XEN,
148 size * num, PROT_READ | PROT_WRITE,
149 tbufs_mfn);
151 xc_interface_close(xc_handle);
153 if ( tbufs_mapped == 0 )
154 {
155 PERROR("Failed to mmap trace buffers");
156 exit(EXIT_FAILURE);
157 }
159 return tbufs_mapped;
160 }
162 /**
163 * set_mask - set the cpu/event mask in HV
164 * @mask: the new mask
165 * @type: the new mask type,0-event mask, 1-cpu mask
166 *
167 */
168 void set_mask(uint32_t mask, int type)
169 {
170 int ret;
171 dom0_op_t op; /* dom0 op we'll build */
172 int xc_handle = xc_interface_open(); /* for accessing control interface */
174 op.cmd = DOM0_TBUFCONTROL;
175 op.interface_version = DOM0_INTERFACE_VERSION;
176 if (type == 1) { /* cpu mask */
177 op.u.tbufcontrol.op = DOM0_TBUF_SET_CPU_MASK;
178 op.u.tbufcontrol.cpu_mask = mask;
179 fprintf(stderr, "change cpumask to 0x%x\n", mask);
180 }else if (type == 0) { /* event mask */
181 op.u.tbufcontrol.op = DOM0_TBUF_SET_EVT_MASK;
182 op.u.tbufcontrol.evt_mask = mask;
183 fprintf(stderr, "change evtmask to 0x%x\n", mask);
184 }
186 ret = do_dom0_op(xc_handle, &op);
188 xc_interface_close(xc_handle);
190 if ( ret != 0 )
191 {
192 PERROR("Failure to get trace buffer pointer from Xen and set the new mask");
193 exit(EXIT_FAILURE);
194 }
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 struct t_rec **init_rec_ptrs(struct t_buf **meta, unsigned int num)
241 {
242 int i;
243 struct t_rec **data;
245 data = calloc(num, sizeof(struct t_rec *));
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] = (struct t_rec *)(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 dom0_op_t op;
264 int xc_handle = xc_interface_open();
265 int ret;
267 op.cmd = DOM0_PHYSINFO;
268 op.interface_version = DOM0_INTERFACE_VERSION;
270 ret = do_dom0_op(xc_handle, &op);
272 if ( ret != 0 )
273 {
274 PERROR("Failure to get logical CPU count from Xen");
275 exit(EXIT_FAILURE);
276 }
278 xc_interface_close(xc_handle);
280 return (op.u.physinfo.threads_per_core *
281 op.u.physinfo.cores_per_socket *
282 op.u.physinfo.sockets_per_node *
283 op.u.physinfo.nr_nodes);
284 }
287 /**
288 * monitor_tbufs - monitor the contents of tbufs and output to a file
289 * @logfile: the FILE * representing the file to log to
290 */
291 int monitor_tbufs(FILE *logfile)
292 {
293 int i;
295 void *tbufs_mapped; /* pointer to where the tbufs are mapped */
296 struct t_buf **meta; /* pointers to the trace buffer metadata */
297 struct t_rec **data; /* pointers to the trace buffer data areas
298 * where they are mapped into user space. */
299 unsigned long tbufs_mfn; /* mfn of the tbufs */
300 unsigned int num; /* number of trace buffers / logical CPUS */
301 unsigned long size; /* size of a single trace buffer */
303 int size_in_recs;
305 /* get number of logical CPUs (and therefore number of trace buffers) */
306 num = get_num_cpus();
308 /* setup access to trace buffers */
309 get_tbufs(&tbufs_mfn, &size);
310 tbufs_mapped = map_tbufs(tbufs_mfn, num, size);
312 size_in_recs = (size - sizeof(struct t_buf)) / sizeof(struct t_rec);
314 /* build arrays of convenience ptrs */
315 meta = init_bufs_ptrs(tbufs_mapped, num, size);
316 data = init_rec_ptrs(meta, num);
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, "vmx") == 0){
362 setup->evt_mask |= TRC_VMX;
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 ARGP_KEY_ARG:
416 {
417 if ( state->arg_num == 0 )
418 setup->outfile = arg;
419 else
420 argp_usage(state);
421 }
422 break;
424 default:
425 return ARGP_ERR_UNKNOWN;
426 }
428 return 0;
429 }
431 #define xstr(x) str(x)
432 #define str(x) #x
434 const struct argp_option cmd_opts[] =
435 {
436 { .name = "log-thresh", .key='t', .arg="l",
437 .doc =
438 "Set number, l, of new records required to trigger a write to output "
439 "(default " xstr(NEW_DATA_THRESH) ")." },
441 { .name = "poll-sleep", .key='s', .arg="p",
442 .doc =
443 "Set sleep time, p, in milliseconds between polling the trace buffer "
444 "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
446 { .name = "cpu-mask", .key='c', .arg="c",
447 .doc =
448 "set cpu-mask " },
450 { .name = "evt-mask", .key='e', .arg="e",
451 .doc =
452 "set evt-mask " },
454 {0}
455 };
457 const struct argp parser_def =
458 {
459 .options = cmd_opts,
460 .parser = cmd_parser,
461 .args_doc = "[output file]",
462 .doc =
463 "Tool to capure Xen trace buffer data"
464 "\v"
465 "This tool is used to capture trace buffer data from Xen. The data is "
466 "output in a binary format, in the following order:\n\n"
467 " CPU(uint) TSC(uint64_t) EVENT(uint32_t) D1 D2 D3 D4 D5 "
468 "(all uint32_t)\n\n"
469 "The output should be parsed using the tool xentrace_format, which can "
470 "produce human-readable output in ASCII format."
471 };
474 const char *argp_program_version = "xentrace v1.1";
475 const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
478 int main(int argc, char **argv)
479 {
480 int outfd = 1, ret;
481 FILE *logfile;
482 struct sigaction act;
484 opts.outfile = 0;
485 opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
486 opts.new_data_thresh = NEW_DATA_THRESH;
487 opts.evt_mask = 0;
488 opts.cpu_mask = 0;
490 argp_parse(&parser_def, argc, argv, 0, 0, &opts);
492 if (opts.evt_mask != 0) {
493 set_mask(opts.evt_mask, 0);
494 }
496 if (opts.cpu_mask != 0) {
497 set_mask(opts.cpu_mask, 1);
498 }
500 if ( opts.outfile )
501 outfd = open(opts.outfile, O_WRONLY | O_CREAT | O_LARGEFILE, 0644);
503 if(outfd < 0)
504 {
505 perror("Could not open output file");
506 exit(EXIT_FAILURE);
507 }
509 if(isatty(outfd))
510 {
511 fprintf(stderr, "Cannot output to a TTY, specify a log file.\n");
512 exit(EXIT_FAILURE);
513 }
515 logfile = fdopen(outfd, "w");
517 /* ensure that if we get a signal, we'll do cleanup, then exit */
518 act.sa_handler = close_handler;
519 act.sa_flags = 0;
520 sigemptyset(&act.sa_mask);
521 sigaction(SIGHUP, &act, NULL);
522 sigaction(SIGTERM, &act, NULL);
523 sigaction(SIGINT, &act, NULL);
525 ret = monitor_tbufs(logfile);
527 return ret;
528 }