source: trunk/packages/xen-3.1/xen-3.1/tools/xentrace/xentrace.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 14.6 KB
Line 
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 */
11
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>
25
26#include <xen/xen.h>
27#include <xen/trace.h>
28
29#include <xenctrl.h>
30
31#define PERROR(_m, _a...)                                       \
32do {                                                            \
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)
38
39extern FILE *stderr;
40
41/***** Compile time configuration of defaults ********************************/
42
43/* when we've got more records than this waiting, we log it to the output */
44#define NEW_DATA_THRESH 1
45
46/* sleep for this long (milliseconds) between checking the trace buffers */
47#define POLL_SLEEP_MILLIS 100
48
49#define DEFAULT_TBUF_SIZE 20
50/***** The code **************************************************************/
51
52typedef 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    int discard:1;
60} settings_t;
61
62settings_t opts;
63
64int interrupted = 0; /* gets set if we get a SIGHUP */
65
66void close_handler(int signal)
67{
68    interrupted = 1;
69}
70
71/**
72 * millis_to_timespec - convert a time in milliseconds to a struct timespec
73 * @millis:             time interval in milliseconds
74 */
75struct timespec millis_to_timespec(unsigned long millis)
76{
77    struct timespec spec;
78   
79    spec.tv_sec = millis / 1000;
80    spec.tv_nsec = (millis % 1000) * 1000;
81
82    return spec;
83}
84
85/**
86 * write_rec - output a trace record in binary format
87 * @cpu      - source buffer CPU ID
88 * @rec      - trace record to output
89 * @out      - output stream
90 *
91 * Outputs the trace record to a filestream, prepending the CPU ID of the
92 * source trace buffer.
93 */
94void write_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
95{
96    size_t written = 0;
97    written += fwrite(&cpu, sizeof(cpu), 1, out);
98    written += fwrite(rec, sizeof(*rec), 1, out);
99    if ( written != 2 )
100    {
101        PERROR("Failed to write trace record");
102        exit(EXIT_FAILURE);
103    }
104}
105
106static void get_tbufs(unsigned long *mfn, unsigned long *size)
107{
108    int xc_handle = xc_interface_open();
109    int ret;
110
111    if ( xc_handle < 0 ) 
112    {
113        exit(EXIT_FAILURE);
114    }
115
116    if(!opts.tbuf_size)
117      opts.tbuf_size = DEFAULT_TBUF_SIZE;
118
119    ret = xc_tbuf_enable(xc_handle, opts.tbuf_size, mfn, size);
120
121    if ( ret != 0 )
122    {
123        perror("Couldn't enable trace buffers");
124        exit(1);
125    }
126
127    xc_interface_close(xc_handle);
128}
129
130/**
131 * map_tbufs - memory map Xen trace buffers into user space
132 * @tbufs_mfn: mfn of the trace buffers
133 * @num:       number of trace buffers to map
134 * @size:      size of each trace buffer
135 *
136 * Maps the Xen trace buffers them into process address space.
137 */
138struct t_buf *map_tbufs(unsigned long tbufs_mfn, unsigned int num,
139                        unsigned long size)
140{
141    int xc_handle;
142    struct t_buf *tbufs_mapped;
143
144    xc_handle = xc_interface_open();
145
146    if ( xc_handle < 0 ) 
147    {
148        exit(EXIT_FAILURE);
149    }
150
151    tbufs_mapped = xc_map_foreign_range(xc_handle, DOMID_XEN,
152                                        size * num, PROT_READ | PROT_WRITE,
153                                        tbufs_mfn);
154
155    xc_interface_close(xc_handle);
156
157    if ( tbufs_mapped == 0 ) 
158    {
159        PERROR("Failed to mmap trace buffers");
160        exit(EXIT_FAILURE);
161    }
162
163    return tbufs_mapped;
164}
165
166/**
167 * set_mask - set the cpu/event mask in HV
168 * @mask:           the new mask
169 * @type:           the new mask type,0-event mask, 1-cpu mask
170 *
171 */
172void set_mask(uint32_t mask, int type)
173{
174    int ret = 0;
175    int xc_handle = xc_interface_open(); /* for accessing control interface */
176
177    if (type == 1) {
178        ret = xc_tbuf_set_cpu_mask(xc_handle, mask);
179        fprintf(stderr, "change cpumask to 0x%x\n", mask);
180    } else if (type == 0) {
181        ret = xc_tbuf_set_evt_mask(xc_handle, mask);
182        fprintf(stderr, "change evtmask to 0x%x\n", mask);
183    }
184
185    xc_interface_close(xc_handle);
186
187    if ( ret != 0 )
188    {
189        PERROR("Failure to get trace buffer pointer from Xen and set the new mask");
190        exit(EXIT_FAILURE);
191    }
192}
193
194/**
195 * init_bufs_ptrs - initialises an array of pointers to the trace buffers
196 * @bufs_mapped:    the userspace address where the trace buffers are mapped
197 * @num:            number of trace buffers
198 * @size:           trace buffer size
199 *
200 * Initialises an array of pointers to individual trace buffers within the
201 * mapped region containing all trace buffers.
202 */
203struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
204                              unsigned long size)
205{
206    int i;
207    struct t_buf **user_ptrs;
208
209    user_ptrs = (struct t_buf **)calloc(num, sizeof(struct t_buf *));
210    if ( user_ptrs == NULL )
211    {
212        PERROR( "Failed to allocate memory for buffer pointers\n");
213        exit(EXIT_FAILURE);
214    }
215   
216    /* initialise pointers to the trace buffers - given the size of a trace
217     * buffer and the value of bufs_maped, we can easily calculate these */
218    for ( i = 0; i<num; i++ )
219        user_ptrs[i] = (struct t_buf *)((unsigned long)bufs_mapped + size * i);
220
221    return user_ptrs;
222}
223
224
225/**
226 * init_rec_ptrs - initialises data area pointers to locations in user space
227 * @tbufs_mfn:     base mfn of the trace buffer area
228 * @tbufs_mapped:  user virtual address of base of trace buffer area
229 * @meta:          array of user-space pointers to struct t_buf's of metadata
230 * @num:           number of trace buffers
231 *
232 * Initialises data area pointers to the locations that data areas have been
233 * mapped in user space.  Note that the trace buffer metadata contains machine
234 * pointers - the array returned allows more convenient access to them.
235 */
236struct t_rec **init_rec_ptrs(struct t_buf **meta, unsigned int num)
237{
238    int i;
239    struct t_rec **data;
240   
241    data = calloc(num, sizeof(struct t_rec *));
242    if ( data == NULL )
243    {
244        PERROR("Failed to allocate memory for data pointers\n");
245        exit(EXIT_FAILURE);
246    }
247
248    for ( i = 0; i < num; i++ )
249        data[i] = (struct t_rec *)(meta[i] + 1);
250
251    return data;
252}
253
254/**
255 * get_num_cpus - get the number of logical CPUs
256 */
257unsigned int get_num_cpus(void)
258{
259    xc_physinfo_t physinfo;
260    int xc_handle = xc_interface_open();
261    int ret;
262   
263    ret = xc_physinfo(xc_handle, &physinfo);
264   
265    if ( ret != 0 )
266    {
267        PERROR("Failure to get logical CPU count from Xen");
268        exit(EXIT_FAILURE);
269    }
270
271    xc_interface_close(xc_handle);
272
273    return (physinfo.threads_per_core *
274            physinfo.cores_per_socket *
275            physinfo.sockets_per_node *
276            physinfo.nr_nodes);
277}
278
279
280/**
281 * monitor_tbufs - monitor the contents of tbufs and output to a file
282 * @logfile:       the FILE * representing the file to log to
283 */
284int monitor_tbufs(FILE *logfile)
285{
286    int i;
287
288    void *tbufs_mapped;          /* pointer to where the tbufs are mapped    */
289    struct t_buf **meta;         /* pointers to the trace buffer metadata    */
290    struct t_rec **data;         /* pointers to the trace buffer data areas
291                                  * where they are mapped into user space.   */
292    unsigned long tbufs_mfn;     /* mfn of the tbufs                         */
293    unsigned int  num;           /* number of trace buffers / logical CPUS   */
294    unsigned long size;          /* size of a single trace buffer            */
295
296    int size_in_recs;
297
298    /* get number of logical CPUs (and therefore number of trace buffers) */
299    num = get_num_cpus();
300
301    /* setup access to trace buffers */
302    get_tbufs(&tbufs_mfn, &size);
303    tbufs_mapped = map_tbufs(tbufs_mfn, num, size);
304
305    size_in_recs = (size - sizeof(struct t_buf)) / sizeof(struct t_rec);
306
307    /* build arrays of convenience ptrs */
308    meta  = init_bufs_ptrs(tbufs_mapped, num, size);
309    data  = init_rec_ptrs(meta, num);
310
311    if(opts.discard) {
312        for ( i = 0; (i < num) ; i++ )
313        {
314            meta[i]->cons = meta[i]->prod;
315        }
316    }
317
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        }
331
332        nanosleep(&opts.poll_sleep, NULL);
333    }
334
335    /* cleanup */
336    free(meta);
337    free(data);
338    /* don't need to munmap - cleanup is automatic */
339    fclose(logfile);
340
341    return 0;
342}
343
344
345/******************************************************************************
346 * Various declarations / definitions GNU argp needs to do its work
347 *****************************************************************************/
348
349int parse_evtmask(char *arg, struct argp_state *state)
350{
351    settings_t *setup = (settings_t *)state->input;
352    char *inval;
353
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, "hvm") == 0){ 
362        setup->evt_mask |= TRC_HVM;
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    }
370
371    return 0;
372
373}
374
375/* command parser for GNU argp - see GNU docs for more info */
376error_t cmd_parser(int key, char *arg, struct argp_state *state)
377{
378    settings_t *setup = (settings_t *)state->input;
379
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;
390
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;
399
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;
408   
409    case 'e': /* set new event mask for filtering*/
410    {
411        parse_evtmask(arg, state);
412    }
413    break;
414   
415    case 'S': /* set tbuf size (given in pages) */
416    {
417        char *inval;
418        setup->tbuf_size = strtol(arg, &inval, 0);
419        if ( inval == arg )
420            argp_usage(state);
421    }
422    break;
423
424    case 'D': /* Discard traces currently in the buffer before beginning */
425    {
426        opts.discard=1;
427    }
428    break;
429
430    case ARGP_KEY_ARG:
431    {
432        if ( state->arg_num == 0 )
433            setup->outfile = arg;
434        else
435            argp_usage(state);
436    }
437    break;
438       
439    default:
440        return ARGP_ERR_UNKNOWN;
441    }
442
443    return 0;
444}
445
446#define xstr(x) str(x)
447#define str(x) #x
448
449const struct argp_option cmd_opts[] =
450{
451    { .name = "log-thresh", .key='t', .arg="l",
452      .doc =
453      "Set number, l, of new records required to trigger a write to output "
454      "(default " xstr(NEW_DATA_THRESH) ")." },
455
456    { .name = "poll-sleep", .key='s', .arg="p",
457      .doc = 
458      "Set sleep time, p, in milliseconds between polling the trace buffer "
459      "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
460
461    { .name = "cpu-mask", .key='c', .arg="c",
462      .doc = 
463      "Set cpu-mask." },
464
465    { .name = "evt-mask", .key='e', .arg="e",
466      .doc = 
467      "Set trace event mask.  This can accept a numerical (including hex) "
468      " argument or a symbolic name.  Symbolic names include: gen, sched, "
469      "dom0op, hvm, and all." },
470
471    { .name = "trace-buf-size", .key='S', .arg="N",
472      .doc =
473      "Set trace buffer size in pages (default " xstr(DEFAULT_TBUF_SIZE) "). "
474      "N.B. that the trace buffer cannot be resized.  If it has "
475      "already been set this boot cycle, this argument will be ignored." },
476
477    { .name = "discard-buffers", .key='D', .arg=NULL,
478      .flags=OPTION_ARG_OPTIONAL,
479      .doc = "Discard all records currently in the trace buffers before "
480      " beginning." },
481
482    {0}
483};
484
485const struct argp parser_def =
486{
487    .options = cmd_opts,
488    .parser = cmd_parser,
489    .args_doc = "[output file]",
490    .doc =
491    "Tool to capure Xen trace buffer data"
492    "\v"
493    "This tool is used to capture trace buffer data from Xen.  The data is "
494    "output in a binary format, in the following order:\n\n"
495    "  CPU(uint) TSC(uint64_t) EVENT(uint32_t) D1 D2 D3 D4 D5 "
496    "(all uint32_t)\n\n"
497    "The output should be parsed using the tool xentrace_format, which can "
498    "produce human-readable output in ASCII format."
499};
500
501
502const char *argp_program_version     = "xentrace v1.1";
503const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
504       
505   
506int main(int argc, char **argv)
507{
508    int outfd = 1, ret;
509    FILE *logfile;
510    struct sigaction act;
511
512    opts.outfile = 0;
513    opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
514    opts.new_data_thresh = NEW_DATA_THRESH;
515    opts.evt_mask = 0;
516    opts.cpu_mask = 0;
517
518    argp_parse(&parser_def, argc, argv, 0, 0, &opts);
519
520    if (opts.evt_mask != 0) { 
521        set_mask(opts.evt_mask, 0);
522    }
523
524    if (opts.cpu_mask != 0) {
525        set_mask(opts.cpu_mask, 1);
526    }
527
528    if ( opts.outfile )
529        outfd = open(opts.outfile, O_WRONLY | O_CREAT | O_LARGEFILE, 0644);
530
531    if(outfd < 0)
532    {
533        perror("Could not open output file");
534        exit(EXIT_FAILURE);
535    }       
536
537    if(isatty(outfd))
538    {
539        fprintf(stderr, "Cannot output to a TTY, specify a log file.\n");
540        exit(EXIT_FAILURE);
541    }
542
543    logfile = fdopen(outfd, "w");
544   
545    /* ensure that if we get a signal, we'll do cleanup, then exit */
546    act.sa_handler = close_handler;
547    act.sa_flags = 0;
548    sigemptyset(&act.sa_mask);
549    sigaction(SIGHUP,  &act, NULL);
550    sigaction(SIGTERM, &act, NULL);
551    sigaction(SIGINT,  &act, NULL);
552
553    ret = monitor_tbufs(logfile);
554
555    return ret;
556}
Note: See TracBrowser for help on using the repository browser.