ia64/xen-unstable

view tools/xentrace/xentrace.c @ 2020:4fc62e2234f4

bitkeeper revision 1.1108.33.23 (410d0a8aWM5n48Ku4nUKCSOzCx9B2A)

sync with 2.4 block backend driver:
always print error messages
author cl349@freefall.cl.cam.ac.uk
date Sun Aug 01 15:21:46 2004 +0000 (2004-08-01)
parents 236a9f2698a3
children 6f4e8aa5e998 691d8544fd52 c7b0154dff73 0a4b76b6b5a0
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 <sys/mman.h>
15 #include <stdio.h>
16 #include <sys/types.h>
17 #include <sys/stat.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 /* from xen/include/hypervisor-ifs */
27 #include <trace.h>
29 extern FILE *stderr;
31 /***** Compile time configuration of defaults ********************************/
33 /* when we've got more records than this waiting, we log it to the output */
34 #define NEW_DATA_THRESH 1
36 /* sleep for this long (milliseconds) between checking the trace buffers */
37 #define POLL_SLEEP_MILLIS 100
40 /***** The code **************************************************************/
42 typedef struct settings_st {
43 char *outfile;
44 struct timespec poll_sleep;
45 unsigned long new_data_thresh;
46 } settings_t;
48 settings_t opts;
50 int interrupted = 0; /* gets set if we get a SIGHUP */
52 void close_handler(int signal)
53 {
54 interrupted = 1;
55 }
57 /**
58 * millis_to_timespec - convert a time in milliseconds to a struct timespec
59 * @millis: time interval in milliseconds
60 */
61 struct timespec millis_to_timespec(unsigned long millis)
62 {
63 struct timespec spec;
65 spec.tv_sec = millis / 1000;
66 spec.tv_nsec = (millis % 1000) * 1000;
68 return spec;
69 }
71 /**
72 * write_rec - output a trace record in binary format
73 * @cpu - source buffer CPU ID
74 * @rec - trace record to output
75 * @out - output stream
76 *
77 * Outputs the trace record to a filestream, prepending the CPU ID of the
78 * source trace buffer.
79 */
80 void write_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
81 {
82 fwrite(&cpu, sizeof(cpu), 1, out);
83 fwrite(rec, sizeof(*rec), 1, out);
84 }
86 /**
87 * get_tbufs - get pointer to and size of the trace buffers
88 * @phys_addr: location to store physical address if the trace buffers to
89 * @size: location to store the size of a trace buffer to
90 *
91 * Gets the physical address of the trace pointer area and the size of the
92 * per CPU buffers.
93 */
94 void get_tbufs(unsigned long *phys_addr, unsigned long *size)
95 {
96 int ret;
97 dom0_op_t op; /* dom0 op we'll build */
98 int xc_handle = xc_interface_open(); /* for accessing control interface */
100 op.cmd = DOM0_GETTBUFS;
101 op.interface_version = DOM0_INTERFACE_VERSION;
103 ret = do_dom0_op(xc_handle, &op);
105 xc_interface_close(xc_handle);
107 if ( ret != 0 )
108 {
109 PERROR("Failure to get trace buffer pointer from Xen");
110 exit(EXIT_FAILURE);
111 }
113 *phys_addr = op.u.gettbufs.phys_addr;
114 *size = op.u.gettbufs.size;
115 }
117 /**
118 * map_tbufs - memory map Xen trace buffers into user space
119 * @tbufs: physical address of the trace buffers
120 * @num: number of trace buffers to map
121 * @size: size of each trace buffer
122 *
123 * Maps the Xen trace buffers them into process address space by memory mapping
124 * /dev/mem. Returns the location the buffers have been mapped to.
125 */
126 struct t_buf *map_tbufs(unsigned long tbufs_phys, unsigned int num,
127 unsigned long size)
128 {
129 int dm_fd; /* file descriptor for /dev/mem */
130 struct t_buf *tbufs_mapped;
132 dm_fd = open("/dev/mem", O_RDONLY);
134 if ( dm_fd < 0 )
135 {
136 PERROR("Open /dev/mem when mapping trace buffers\n");
137 exit(EXIT_FAILURE);
138 }
140 tbufs_mapped = (struct t_buf *)mmap(NULL, size * num,
141 PROT_READ, MAP_SHARED,
142 dm_fd, (off_t)tbufs_phys);
144 close(dm_fd);
146 if ( tbufs_mapped == MAP_FAILED )
147 {
148 PERROR("Failed to mmap trace buffers");
149 exit(EXIT_FAILURE);
150 }
152 return (struct t_buf *)tbufs_mapped;
153 }
156 /**
157 * init_bufs_ptrs - initialises an array of pointers to the trace buffers
158 * @bufs_mapped: the userspace address where the trace buffers are mapped
159 * @num: number of trace buffers
160 * @size: trace buffer size
161 *
162 * Initialises an array of pointers to individual trace buffers within the
163 * mapped region containing all trace buffers.
164 */
165 struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
166 unsigned long size)
167 {
168 int i;
169 struct t_buf **user_ptrs;
171 user_ptrs = (struct t_buf **)calloc(num, sizeof(struct t_buf *));
172 if ( user_ptrs == NULL )
173 {
174 PERROR( "Failed to allocate memory for buffer pointers\n");
175 exit(EXIT_FAILURE);
176 }
178 /* initialise pointers to the trace buffers - given the size of a trace
179 * buffer and the value of bufs_maped, we can easily calculate these */
180 for ( i = 0; i<num; i++ )
181 user_ptrs[i] = (struct t_buf *)(
182 (unsigned long)bufs_mapped + size * i);
184 return user_ptrs;
185 }
188 /**
189 * init_rec_ptrs - initialises data area pointers to locations in user space
190 * @tbufs_phys: physical base address of the trace buffer area
191 * @tbufs_mapped: user virtual address of base of trace buffer area
192 * @meta: array of user-space pointers to struct t_buf's of metadata
193 * @num: number of trace buffers
194 *
195 * Initialises data area pointers to the locations that data areas have been
196 * mapped in user space. Note that the trace buffer metadata contains physical
197 * pointers - the array returned allows more convenient access to them.
198 */
199 struct t_rec **init_rec_ptrs(unsigned long tbufs_phys,
200 struct t_buf *tbufs_mapped,
201 struct t_buf **meta,
202 unsigned int num)
203 {
204 int i;
205 struct t_rec **data;
207 data = calloc(num, sizeof(struct t_rec *));
208 if ( data == NULL )
209 {
210 PERROR("Failed to allocate memory for data pointers\n");
211 exit(EXIT_FAILURE);
212 }
214 for ( i = 0; i<num; i++ )
215 data[i] = (struct t_rec *)((unsigned long)meta[i]->data -
216 tbufs_phys + (unsigned long)tbufs_mapped);
218 return data;
219 }
221 /**
222 * init_tail_idxs - initialise an array of tail indexes
223 * @bufs: array of pointers to trace buffer metadata
224 * @num: number of trace buffers
225 *
226 * The tail indexes indicate where we're read to so far in the data array of a
227 * trace buffer. Each entry in this table corresponds to the tail index for a
228 * particular trace buffer.
229 */
230 unsigned long *init_tail_idxs(struct t_buf **bufs, unsigned int num)
231 {
232 int i;
233 unsigned long *tails = calloc(num, sizeof(unsigned int));
235 if ( tails == NULL )
236 {
237 PERROR("Failed to allocate memory for tail pointers\n");
238 exit(EXIT_FAILURE);
239 }
241 for ( i = 0; i<num; i++ )
242 tails[i] = bufs[i]->head;
244 return tails;
245 }
247 /**
248 * get_num_cpus - get the number of logical CPUs
249 */
250 unsigned int get_num_cpus()
251 {
252 dom0_op_t op;
253 int xc_handle = xc_interface_open();
254 int ret;
256 op.cmd = DOM0_PHYSINFO;
257 op.interface_version = DOM0_INTERFACE_VERSION;
259 ret = do_dom0_op(xc_handle, &op);
261 if ( ret != 0 )
262 {
263 PERROR("Failure to get logical CPU count from Xen");
264 exit(EXIT_FAILURE);
265 }
267 xc_interface_close(xc_handle);
269 return op.u.physinfo.ht_per_core * op.u.physinfo.cores;
270 }
273 /**
274 * monitor_tbufs - monitor the contents of tbufs and output to a file
275 * @logfile: the FILE * representing the file to log to
276 */
277 int monitor_tbufs(FILE *logfile)
278 {
279 int i;
281 void *tbufs_mapped; /* pointer to where the tbufs are mapped */
282 struct t_buf **meta; /* pointers to the trace buffer metadata */
283 struct t_rec **data; /* pointers to the trace buffer data areas
284 * where they are mapped into user space. */
285 unsigned long *cons; /* store tail indexes for the trace buffers */
286 unsigned long tbufs_phys; /* physical address of the tbufs */
287 unsigned int num; /* number of trace buffers / logical CPUS */
288 unsigned long size; /* size of a single trace buffer */
290 int size_in_recs;
292 /* get number of logical CPUs (and therefore number of trace buffers) */
293 num = get_num_cpus();
295 /* setup access to trace buffers */
296 get_tbufs(&tbufs_phys, &size);
297 tbufs_mapped = map_tbufs(tbufs_phys, num, size);
299 size_in_recs = (size / sizeof(struct t_rec) )-1;
301 /* build arrays of convenience ptrs */
302 meta = init_bufs_ptrs (tbufs_mapped, num, size);
303 data = init_rec_ptrs (tbufs_phys, tbufs_mapped, meta, num);
304 cons = init_tail_idxs (meta, num);
306 /* now, scan buffers for events */
307 while ( !interrupted )
308 {
309 for ( i = 0; ( i < num ) && !interrupted; i++ )
310 {
311 /* printf("XX%d: cons=%ld head=%ld %p\n", i,
312 cons[i], meta[i]->head, data[i] + (cons[i] % size_in_recs) );
313 */
314 while( cons[i] != meta[i]->head )
315 {
316 /*
317 if( (cons[i] % 6 ) == 0 )
318 printf("%d: cons=%ld head=%ld %p\n", i,
319 cons[i], meta[i]->head, data[i] + (cons[i] % size_in_recs) );
320 */
321 write_rec(i, data[i] + (cons[i] % size_in_recs), logfile);
322 cons[i]++;
323 }
325 }
326 nanosleep(&opts.poll_sleep, NULL);
327 }
329 /* cleanup */
330 free(meta);
331 free(data);
332 free(cons);
333 /* don't need to munmap - cleanup is automatic */
334 fclose(logfile);
336 return 0;
337 }
340 /******************************************************************************
341 * Various declarations / definitions GNU argp needs to do its work
342 *****************************************************************************/
345 /* command parser for GNU argp - see GNU docs for more info */
346 error_t cmd_parser(int key, char *arg, struct argp_state *state)
347 {
348 settings_t *setup = (settings_t *)state->input;
350 switch ( key )
351 {
352 case 't': /* set new records threshold for logging */
353 {
354 char *inval;
355 setup->new_data_thresh = strtol(arg, &inval, 0);
356 if ( inval == arg )
357 argp_usage(state);
358 }
359 break;
361 case 's': /* set sleep time (given in milliseconds) */
362 {
363 char *inval;
364 setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0));
365 if ( inval == arg )
366 argp_usage(state);
367 }
368 break;
370 case ARGP_KEY_ARG:
371 {
372 if ( state->arg_num == 0 )
373 setup->outfile = arg;
374 else
375 argp_usage(state);
376 }
377 break;
379 default:
380 return ARGP_ERR_UNKNOWN;
381 }
383 return 0;
384 }
386 #define xstr(x) str(x)
387 #define str(x) #x
389 const struct argp_option cmd_opts[] =
390 {
391 { .name = "log-thresh", .key='t', .arg="l",
392 .doc =
393 "Set number, l, of new records required to trigger a write to output "
394 "(default " xstr(NEW_DATA_THRESH) ")." },
396 { .name = "poll-sleep", .key='s', .arg="p",
397 .doc =
398 "Set sleep time, p, in milliseconds between polling the trace buffer "
399 "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
401 {0}
402 };
404 const struct argp parser_def =
405 {
406 .options = cmd_opts,
407 .parser = cmd_parser,
408 .args_doc = "[output file]",
409 .doc =
410 "Tool to capure Xen trace buffer data"
411 "\v"
412 "This tool is used to capture trace buffer data from Xen. The data is "
413 "output in a binary format, in the following order:\n\n"
414 " CPU(uint) TSC(u64) EVENT(u32) D1 D2 D3 D4 D5 (all u32)\n\n"
415 "The output should be parsed using the tool xentrace_format, which can "
416 "produce human-readable output in ASCII format."
417 };
420 const char *argp_program_version = "xentrace v1.1";
421 const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
424 int main(int argc, char **argv)
425 {
426 int outfd = 1, ret;
427 FILE *logfile;
428 struct sigaction act;
430 opts.outfile = 0;
431 opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
432 opts.new_data_thresh = NEW_DATA_THRESH;
434 argp_parse(&parser_def, argc, argv, 0, 0, &opts);
436 if ( opts.outfile )
437 outfd = open(opts.outfile, O_WRONLY | O_CREAT);
439 if(outfd < 0)
440 {
441 perror("Could not open output file");
442 exit(EXIT_FAILURE);
443 }
445 if(isatty(outfd))
446 {
447 fprintf(stderr, "Cannot output to a TTY, specify a log file.\n");
448 exit(EXIT_FAILURE);
449 }
451 logfile = fdopen(outfd, "w");
453 /* ensure that if we get a signal, we'll do cleanup, then exit */
454 act.sa_handler = close_handler;
455 sigaction(SIGHUP, &act, 0);
456 sigaction(SIGTERM, &act, 0);
457 sigaction(SIGINT, &act, 0);
459 ret = monitor_tbufs(logfile);
461 return ret;
462 }