ia64/xen-unstable

view tools/xentrace/xentrace.c @ 2621:9402048e2325

bitkeeper revision 1.1159.1.218 (416a8128OiHXHyk_Sy8FsA0YUQcEnA)

Merge freefall.cl.cam.ac.uk:/auto/groups/xeno/users/cl349/BK/xeno.bk-26dom0
into freefall.cl.cam.ac.uk:/local/scratch/cl349/xeno.bk-26dom0
author cl349@freefall.cl.cam.ac.uk
date Mon Oct 11 12:48:40 2004 +0000 (2004-10-11)
parents 1e99cd1cb3a3
children a5c335665fd6 3f929065a1d1
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 * @mach_addr: location to store machine address if the trace buffers to
89 * @size: location to store the size of a trace buffer to
90 *
91 * Gets the machine address of the trace pointer area and the size of the
92 * per CPU buffers.
93 */
94 void get_tbufs(unsigned long *mach_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 *mach_addr = op.u.gettbufs.mach_addr;
114 *size = op.u.gettbufs.size;
115 }
117 /**
118 * map_tbufs - memory map Xen trace buffers into user space
119 * @tbufs: machine 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.
124 */
125 struct t_buf *map_tbufs(unsigned long tbufs_mach, unsigned int num,
126 unsigned long size)
127 {
128 int xc_handle; /* file descriptor for /proc/xen/privcmd */
129 struct t_buf *tbufs_mapped;
131 xc_handle = xc_interface_open();
133 if ( xc_handle < 0 )
134 {
135 PERROR("Open /proc/xen/privcmd when mapping trace buffers\n");
136 exit(EXIT_FAILURE);
137 }
139 tbufs_mapped = xc_map_foreign_range(xc_handle, 0 /* Dom 0 ID */,
140 size * num, PROT_READ,
141 tbufs_mach >> PAGE_SHIFT);
143 xc_interface_close(xc_handle);
145 if ( tbufs_mapped == 0 )
146 {
147 PERROR("Failed to mmap trace buffers");
148 exit(EXIT_FAILURE);
149 }
151 return (struct t_buf *)tbufs_mapped;
152 }
155 /**
156 * init_bufs_ptrs - initialises an array of pointers to the trace buffers
157 * @bufs_mapped: the userspace address where the trace buffers are mapped
158 * @num: number of trace buffers
159 * @size: trace buffer size
160 *
161 * Initialises an array of pointers to individual trace buffers within the
162 * mapped region containing all trace buffers.
163 */
164 struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
165 unsigned long size)
166 {
167 int i;
168 struct t_buf **user_ptrs;
170 user_ptrs = (struct t_buf **)calloc(num, sizeof(struct t_buf *));
171 if ( user_ptrs == NULL )
172 {
173 PERROR( "Failed to allocate memory for buffer pointers\n");
174 exit(EXIT_FAILURE);
175 }
177 /* initialise pointers to the trace buffers - given the size of a trace
178 * buffer and the value of bufs_maped, we can easily calculate these */
179 for ( i = 0; i<num; i++ )
180 user_ptrs[i] = (struct t_buf *)(
181 (unsigned long)bufs_mapped + size * i);
183 return user_ptrs;
184 }
187 /**
188 * init_rec_ptrs - initialises data area pointers to locations in user space
189 * @tbufs_mach: machine base address of the trace buffer area
190 * @tbufs_mapped: user virtual address of base of trace buffer area
191 * @meta: array of user-space pointers to struct t_buf's of metadata
192 * @num: number of trace buffers
193 *
194 * Initialises data area pointers to the locations that data areas have been
195 * mapped in user space. Note that the trace buffer metadata contains machine
196 * pointers - the array returned allows more convenient access to them.
197 */
198 struct t_rec **init_rec_ptrs(unsigned long tbufs_mach,
199 struct t_buf *tbufs_mapped,
200 struct t_buf **meta,
201 unsigned int num)
202 {
203 int i;
204 struct t_rec **data;
206 data = calloc(num, sizeof(struct t_rec *));
207 if ( data == NULL )
208 {
209 PERROR("Failed to allocate memory for data pointers\n");
210 exit(EXIT_FAILURE);
211 }
213 for ( i = 0; i<num; i++ )
214 data[i] = (struct t_rec *)(meta[i]->data - tbufs_mach
215 + (unsigned long)tbufs_mapped);
217 return data;
218 }
220 /**
221 * init_tail_idxs - initialise an array of tail indexes
222 * @bufs: array of pointers to trace buffer metadata
223 * @num: number of trace buffers
224 *
225 * The tail indexes indicate where we're read to so far in the data array of a
226 * trace buffer. Each entry in this table corresponds to the tail index for a
227 * particular trace buffer.
228 */
229 unsigned long *init_tail_idxs(struct t_buf **bufs, unsigned int num)
230 {
231 int i;
232 unsigned long *tails = calloc(num, sizeof(unsigned int));
234 if ( tails == NULL )
235 {
236 PERROR("Failed to allocate memory for tail pointers\n");
237 exit(EXIT_FAILURE);
238 }
240 for ( i = 0; i<num; i++ )
241 tails[i] = bufs[i]->head;
243 return tails;
244 }
246 /**
247 * get_num_cpus - get the number of logical CPUs
248 */
249 unsigned int get_num_cpus()
250 {
251 dom0_op_t op;
252 int xc_handle = xc_interface_open();
253 int ret;
255 op.cmd = DOM0_PHYSINFO;
256 op.interface_version = DOM0_INTERFACE_VERSION;
258 ret = do_dom0_op(xc_handle, &op);
260 if ( ret != 0 )
261 {
262 PERROR("Failure to get logical CPU count from Xen");
263 exit(EXIT_FAILURE);
264 }
266 xc_interface_close(xc_handle);
268 return op.u.physinfo.ht_per_core * op.u.physinfo.cores;
269 }
272 /**
273 * monitor_tbufs - monitor the contents of tbufs and output to a file
274 * @logfile: the FILE * representing the file to log to
275 */
276 int monitor_tbufs(FILE *logfile)
277 {
278 int i;
280 void *tbufs_mapped; /* pointer to where the tbufs are mapped */
281 struct t_buf **meta; /* pointers to the trace buffer metadata */
282 struct t_rec **data; /* pointers to the trace buffer data areas
283 * where they are mapped into user space. */
284 unsigned long *cons; /* store tail indexes for the trace buffers */
285 unsigned long tbufs_mach; /* machine address of the tbufs */
286 unsigned int num; /* number of trace buffers / logical CPUS */
287 unsigned long size; /* size of a single trace buffer */
289 int size_in_recs;
291 /* get number of logical CPUs (and therefore number of trace buffers) */
292 num = get_num_cpus();
294 /* setup access to trace buffers */
295 get_tbufs(&tbufs_mach, &size);
296 tbufs_mapped = map_tbufs(tbufs_mach, num, size);
298 size_in_recs = (size / sizeof(struct t_rec) )-1;
300 /* build arrays of convenience ptrs */
301 meta = init_bufs_ptrs (tbufs_mapped, num, size);
302 data = init_rec_ptrs (tbufs_mach, tbufs_mapped, meta, num);
303 cons = init_tail_idxs (meta, num);
305 /* now, scan buffers for events */
306 while ( !interrupted )
307 {
308 for ( i = 0; ( i < num ) && !interrupted; i++ )
309 while( cons[i] != meta[i]->head )
310 {
311 write_rec(i, data[i] + (cons[i] % size_in_recs), logfile);
312 cons[i]++;
313 }
315 nanosleep(&opts.poll_sleep, NULL);
316 }
318 /* cleanup */
319 free(meta);
320 free(data);
321 free(cons);
322 /* don't need to munmap - cleanup is automatic */
323 fclose(logfile);
325 return 0;
326 }
329 /******************************************************************************
330 * Various declarations / definitions GNU argp needs to do its work
331 *****************************************************************************/
334 /* command parser for GNU argp - see GNU docs for more info */
335 error_t cmd_parser(int key, char *arg, struct argp_state *state)
336 {
337 settings_t *setup = (settings_t *)state->input;
339 switch ( key )
340 {
341 case 't': /* set new records threshold for logging */
342 {
343 char *inval;
344 setup->new_data_thresh = strtol(arg, &inval, 0);
345 if ( inval == arg )
346 argp_usage(state);
347 }
348 break;
350 case 's': /* set sleep time (given in milliseconds) */
351 {
352 char *inval;
353 setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0));
354 if ( inval == arg )
355 argp_usage(state);
356 }
357 break;
359 case ARGP_KEY_ARG:
360 {
361 if ( state->arg_num == 0 )
362 setup->outfile = arg;
363 else
364 argp_usage(state);
365 }
366 break;
368 default:
369 return ARGP_ERR_UNKNOWN;
370 }
372 return 0;
373 }
375 #define xstr(x) str(x)
376 #define str(x) #x
378 const struct argp_option cmd_opts[] =
379 {
380 { .name = "log-thresh", .key='t', .arg="l",
381 .doc =
382 "Set number, l, of new records required to trigger a write to output "
383 "(default " xstr(NEW_DATA_THRESH) ")." },
385 { .name = "poll-sleep", .key='s', .arg="p",
386 .doc =
387 "Set sleep time, p, in milliseconds between polling the trace buffer "
388 "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
390 {0}
391 };
393 const struct argp parser_def =
394 {
395 .options = cmd_opts,
396 .parser = cmd_parser,
397 .args_doc = "[output file]",
398 .doc =
399 "Tool to capure Xen trace buffer data"
400 "\v"
401 "This tool is used to capture trace buffer data from Xen. The data is "
402 "output in a binary format, in the following order:\n\n"
403 " CPU(uint) TSC(u64) EVENT(u32) D1 D2 D3 D4 D5 (all u32)\n\n"
404 "The output should be parsed using the tool xentrace_format, which can "
405 "produce human-readable output in ASCII format."
406 };
409 const char *argp_program_version = "xentrace v1.1";
410 const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
413 int main(int argc, char **argv)
414 {
415 int outfd = 1, ret;
416 FILE *logfile;
417 struct sigaction act;
419 opts.outfile = 0;
420 opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
421 opts.new_data_thresh = NEW_DATA_THRESH;
423 argp_parse(&parser_def, argc, argv, 0, 0, &opts);
425 if ( opts.outfile )
426 outfd = open(opts.outfile, O_WRONLY | O_CREAT);
428 if(outfd < 0)
429 {
430 perror("Could not open output file");
431 exit(EXIT_FAILURE);
432 }
434 if(isatty(outfd))
435 {
436 fprintf(stderr, "Cannot output to a TTY, specify a log file.\n");
437 exit(EXIT_FAILURE);
438 }
440 logfile = fdopen(outfd, "w");
442 /* ensure that if we get a signal, we'll do cleanup, then exit */
443 act.sa_handler = close_handler;
444 sigaction(SIGHUP, &act, 0);
445 sigaction(SIGTERM, &act, 0);
446 sigaction(SIGINT, &act, 0);
448 ret = monitor_tbufs(logfile);
450 return ret;
451 }