linux/tools/perf/builtin-trace.c
<<
>>
Prefs
   1#include "builtin.h"
   2
   3#include "util/util.h"
   4#include "util/cache.h"
   5#include "util/symbol.h"
   6#include "util/thread.h"
   7#include "util/header.h"
   8
   9#include "util/parse-options.h"
  10
  11#include "perf.h"
  12#include "util/debug.h"
  13
  14#include "util/trace-event.h"
  15
  16static char             const *input_name = "perf.data";
  17static int              input;
  18static unsigned long    page_size;
  19static unsigned long    mmap_window = 32;
  20
  21static unsigned long    total = 0;
  22static unsigned long    total_comm = 0;
  23
  24static struct rb_root   threads;
  25static struct thread    *last_match;
  26
  27static struct perf_header *header;
  28static u64              sample_type;
  29
  30
  31static int
  32process_comm_event(event_t *event, unsigned long offset, unsigned long head)
  33{
  34        struct thread *thread;
  35
  36        thread = threads__findnew(event->comm.pid, &threads, &last_match);
  37
  38        dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
  39                (void *)(offset + head),
  40                (void *)(long)(event->header.size),
  41                event->comm.comm, event->comm.pid);
  42
  43        if (thread == NULL ||
  44            thread__set_comm(thread, event->comm.comm)) {
  45                dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
  46                return -1;
  47        }
  48        total_comm++;
  49
  50        return 0;
  51}
  52
  53static int
  54process_sample_event(event_t *event, unsigned long offset, unsigned long head)
  55{
  56        char level;
  57        int show = 0;
  58        struct dso *dso = NULL;
  59        struct thread *thread;
  60        u64 ip = event->ip.ip;
  61        u64 timestamp = -1;
  62        u32 cpu = -1;
  63        u64 period = 1;
  64        void *more_data = event->ip.__more_data;
  65        int cpumode;
  66
  67        thread = threads__findnew(event->ip.pid, &threads, &last_match);
  68
  69        if (sample_type & PERF_SAMPLE_TIME) {
  70                timestamp = *(u64 *)more_data;
  71                more_data += sizeof(u64);
  72        }
  73
  74        if (sample_type & PERF_SAMPLE_CPU) {
  75                cpu = *(u32 *)more_data;
  76                more_data += sizeof(u32);
  77                more_data += sizeof(u32); /* reserved */
  78        }
  79
  80        if (sample_type & PERF_SAMPLE_PERIOD) {
  81                period = *(u64 *)more_data;
  82                more_data += sizeof(u64);
  83        }
  84
  85        dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
  86                (void *)(offset + head),
  87                (void *)(long)(event->header.size),
  88                event->header.misc,
  89                event->ip.pid, event->ip.tid,
  90                (void *)(long)ip,
  91                (long long)period);
  92
  93        dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
  94
  95        if (thread == NULL) {
  96                eprintf("problem processing %d event, skipping it.\n",
  97                        event->header.type);
  98                return -1;
  99        }
 100
 101        cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 102
 103        if (cpumode == PERF_RECORD_MISC_KERNEL) {
 104                show = SHOW_KERNEL;
 105                level = 'k';
 106
 107                dso = kernel_dso;
 108
 109                dump_printf(" ...... dso: %s\n", dso->name);
 110
 111        } else if (cpumode == PERF_RECORD_MISC_USER) {
 112
 113                show = SHOW_USER;
 114                level = '.';
 115
 116        } else {
 117                show = SHOW_HV;
 118                level = 'H';
 119
 120                dso = hypervisor_dso;
 121
 122                dump_printf(" ...... dso: [hypervisor]\n");
 123        }
 124
 125        if (sample_type & PERF_SAMPLE_RAW) {
 126                struct {
 127                        u32 size;
 128                        char data[0];
 129                } *raw = more_data;
 130
 131                /*
 132                 * FIXME: better resolve from pid from the struct trace_entry
 133                 * field, although it should be the same than this perf
 134                 * event pid
 135                 */
 136                print_event(cpu, raw->data, raw->size, timestamp, thread->comm);
 137        }
 138        total += period;
 139
 140        return 0;
 141}
 142
 143static int
 144process_event(event_t *event, unsigned long offset, unsigned long head)
 145{
 146        trace_event(event);
 147
 148        switch (event->header.type) {
 149        case PERF_RECORD_MMAP ... PERF_RECORD_LOST:
 150                return 0;
 151
 152        case PERF_RECORD_COMM:
 153                return process_comm_event(event, offset, head);
 154
 155        case PERF_RECORD_EXIT ... PERF_RECORD_READ:
 156                return 0;
 157
 158        case PERF_RECORD_SAMPLE:
 159                return process_sample_event(event, offset, head);
 160
 161        case PERF_RECORD_MAX:
 162        default:
 163                return -1;
 164        }
 165
 166        return 0;
 167}
 168
 169static int __cmd_trace(void)
 170{
 171        int ret, rc = EXIT_FAILURE;
 172        unsigned long offset = 0;
 173        unsigned long head = 0;
 174        struct stat perf_stat;
 175        event_t *event;
 176        uint32_t size;
 177        char *buf;
 178
 179        trace_report();
 180        register_idle_thread(&threads, &last_match);
 181
 182        input = open(input_name, O_RDONLY);
 183        if (input < 0) {
 184                perror("failed to open file");
 185                exit(-1);
 186        }
 187
 188        ret = fstat(input, &perf_stat);
 189        if (ret < 0) {
 190                perror("failed to stat file");
 191                exit(-1);
 192        }
 193
 194        if (!perf_stat.st_size) {
 195                fprintf(stderr, "zero-sized file, nothing to do!\n");
 196                exit(0);
 197        }
 198        header = perf_header__read(input);
 199        head = header->data_offset;
 200        sample_type = perf_header__sample_type(header);
 201
 202        if (!(sample_type & PERF_SAMPLE_RAW))
 203                die("No trace sample to read. Did you call perf record "
 204                    "without -R?");
 205
 206        if (load_kernel() < 0) {
 207                perror("failed to load kernel symbols");
 208                return EXIT_FAILURE;
 209        }
 210
 211remap:
 212        buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
 213                           MAP_SHARED, input, offset);
 214        if (buf == MAP_FAILED) {
 215                perror("failed to mmap file");
 216                exit(-1);
 217        }
 218
 219more:
 220        event = (event_t *)(buf + head);
 221
 222        if (head + event->header.size >= page_size * mmap_window) {
 223                unsigned long shift = page_size * (head / page_size);
 224                int res;
 225
 226                res = munmap(buf, page_size * mmap_window);
 227                assert(res == 0);
 228
 229                offset += shift;
 230                head -= shift;
 231                goto remap;
 232        }
 233
 234        size = event->header.size;
 235
 236        if (!size || process_event(event, offset, head) < 0) {
 237
 238                /*
 239                 * assume we lost track of the stream, check alignment, and
 240                 * increment a single u64 in the hope to catch on again 'soon'.
 241                 */
 242
 243                if (unlikely(head & 7))
 244                        head &= ~7ULL;
 245
 246                size = 8;
 247        }
 248
 249        head += size;
 250
 251        if (offset + head < (unsigned long)perf_stat.st_size)
 252                goto more;
 253
 254        rc = EXIT_SUCCESS;
 255        close(input);
 256
 257        return rc;
 258}
 259
 260static const char * const annotate_usage[] = {
 261        "perf trace [<options>] <command>",
 262        NULL
 263};
 264
 265static const struct option options[] = {
 266        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
 267                    "dump raw trace in ASCII"),
 268        OPT_BOOLEAN('v', "verbose", &verbose,
 269                    "be more verbose (show symbol address, etc)"),
 270        OPT_END()
 271};
 272
 273int cmd_trace(int argc, const char **argv, const char *prefix __used)
 274{
 275        symbol__init();
 276        page_size = getpagesize();
 277
 278        argc = parse_options(argc, argv, options, annotate_usage, 0);
 279        if (argc) {
 280                /*
 281                 * Special case: if there's an argument left then assume tha
 282                 * it's a symbol filter:
 283                 */
 284                if (argc > 1)
 285                        usage_with_options(annotate_usage, options);
 286        }
 287
 288        setup_pager();
 289
 290        return __cmd_trace();
 291}
 292