perf tools: do not complain if root is owning perf.data
[safe/jmp/linux-2.6] / tools / perf / builtin-report.c
index 6321951..ea6328a 100644 (file)
 #include "util/values.h"
 
 #include "perf.h"
+#include "util/debug.h"
 #include "util/header.h"
 
 #include "util/parse-options.h"
 #include "util/parse-events.h"
 
-#define SHOW_KERNEL    1
-#define SHOW_USER      2
-#define SHOW_HV                4
+#include "util/thread.h"
 
 static char            const *input_name = "perf.data";
 
@@ -38,13 +37,10 @@ static char         *dso_list_str, *comm_list_str, *sym_list_str,
 static struct strlist  *dso_list, *comm_list, *sym_list;
 static char            *field_sep;
 
+static int             force;
 static int             input;
 static int             show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
 
-static int             dump_trace = 0;
-#define dprintf(x...)  do { if (dump_trace) printf(x); } while (0)
-#define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0)
-
 static int             full_paths;
 static int             show_nr_samples;
 
@@ -71,6 +67,11 @@ static char          __cwd[PATH_MAX];
 static char            *cwd = __cwd;
 static int             cwdlen;
 
+static struct rb_root  threads;
+static struct thread   *last_match;
+
+static struct perf_header *header;
+
 static
 struct callchain_param callchain_param = {
        .mode   = CHAIN_GRAPH_REL,
@@ -92,6 +93,7 @@ static int repsep_fprintf(FILE *fp, const char *fmt, ...)
                n = vasprintf(&bf, fmt, ap);
                if (n > 0) {
                        char *sep = bf;
+
                        while (1) {
                                sep = strchr(sep, *field_sep);
                                if (sep == NULL)
@@ -106,187 +108,10 @@ static int repsep_fprintf(FILE *fp, const char *fmt, ...)
        return n;
 }
 
-struct thread {
-       struct rb_node   rb_node;
-       struct list_head maps;
-       pid_t            pid;
-       char             *comm;
-};
-
-static struct thread *thread__new(pid_t pid)
-{
-       struct thread *self = malloc(sizeof(*self));
-
-       if (self != NULL) {
-               self->pid = pid;
-               self->comm = malloc(32);
-               if (self->comm)
-                       snprintf(self->comm, 32, ":%d", self->pid);
-               INIT_LIST_HEAD(&self->maps);
-       }
-
-       return self;
-}
-
 static unsigned int dsos__col_width,
                    comms__col_width,
                    threads__col_width;
 
-static int thread__set_comm(struct thread *self, const char *comm)
-{
-       if (self->comm)
-               free(self->comm);
-       self->comm = strdup(comm);
-       if (!self->comm)
-               return -ENOMEM;
-
-       if (!col_width_list_str && !field_sep &&
-           (!comm_list || strlist__has_entry(comm_list, comm))) {
-               unsigned int slen = strlen(comm);
-               if (slen > comms__col_width) {
-                       comms__col_width = slen;
-                       threads__col_width = slen + 6;
-               }
-       }
-
-       return 0;
-}
-
-static size_t thread__fprintf(struct thread *self, FILE *fp)
-{
-       struct map *pos;
-       size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
-
-       list_for_each_entry(pos, &self->maps, node)
-               ret += map__fprintf(pos, fp);
-
-       return ret;
-}
-
-
-static struct rb_root threads;
-static struct thread *last_match;
-
-static struct thread *threads__findnew(pid_t pid)
-{
-       struct rb_node **p = &threads.rb_node;
-       struct rb_node *parent = NULL;
-       struct thread *th;
-
-       /*
-        * Font-end cache - PID lookups come in blocks,
-        * so most of the time we dont have to look up
-        * the full rbtree:
-        */
-       if (last_match && last_match->pid == pid)
-               return last_match;
-
-       while (*p != NULL) {
-               parent = *p;
-               th = rb_entry(parent, struct thread, rb_node);
-
-               if (th->pid == pid) {
-                       last_match = th;
-                       return th;
-               }
-
-               if (pid < th->pid)
-                       p = &(*p)->rb_left;
-               else
-                       p = &(*p)->rb_right;
-       }
-
-       th = thread__new(pid);
-       if (th != NULL) {
-               rb_link_node(&th->rb_node, parent, p);
-               rb_insert_color(&th->rb_node, &threads);
-               last_match = th;
-       }
-
-       return th;
-}
-
-static void thread__insert_map(struct thread *self, struct map *map)
-{
-       struct map *pos, *tmp;
-
-       list_for_each_entry_safe(pos, tmp, &self->maps, node) {
-               if (map__overlap(pos, map)) {
-                       if (verbose >= 2) {
-                               printf("overlapping maps:\n");
-                               map__fprintf(map, stdout);
-                               map__fprintf(pos, stdout);
-                       }
-
-                       if (map->start <= pos->start && map->end > pos->start)
-                               pos->start = map->end;
-
-                       if (map->end >= pos->end && map->start < pos->end)
-                               pos->end = map->start;
-
-                       if (verbose >= 2) {
-                               printf("after collision:\n");
-                               map__fprintf(pos, stdout);
-                       }
-
-                       if (pos->start >= pos->end) {
-                               list_del_init(&pos->node);
-                               free(pos);
-                       }
-               }
-       }
-
-       list_add_tail(&map->node, &self->maps);
-}
-
-static int thread__fork(struct thread *self, struct thread *parent)
-{
-       struct map *map;
-
-       if (self->comm)
-               free(self->comm);
-       self->comm = strdup(parent->comm);
-       if (!self->comm)
-               return -ENOMEM;
-
-       list_for_each_entry(map, &parent->maps, node) {
-               struct map *new = map__clone(map);
-               if (!new)
-                       return -ENOMEM;
-               thread__insert_map(self, new);
-       }
-
-       return 0;
-}
-
-static struct map *thread__find_map(struct thread *self, u64 ip)
-{
-       struct map *pos;
-
-       if (self == NULL)
-               return NULL;
-
-       list_for_each_entry(pos, &self->maps, node)
-               if (ip >= pos->start && ip <= pos->end)
-                       return pos;
-
-       return NULL;
-}
-
-static size_t threads__fprintf(FILE *fp)
-{
-       size_t ret = 0;
-       struct rb_node *nd;
-
-       for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
-               struct thread *pos = rb_entry(nd, struct thread, rb_node);
-
-               ret += thread__fprintf(pos, fp);
-       }
-
-       return ret;
-}
-
 /*
  * histogram, sorted on item, collects counts
  */
@@ -316,7 +141,7 @@ struct hist_entry {
 struct sort_entry {
        struct list_head list;
 
-       char *header;
+       const char *header;
 
        int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
        int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
@@ -500,7 +325,7 @@ static int sort__need_collapse = 0;
 static int sort__has_parent = 0;
 
 struct sort_dimension {
-       char                    *name;
+       const char              *name;
        struct sort_entry       *entry;
        int                     taken;
 };
@@ -515,7 +340,7 @@ static struct sort_dimension sort_dimensions[] = {
 
 static LIST_HEAD(hist_entry__sort_list);
 
-static int sort_dimension__add(char *tok)
+static int sort_dimension__add(const char *tok)
 {
        unsigned int i;
 
@@ -774,6 +599,7 @@ hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
                case CHAIN_GRAPH_REL:
                        ret += callchain__fprintf_graph(fp, chain,
                                                        total_samples, 1, 1);
+               case CHAIN_NONE:
                default:
                        break;
                }
@@ -840,6 +666,27 @@ static void dso__calc_col_width(struct dso *self)
        self->slen_calculated = 1;
 }
 
+static int thread__set_comm_adjust(struct thread *self, const char *comm)
+{
+       int ret = thread__set_comm(self, comm);
+
+       if (ret)
+               return ret;
+
+       if (!col_width_list_str && !field_sep &&
+           (!comm_list || strlist__has_entry(comm_list, comm))) {
+               unsigned int slen = strlen(comm);
+
+               if (slen > comms__col_width) {
+                       comms__col_width = slen;
+                       threads__col_width = slen + 6;
+               }
+       }
+
+       return 0;
+}
+
+
 static struct symbol *
 resolve_symbol(struct thread *thread, struct map **mapp,
               struct dso **dsop, u64 *ipp)
@@ -883,8 +730,8 @@ got_map:
                if ((long long)ip < 0)
                dso = kernel_dso;
        }
-       dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
-       dprintf(" ...... map: %Lx -> %Lx\n", *ipp, ip);
+       dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
+       dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip);
        *ipp  = ip;
 
        if (dsop)
@@ -1228,10 +1075,10 @@ print_entries:
 
 static void register_idle_thread(void)
 {
-       struct thread *thread = threads__findnew(0);
+       struct thread *thread = threads__findnew(0, &threads, &last_match);
 
        if (thread == NULL ||
-                       thread__set_comm(thread, "[idle]")) {
+                       thread__set_comm_adjust(thread, "[idle]")) {
                fprintf(stderr, "problem inserting idle task.\n");
                exit(-1);
        }
@@ -1263,7 +1110,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
        char level;
        int show = 0;
        struct dso *dso = NULL;
-       struct thread *thread = threads__findnew(event->ip.pid);
+       struct thread *thread;
        u64 ip = event->ip.ip;
        u64 period = 1;
        struct map *map = NULL;
@@ -1271,12 +1118,14 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
        struct ip_callchain *chain = NULL;
        int cpumode;
 
+       thread = threads__findnew(event->ip.pid, &threads, &last_match);
+
        if (sample_type & PERF_SAMPLE_PERIOD) {
                period = *(u64 *)more_data;
                more_data += sizeof(u64);
        }
 
-       dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
+       dump_printf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
                (void *)(offset + head),
                (void *)(long)(event->header.size),
                event->header.misc,
@@ -1289,7 +1138,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
 
                chain = (void *)more_data;
 
-               dprintf("... chain: nr:%Lu\n", chain->nr);
+               dump_printf("... chain: nr:%Lu\n", chain->nr);
 
                if (validate_chain(chain, event) < 0) {
                        eprintf("call-chain problem with event, skipping it.\n");
@@ -1298,11 +1147,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
 
                if (dump_trace) {
                        for (i = 0; i < chain->nr; i++)
-                               dprintf("..... %2d: %016Lx\n", i, chain->ips[i]);
+                               dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]);
                }
        }
 
-       dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
+       dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
 
        if (thread == NULL) {
                eprintf("problem processing %d event, skipping it.\n",
@@ -1321,7 +1170,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
 
                dso = kernel_dso;
 
-               dprintf(" ...... dso: %s\n", dso->name);
+               dump_printf(" ...... dso: %s\n", dso->name);
 
        } else if (cpumode == PERF_EVENT_MISC_USER) {
 
@@ -1334,7 +1183,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
 
                dso = hypervisor_dso;
 
-               dprintf(" ...... dso: [hypervisor]\n");
+               dump_printf(" ...... dso: [hypervisor]\n");
        }
 
        if (show & show_mask) {
@@ -1360,10 +1209,12 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
 static int
 process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
 {
-       struct thread *thread = threads__findnew(event->mmap.pid);
+       struct thread *thread;
        struct map *map = map__new(&event->mmap, cwd, cwdlen);
 
-       dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
+       thread = threads__findnew(event->mmap.pid, &threads, &last_match);
+
+       dump_printf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
                (void *)(offset + head),
                (void *)(long)(event->header.size),
                event->mmap.pid,
@@ -1374,7 +1225,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
                event->mmap.filename);
 
        if (thread == NULL || map == NULL) {
-               dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n");
+               dump_printf("problem processing PERF_EVENT_MMAP, skipping event.\n");
                return 0;
        }
 
@@ -1387,16 +1238,18 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
 static int
 process_comm_event(event_t *event, unsigned long offset, unsigned long head)
 {
-       struct thread *thread = threads__findnew(event->comm.pid);
+       struct thread *thread;
+
+       thread = threads__findnew(event->comm.pid, &threads, &last_match);
 
-       dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
+       dump_printf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
                (void *)(offset + head),
                (void *)(long)(event->header.size),
                event->comm.comm, event->comm.pid);
 
        if (thread == NULL ||
-           thread__set_comm(thread, event->comm.comm)) {
-               dprintf("problem processing PERF_EVENT_COMM, skipping event.\n");
+           thread__set_comm_adjust(thread, event->comm.comm)) {
+               dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n");
                return -1;
        }
        total_comm++;
@@ -1407,10 +1260,13 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
 static int
 process_task_event(event_t *event, unsigned long offset, unsigned long head)
 {
-       struct thread *thread = threads__findnew(event->fork.pid);
-       struct thread *parent = threads__findnew(event->fork.ppid);
+       struct thread *thread;
+       struct thread *parent;
+
+       thread = threads__findnew(event->fork.pid, &threads, &last_match);
+       parent = threads__findnew(event->fork.ppid, &threads, &last_match);
 
-       dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n",
+       dump_printf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n",
                (void *)(offset + head),
                (void *)(long)(event->header.size),
                event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT",
@@ -1428,7 +1284,7 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head)
                return 0;
 
        if (!thread || !parent || thread__fork(thread, parent)) {
-               dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
+               dump_printf("problem processing PERF_EVENT_FORK, skipping event.\n");
                return -1;
        }
        total_fork++;
@@ -1439,7 +1295,7 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head)
 static int
 process_lost_event(event_t *event, unsigned long offset, unsigned long head)
 {
-       dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n",
+       dump_printf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n",
                (void *)(offset + head),
                (void *)(long)(event->header.size),
                event->lost.id,
@@ -1450,68 +1306,15 @@ process_lost_event(event_t *event, unsigned long offset, unsigned long head)
        return 0;
 }
 
-static void trace_event(event_t *event)
-{
-       unsigned char *raw_event = (void *)event;
-       char *color = PERF_COLOR_BLUE;
-       int i, j;
-
-       if (!dump_trace)
-               return;
-
-       dprintf(".");
-       cdprintf("\n. ... raw event: size %d bytes\n", event->header.size);
-
-       for (i = 0; i < event->header.size; i++) {
-               if ((i & 15) == 0) {
-                       dprintf(".");
-                       cdprintf("  %04x: ", i);
-               }
-
-               cdprintf(" %02x", raw_event[i]);
-
-               if (((i & 15) == 15) || i == event->header.size-1) {
-                       cdprintf("  ");
-                       for (j = 0; j < 15-(i & 15); j++)
-                               cdprintf("   ");
-                       for (j = 0; j < (i & 15); j++) {
-                               if (isprint(raw_event[i-15+j]))
-                                       cdprintf("%c", raw_event[i-15+j]);
-                               else
-                                       cdprintf(".");
-                       }
-                       cdprintf("\n");
-               }
-       }
-       dprintf(".\n");
-}
-
-static struct perf_header      *header;
-
-static struct perf_counter_attr *perf_header__find_attr(u64 id)
-{
-       int i;
-
-       for (i = 0; i < header->attrs; i++) {
-               struct perf_header_attr *attr = header->attr[i];
-               int j;
-
-               for (j = 0; j < attr->ids; j++) {
-                       if (attr->id[j] == id)
-                               return &attr->attr;
-               }
-       }
-
-       return NULL;
-}
-
 static int
 process_read_event(event_t *event, unsigned long offset, unsigned long head)
 {
-       struct perf_counter_attr *attr = perf_header__find_attr(event->read.id);
+       struct perf_counter_attr *attr;
+
+       attr = perf_header__find_attr(event->read.id, header);
 
        if (show_threads) {
-               char *name = attr ? __event_name(attr->type, attr->config)
+               const char *name = attr ? __event_name(attr->type, attr->config)
                                   : "unknown";
                perf_read_values_add_value(&show_threads_values,
                                           event->read.pid, event->read.tid,
@@ -1520,7 +1323,7 @@ process_read_event(event_t *event, unsigned long offset, unsigned long head)
                                           event->read.value);
        }
 
-       dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n",
+       dump_printf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n",
                        (void *)(offset + head),
                        (void *)(long)(event->header.size),
                        event->read.pid,
@@ -1572,29 +1375,12 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
        return 0;
 }
 
-static u64 perf_header__sample_type(void)
-{
-       u64 sample_type = 0;
-       int i;
-
-       for (i = 0; i < header->attrs; i++) {
-               struct perf_header_attr *attr = header->attr[i];
-
-               if (!sample_type)
-                       sample_type = attr->attr.sample_type;
-               else if (sample_type != attr->attr.sample_type)
-                       die("non matching sample_type");
-       }
-
-       return sample_type;
-}
-
 static int __cmd_report(void)
 {
        int ret, rc = EXIT_FAILURE;
        unsigned long offset = 0;
        unsigned long head, shift;
-       struct stat stat;
+       struct stat input_stat;
        event_t *event;
        uint32_t size;
        char *buf;
@@ -1613,13 +1399,18 @@ static int __cmd_report(void)
                exit(-1);
        }
 
-       ret = fstat(input, &stat);
+       ret = fstat(input, &input_stat);
        if (ret < 0) {
                perror("failed to stat file");
                exit(-1);
        }
 
-       if (!stat.st_size) {
+       if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
+               fprintf(stderr, "file: %s not owned by current user or root\n", input_name);
+               exit(-1);
+       }
+
+       if (!input_stat.st_size) {
                fprintf(stderr, "zero-sized file, nothing to do!\n");
                exit(0);
        }
@@ -1627,7 +1418,7 @@ static int __cmd_report(void)
        header = perf_header__read(input);
        head = header->data_offset;
 
-       sample_type = perf_header__sample_type();
+       sample_type = perf_header__sample_type(header);
 
        if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
                if (sort__has_parent) {
@@ -1687,12 +1478,12 @@ more:
                size = 8;
 
        if (head + event->header.size >= page_size * mmap_window) {
-               int ret;
+               int munmap_ret;
 
                shift = page_size * (head / page_size);
 
-               ret = munmap(buf, page_size * mmap_window);
-               assert(ret == 0);
+               munmap_ret = munmap(buf, page_size * mmap_window);
+               assert(munmap_ret == 0);
 
                offset += shift;
                head -= shift;
@@ -1701,14 +1492,14 @@ more:
 
        size = event->header.size;
 
-       dprintf("\n%p [%p]: event: %d\n",
+       dump_printf("\n%p [%p]: event: %d\n",
                        (void *)(offset + head),
                        (void *)(long)event->header.size,
                        event->header.type);
 
        if (!size || process_event(event, offset, head) < 0) {
 
-               dprintf("%p [%p]: skipping unknown header type: %d\n",
+               dump_printf("%p [%p]: skipping unknown header type: %d\n",
                        (void *)(offset + head),
                        (void *)(long)(event->header.size),
                        event->header.type);
@@ -1731,25 +1522,25 @@ more:
        if (offset + head >= header->data_offset + header->data_size)
                goto done;
 
-       if (offset + head < (unsigned long)stat.st_size)
+       if (offset + head < (unsigned long)input_stat.st_size)
                goto more;
 
 done:
        rc = EXIT_SUCCESS;
        close(input);
 
-       dprintf("      IP events: %10ld\n", total);
-       dprintf("    mmap events: %10ld\n", total_mmap);
-       dprintf("    comm events: %10ld\n", total_comm);
-       dprintf("    fork events: %10ld\n", total_fork);
-       dprintf("    lost events: %10ld\n", total_lost);
-       dprintf(" unknown events: %10ld\n", total_unknown);
+       dump_printf("      IP events: %10ld\n", total);
+       dump_printf("    mmap events: %10ld\n", total_mmap);
+       dump_printf("    comm events: %10ld\n", total_comm);
+       dump_printf("    fork events: %10ld\n", total_fork);
+       dump_printf("    lost events: %10ld\n", total_lost);
+       dump_printf(" unknown events: %10ld\n", total_unknown);
 
        if (dump_trace)
                return 0;
 
        if (verbose >= 3)
-               threads__fprintf(stdout);
+               threads__fprintf(stdout, &threads);
 
        if (verbose >= 2)
                dsos__fprintf(stdout);
@@ -1829,7 +1620,8 @@ static const struct option options[] = {
                    "be more verbose (show symbol address, etc)"),
        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
                    "dump raw trace in ASCII"),
-       OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
+       OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
+       OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
        OPT_BOOLEAN('m', "modules", &modules,
                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
        OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples,