ia64/xen-unstable

view tools/xentrace/xentrace.c @ 2860:61d139354129

bitkeeper revision 1.1159.157.1 (418934b0-qzq3Mn8ZcEAFEUYycHb2Q)

Device section fixes.
author akw27@labyrinth.cl.cam.ac.uk
date Wed Nov 03 19:42:40 2004 +0000 (2004-11-03)
parents 3f929065a1d1
children 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"
25 #include <xen/trace.h>
27 extern FILE *stderr;
29 /***** Compile time configuration of defaults ********************************/
31 /* when we've got more records than this waiting, we log it to the output */
32 #define NEW_DATA_THRESH 1
34 /* sleep for this long (milliseconds) between checking the trace buffers */
35 #define POLL_SLEEP_MILLIS 100
38 /***** The code **************************************************************/
40 typedef struct settings_st {
41 char *outfile;
42 struct timespec poll_sleep;
43 unsigned long new_data_thresh;
44 } settings_t;
46 settings_t opts;
48 int interrupted = 0; /* gets set if we get a SIGHUP */
50 void close_handler(int signal)
51 {
52 interrupted = 1;
53 }
55 /**
56 * millis_to_timespec - convert a time in milliseconds to a struct timespec
57 * @millis: time interval in milliseconds
58 */
59 struct timespec millis_to_timespec(unsigned long millis)
60 {
61 struct timespec spec;
63 spec.tv_sec = millis / 1000;
64 spec.tv_nsec = (millis % 1000) * 1000;
66 return spec;
67 }
69 /**
70 * write_rec - output a trace record in binary format
71 * @cpu - source buffer CPU ID
72 * @rec - trace record to output
73 * @out - output stream
74 *
75 * Outputs the trace record to a filestream, prepending the CPU ID of the
76 * source trace buffer.
77 */
78 void write_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
79 {
80 fwrite(&cpu, sizeof(cpu), 1, out);
81 fwrite(rec, sizeof(*rec), 1, out);
82 }
84 /**
85 * get_tbufs - get pointer to and size of the trace buffers
86 * @mach_addr: location to store machine address if the trace buffers to
87 * @size: location to store the size of a trace buffer to
88 *
89 * Gets the machine address of the trace pointer area and the size of the
90 * per CPU buffers.
91 */
92 void get_tbufs(unsigned long *mach_addr, unsigned long *size)
93 {
94 int ret;
95 dom0_op_t op; /* dom0 op we'll build */
96 int xc_handle = xc_interface_open(); /* for accessing control interface */
98 op.cmd = DOM0_GETTBUFS;
99 op.interface_version = DOM0_INTERFACE_VERSION;
101 ret = do_dom0_op(xc_handle, &op);
103 xc_interface_close(xc_handle);
105 if ( ret != 0 )
106 {
107 PERROR("Failure to get trace buffer pointer from Xen");
108 exit(EXIT_FAILURE);
109 }
111 *mach_addr = op.u.gettbufs.mach_addr;
112 *size = op.u.gettbufs.size;
113 }
115 /**
116 * map_tbufs - memory map Xen trace buffers into user space
117 * @tbufs: machine address of the trace buffers
118 * @num: number of trace buffers to map
119 * @size: size of each trace buffer
120 *
121 * Maps the Xen trace buffers them into process address space.
122 */
123 struct t_buf *map_tbufs(unsigned long tbufs_mach, unsigned int num,
124 unsigned long size)
125 {
126 int xc_handle; /* file descriptor for /proc/xen/privcmd */
127 struct t_buf *tbufs_mapped;
129 xc_handle = xc_interface_open();
131 if ( xc_handle < 0 )
132 {
133 PERROR("Open /proc/xen/privcmd when mapping trace buffers\n");
134 exit(EXIT_FAILURE);
135 }
137 tbufs_mapped = xc_map_foreign_range(xc_handle, 0 /* Dom 0 ID */,
138 size * num, PROT_READ,
139 tbufs_mach >> PAGE_SHIFT);
141 xc_interface_close(xc_handle);
143 if ( tbufs_mapped == 0 )
144 {
145 PERROR("Failed to mmap trace buffers");
146 exit(EXIT_FAILURE);
147 }
149 return (struct t_buf *)tbufs_mapped;
150 }
153 /**
154 * init_bufs_ptrs - initialises an array of pointers to the trace buffers
155 * @bufs_mapped: the userspace address where the trace buffers are mapped
156 * @num: number of trace buffers
157 * @size: trace buffer size
158 *
159 * Initialises an array of pointers to individual trace buffers within the
160 * mapped region containing all trace buffers.
161 */
162 struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
163 unsigned long size)
164 {
165 int i;
166 struct t_buf **user_ptrs;
168 user_ptrs = (struct t_buf **)calloc(num, sizeof(struct t_buf *));
169 if ( user_ptrs == NULL )
170 {
171 PERROR( "Failed to allocate memory for buffer pointers\n");
172 exit(EXIT_FAILURE);
173 }
175 /* initialise pointers to the trace buffers - given the size of a trace
176 * buffer and the value of bufs_maped, we can easily calculate these */
177 for ( i = 0; i<num; i++ )
178 user_ptrs[i] = (struct t_buf *)(
179 (unsigned long)bufs_mapped + size * i);
181 return user_ptrs;
182 }
185 /**
186 * init_rec_ptrs - initialises data area pointers to locations in user space
187 * @tbufs_mach: machine base address of the trace buffer area
188 * @tbufs_mapped: user virtual address of base of trace buffer area
189 * @meta: array of user-space pointers to struct t_buf's of metadata
190 * @num: number of trace buffers
191 *
192 * Initialises data area pointers to the locations that data areas have been
193 * mapped in user space. Note that the trace buffer metadata contains machine
194 * pointers - the array returned allows more convenient access to them.
195 */
196 struct t_rec **init_rec_ptrs(unsigned long tbufs_mach,
197 struct t_buf *tbufs_mapped,
198 struct t_buf **meta,
199 unsigned int num)
200 {
201 int i;
202 struct t_rec **data;
204 data = calloc(num, sizeof(struct t_rec *));
205 if ( data == NULL )
206 {
207 PERROR("Failed to allocate memory for data pointers\n");
208 exit(EXIT_FAILURE);
209 }
211 for ( i = 0; i<num; i++ )
212 data[i] = (struct t_rec *)(meta[i]->data - tbufs_mach
213 + (unsigned long)tbufs_mapped);
215 return data;
216 }
218 /**
219 * init_tail_idxs - initialise an array of tail indexes
220 * @bufs: array of pointers to trace buffer metadata
221 * @num: number of trace buffers
222 *
223 * The tail indexes indicate where we're read to so far in the data array of a
224 * trace buffer. Each entry in this table corresponds to the tail index for a
225 * particular trace buffer.
226 */
227 unsigned long *init_tail_idxs(struct t_buf **bufs, unsigned int num)
228 {
229 int i;
230 unsigned long *tails = calloc(num, sizeof(unsigned int));
232 if ( tails == NULL )
233 {
234 PERROR("Failed to allocate memory for tail pointers\n");
235 exit(EXIT_FAILURE);
236 }
238 for ( i = 0; i<num; i++ )
239 tails[i] = bufs[i]->head;
241 return tails;
242 }
244 /**
245 * get_num_cpus - get the number of logical CPUs
246 */
247 unsigned int get_num_cpus()
248 {
249 dom0_op_t op;
250 int xc_handle = xc_interface_open();
251 int ret;
253 op.cmd = DOM0_PHYSINFO;
254 op.interface_version = DOM0_INTERFACE_VERSION;
256 ret = do_dom0_op(xc_handle, &op);
258 if ( ret != 0 )
259 {
260 PERROR("Failure to get logical CPU count from Xen");
261 exit(EXIT_FAILURE);
262 }
264 xc_interface_close(xc_handle);
266 return op.u.physinfo.ht_per_core * op.u.physinfo.cores;
267 }
270 /**
271 * monitor_tbufs - monitor the contents of tbufs and output to a file
272 * @logfile: the FILE * representing the file to log to
273 */
274 int monitor_tbufs(FILE *logfile)
275 {
276 int i;
278 void *tbufs_mapped; /* pointer to where the tbufs are mapped */
279 struct t_buf **meta; /* pointers to the trace buffer metadata */
280 struct t_rec **data; /* pointers to the trace buffer data areas
281 * where they are mapped into user space. */
282 unsigned long *cons; /* store tail indexes for the trace buffers */
283 unsigned long tbufs_mach; /* machine address of the tbufs */
284 unsigned int num; /* number of trace buffers / logical CPUS */
285 unsigned long size; /* size of a single trace buffer */
287 int size_in_recs;
289 /* get number of logical CPUs (and therefore number of trace buffers) */
290 num = get_num_cpus();
292 /* setup access to trace buffers */
293 get_tbufs(&tbufs_mach, &size);
294 tbufs_mapped = map_tbufs(tbufs_mach, num, size);
296 size_in_recs = (size / sizeof(struct t_rec) )-1;
298 /* build arrays of convenience ptrs */
299 meta = init_bufs_ptrs (tbufs_mapped, num, size);
300 data = init_rec_ptrs (tbufs_mach, tbufs_mapped, meta, num);
301 cons = init_tail_idxs (meta, num);
303 /* now, scan buffers for events */
304 while ( !interrupted )
305 {
306 for ( i = 0; ( i < num ) && !interrupted; i++ )
307 while( cons[i] != meta[i]->head )
308 {
309 write_rec(i, data[i] + (cons[i] % size_in_recs), logfile);
310 cons[i]++;
311 }
313 nanosleep(&opts.poll_sleep, NULL);
314 }
316 /* cleanup */
317 free(meta);
318 free(data);
319 free(cons);
320 /* don't need to munmap - cleanup is automatic */
321 fclose(logfile);
323 return 0;
324 }
327 /******************************************************************************
328 * Various declarations / definitions GNU argp needs to do its work
329 *****************************************************************************/
332 /* command parser for GNU argp - see GNU docs for more info */
333 error_t cmd_parser(int key, char *arg, struct argp_state *state)
334 {
335 settings_t *setup = (settings_t *)state->input;
337 switch ( key )
338 {
339 case 't': /* set new records threshold for logging */
340 {
341 char *inval;
342 setup->new_data_thresh = strtol(arg, &inval, 0);
343 if ( inval == arg )
344 argp_usage(state);
345 }
346 break;
348 case 's': /* set sleep time (given in milliseconds) */
349 {
350 char *inval;
351 setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0));
352 if ( inval == arg )
353 argp_usage(state);
354 }
355 break;
357 case ARGP_KEY_ARG:
358 {
359 if ( state->arg_num == 0 )
360 setup->outfile = arg;
361 else
362 argp_usage(state);
363 }
364 break;
366 default:
367 return ARGP_ERR_UNKNOWN;
368 }
370 return 0;
371 }
373 #define xstr(x) str(x)
374 #define str(x) #x
376 const struct argp_option cmd_opts[] =
377 {
378 { .name = "log-thresh", .key='t', .arg="l",
379 .doc =
380 "Set number, l, of new records required to trigger a write to output "
381 "(default " xstr(NEW_DATA_THRESH) ")." },
383 { .name = "poll-sleep", .key='s', .arg="p",
384 .doc =
385 "Set sleep time, p, in milliseconds between polling the trace buffer "
386 "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
388 {0}
389 };
391 const struct argp parser_def =
392 {
393 .options = cmd_opts,
394 .parser = cmd_parser,
395 .args_doc = "[output file]",
396 .doc =
397 "Tool to capure Xen trace buffer data"
398 "\v"
399 "This tool is used to capture trace buffer data from Xen. The data is "
400 "output in a binary format, in the following order:\n\n"
401 " CPU(uint) TSC(u64) EVENT(u32) D1 D2 D3 D4 D5 (all u32)\n\n"
402 "The output should be parsed using the tool xentrace_format, which can "
403 "produce human-readable output in ASCII format."
404 };
407 const char *argp_program_version = "xentrace v1.1";
408 const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
411 int main(int argc, char **argv)
412 {
413 int outfd = 1, ret;
414 FILE *logfile;
415 struct sigaction act;
417 opts.outfile = 0;
418 opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
419 opts.new_data_thresh = NEW_DATA_THRESH;
421 argp_parse(&parser_def, argc, argv, 0, 0, &opts);
423 if ( opts.outfile )
424 outfd = open(opts.outfile, O_WRONLY | O_CREAT);
426 if(outfd < 0)
427 {
428 perror("Could not open output file");
429 exit(EXIT_FAILURE);
430 }
432 if(isatty(outfd))
433 {
434 fprintf(stderr, "Cannot output to a TTY, specify a log file.\n");
435 exit(EXIT_FAILURE);
436 }
438 logfile = fdopen(outfd, "w");
440 /* ensure that if we get a signal, we'll do cleanup, then exit */
441 act.sa_handler = close_handler;
442 sigaction(SIGHUP, &act, 0);
443 sigaction(SIGTERM, &act, 0);
444 sigaction(SIGINT, &act, 0);
446 ret = monitor_tbufs(logfile);
448 return ret;
449 }