X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=tools%2Fperf%2Fbuiltin-report.c;h=88e88c510ae586070c48e6c85b7f819c556d958d;hb=d8db1b57d31a6b30ea2f0df318eab50fc92b38d6;hp=fe66895111b1450c725d9789e6064836f59cf024;hpb=9d91a6f7a489eb914c16b82d927f9d81d629c259;p=firefly-linux-kernel-4.4.55.git diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index fe66895111b1..88e88c510ae5 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -15,8 +15,11 @@ #include "util/rbtree.h" #include "util/symbol.h" #include "util/string.h" +#include "util/callchain.h" +#include "util/strlist.h" #include "perf.h" +#include "util/header.h" #include "util/parse-options.h" #include "util/parse-events.h" @@ -30,6 +33,8 @@ static char *vmlinux = NULL; static char default_sort_order[] = "comm,dso"; static char *sort_order = default_sort_order; +static char *dso_list_str, *comm_list_str, *sym_list_str; +static struct strlist *dso_list, *comm_list, *sym_list; static int input; static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; @@ -46,47 +51,60 @@ static int full_paths; static unsigned long page_size; static unsigned long mmap_window = 32; -static char *parent_pattern = "^sys_|^do_page_fault"; +static char default_parent_pattern[] = "^sys_|^do_page_fault"; +static char *parent_pattern = default_parent_pattern; static regex_t parent_regex; +static int exclude_other = 1; +static int callchain; + +static u64 sample_type; + struct ip_event { struct perf_event_header header; - __u64 ip; - __u32 pid, tid; + u64 ip; + u32 pid, tid; unsigned char __more_data[]; }; struct mmap_event { struct perf_event_header header; - __u32 pid, tid; - __u64 start; - __u64 len; - __u64 pgoff; + u32 pid, tid; + u64 start; + u64 len; + u64 pgoff; char filename[PATH_MAX]; }; struct comm_event { struct perf_event_header header; - __u32 pid, tid; + u32 pid, tid; char comm[16]; }; struct fork_event { struct perf_event_header header; - __u32 pid, ppid; + u32 pid, ppid; }; struct period_event { struct perf_event_header header; - __u64 time; - __u64 id; - __u64 sample_period; + u64 time; + u64 id; + u64 sample_period; }; struct lost_event { struct perf_event_header header; - __u64 id; - __u64 lost; + u64 id; + u64 lost; +}; + +struct read_event { + struct perf_event_header header; + u32 pid,tid; + u64 value; + u64 format[3]; }; typedef union event_union { @@ -97,6 +115,7 @@ typedef union event_union { struct fork_event fork; struct period_event period; struct lost_event lost; + struct read_event read; } event_t; static LIST_HEAD(dsos); @@ -155,7 +174,7 @@ static void dsos__fprintf(FILE *fp) dso__fprintf(pos, fp); } -static struct symbol *vdso__find_symbol(struct dso *dso, __u64 ip) +static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) { return dso__find_symbol(kernel_dso, ip); } @@ -202,26 +221,26 @@ static int strcommon(const char *pathname) struct map { struct list_head node; - __u64 start; - __u64 end; - __u64 pgoff; - __u64 (*map_ip)(struct map *, __u64); + u64 start; + u64 end; + u64 pgoff; + u64 (*map_ip)(struct map *, u64); struct dso *dso; }; -static __u64 map__map_ip(struct map *map, __u64 ip) +static u64 map__map_ip(struct map *map, u64 ip) { return ip - map->start + map->pgoff; } -static __u64 vdso__map_ip(struct map *map, __u64 ip) +static u64 vdso__map_ip(struct map *map, u64 ip) { return ip; } static inline int is_anon_memory(const char *filename) { - return strcmp(filename, "//anon") == 0; + return strcmp(filename, "//anon") == 0; } static struct map *map__new(struct mmap_event *event) @@ -392,9 +411,27 @@ static void thread__insert_map(struct thread *self, struct map *map) list_for_each_entry_safe(pos, tmp, &self->maps, node) { if (map__overlap(pos, map)) { - list_del_init(&pos->node); - /* XXX leaks dsos */ - free(pos); + 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); + } } } @@ -421,7 +458,7 @@ static int thread__fork(struct thread *self, struct thread *parent) return 0; } -static struct map *thread__find_map(struct thread *self, __u64 ip) +static struct map *thread__find_map(struct thread *self, u64 ip) { struct map *pos; @@ -456,17 +493,19 @@ static size_t threads__fprintf(FILE *fp) static struct rb_root hist; struct hist_entry { - struct rb_node rb_node; - - struct thread *thread; - struct map *map; - struct dso *dso; - struct symbol *sym; - struct symbol *parent; - __u64 ip; - char level; - - __u64 count; + struct rb_node rb_node; + + struct thread *thread; + struct map *map; + struct dso *dso; + struct symbol *sym; + struct symbol *parent; + u64 ip; + char level; + struct callchain_node callchain; + struct rb_root sorted_chain; + + u64 count; }; /* @@ -566,7 +605,7 @@ sort__dso_print(FILE *fp, struct hist_entry *self) if (self->dso) return fprintf(fp, "%-25s", self->dso->name); - return fprintf(fp, "%016llx ", (__u64)self->ip); + return fprintf(fp, "%016llx ", (u64)self->ip); } static struct sort_entry sort_dso = { @@ -580,7 +619,7 @@ static struct sort_entry sort_dso = { static int64_t sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) { - __u64 ip_l, ip_r; + u64 ip_l, ip_r; if (left->sym == right->sym) return 0; @@ -597,13 +636,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self) size_t ret = 0; if (verbose) - ret += fprintf(fp, "%#018llx ", (__u64)self->ip); + ret += fprintf(fp, "%#018llx ", (u64)self->ip); if (self->sym) { ret += fprintf(fp, "[%c] %s", self->dso == kernel_dso ? 'k' : '.', self->sym->name); } else { - ret += fprintf(fp, "%#016llx", (__u64)self->ip); + ret += fprintf(fp, "%#016llx", (u64)self->ip); } return ret; @@ -737,11 +776,56 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) } static size_t -hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples) +callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples) +{ + struct callchain_list *chain; + size_t ret = 0; + + if (!self) + return 0; + + ret += callchain__fprintf(fp, self->parent, total_samples); + + + list_for_each_entry(chain, &self->val, list) + ret += fprintf(fp, " %p\n", (void *)chain->ip); + + return ret; +} + +static size_t +hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, + u64 total_samples) +{ + struct rb_node *rb_node; + struct callchain_node *chain; + size_t ret = 0; + + rb_node = rb_first(&self->sorted_chain); + while (rb_node) { + double percent; + + chain = rb_entry(rb_node, struct callchain_node, rb_node); + percent = chain->hit * 100.0 / total_samples; + ret += fprintf(fp, " %6.2f%%\n", percent); + ret += callchain__fprintf(fp, chain, total_samples); + ret += fprintf(fp, "\n"); + rb_node = rb_next(rb_node); + } + + return ret; +} + + +static size_t +hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) { struct sort_entry *se; size_t ret; + if (exclude_other && !self->parent) + return 0; + if (total_samples) { double percent = self->count * 100.0 / total_samples; char *color = PERF_COLOR_NORMAL; @@ -764,12 +848,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples) ret = fprintf(fp, "%12Ld ", self->count); list_for_each_entry(se, &hist_entry__sort_list, list) { + if (exclude_other && (se == &sort_parent)) + continue; + fprintf(fp, " "); ret += se->print(fp, self); } ret += fprintf(fp, "\n"); + if (callchain) + hist_entry_callchain__fprintf(fp, self, total_samples); + return ret; } @@ -779,11 +869,11 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples) static struct symbol * resolve_symbol(struct thread *thread, struct map **mapp, - struct dso **dsop, __u64 *ipp) + struct dso **dsop, u64 *ipp) { struct dso *dso = dsop ? *dsop : NULL; struct map *map = mapp ? *mapp : NULL; - uint64_t ip = *ipp; + u64 ip = *ipp; if (!thread) return NULL; @@ -800,7 +890,6 @@ resolve_symbol(struct thread *thread, struct map **mapp, *mapp = map; got_map: ip = map->map_ip(map, ip); - *ipp = ip; dso = map->dso; } else { @@ -814,6 +903,8 @@ got_map: dso = kernel_dso; } dprintf(" ...... dso: %s\n", dso ? dso->name : ""); + dprintf(" ...... map: %Lx -> %Lx\n", *ipp, ip); + *ipp = ip; if (dsop) *dsop = dso; @@ -824,15 +915,12 @@ got_dso: return dso->find_symbol(dso, ip); } -static struct symbol *call__match(struct symbol *sym) +static int call__match(struct symbol *sym) { - if (!sym) - return NULL; - if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) - return sym; + return 1; - return NULL; + return 0; } /* @@ -841,8 +929,8 @@ static struct symbol *call__match(struct symbol *sym) static int hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, - struct symbol *sym, __u64 ip, struct perf_callchain_entry *chain, - char level, __u64 count) + struct symbol *sym, u64 ip, struct ip_callchain *chain, + char level, u64 count) { struct rb_node **p = &hist.rb_node; struct rb_node *parent = NULL; @@ -855,35 +943,41 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, .ip = ip, .level = level, .count = count, + .parent = NULL, + .sorted_chain = RB_ROOT }; int cmp; if (sort__has_parent && chain) { - int i, nr = chain->hv; - struct symbol *sym; - struct dso *dso; - __u64 ip; - - for (i = 0; i < chain->kernel; i++) { - ip = chain->ip[nr + i]; - dso = kernel_dso; + u64 context = PERF_CONTEXT_MAX; + int i; + + for (i = 0; i < chain->nr; i++) { + u64 ip = chain->ips[i]; + struct dso *dso = NULL; + struct symbol *sym; + + if (ip >= PERF_CONTEXT_MAX) { + context = ip; + continue; + } + + switch (context) { + case PERF_CONTEXT_KERNEL: + dso = kernel_dso; + break; + default: + break; + } + sym = resolve_symbol(thread, NULL, &dso, &ip); - entry.parent = call__match(sym); - if (entry.parent) - goto got_parent; - } - nr += i; - - for (i = 0; i < chain->user; i++) { - ip = chain->ip[nr + i]; - sym = resolve_symbol(thread, NULL, NULL, &ip); - entry.parent = call__match(sym); - if (entry.parent) - goto got_parent; + + if (sym && call__match(sym)) { + entry.parent = sym; + break; + } } - nr += i; } -got_parent: while (*p != NULL) { parent = *p; @@ -893,6 +987,8 @@ got_parent: if (!cmp) { he->count += count; + if (callchain) + append_chain(&he->callchain, chain); return 0; } @@ -906,6 +1002,10 @@ got_parent: if (!he) return -ENOMEM; *he = entry; + if (callchain) { + callchain_init(&he->callchain); + append_chain(&he->callchain, chain); + } rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, &hist); @@ -982,6 +1082,9 @@ static void output__insert_entry(struct hist_entry *he) struct rb_node *parent = NULL; struct hist_entry *iter; + if (callchain) + sort_chain_to_rbtree(&he->sorted_chain, &he->callchain); + while (*p != NULL) { parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); @@ -1016,7 +1119,7 @@ static void output__resort(void) } } -static size_t output__fprintf(FILE *fp, __u64 total_samples) +static size_t output__fprintf(FILE *fp, u64 total_samples) { struct hist_entry *pos; struct sort_entry *se; @@ -1025,18 +1128,24 @@ static size_t output__fprintf(FILE *fp, __u64 total_samples) fprintf(fp, "\n"); fprintf(fp, "#\n"); - fprintf(fp, "# (%Ld samples)\n", (__u64)total_samples); + fprintf(fp, "# (%Ld samples)\n", (u64)total_samples); fprintf(fp, "#\n"); fprintf(fp, "# Overhead"); - list_for_each_entry(se, &hist_entry__sort_list, list) + list_for_each_entry(se, &hist_entry__sort_list, list) { + if (exclude_other && (se == &sort_parent)) + continue; fprintf(fp, " %s", se->header); + } fprintf(fp, "\n"); fprintf(fp, "# ........"); list_for_each_entry(se, &hist_entry__sort_list, list) { int i; + if (exclude_other && (se == &sort_parent)) + continue; + fprintf(fp, " "); for (i = 0; i < strlen(se->header); i++) fprintf(fp, "."); @@ -1050,7 +1159,8 @@ static size_t output__fprintf(FILE *fp, __u64 total_samples) ret += hist_entry__fprintf(fp, pos, total_samples); } - if (!strcmp(sort_order, default_sort_order)) { + if (sort_order == default_sort_order && + parent_pattern == default_parent_pattern) { fprintf(fp, "#\n"); fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); fprintf(fp, "#\n"); @@ -1078,49 +1188,39 @@ static unsigned long total = 0, total_unknown = 0, total_lost = 0; -static int validate_chain(struct perf_callchain_entry *chain, event_t *event) +static int validate_chain(struct ip_callchain *chain, event_t *event) { unsigned int chain_size; - if (chain->nr > MAX_STACK_DEPTH) - return -1; - if (chain->hv > MAX_STACK_DEPTH) - return -1; - if (chain->kernel > MAX_STACK_DEPTH) - return -1; - if (chain->user > MAX_STACK_DEPTH) - return -1; - if (chain->hv + chain->kernel + chain->user != chain->nr) - return -1; - chain_size = event->header.size; chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; - if (chain->nr*sizeof(__u64) > chain_size) + if (chain->nr*sizeof(u64) > chain_size) return -1; return 0; } static int -process_overflow_event(event_t *event, unsigned long offset, unsigned long head) +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); - __u64 ip = event->ip.ip; - __u64 period = 1; + u64 ip = event->ip.ip; + u64 period = 1; struct map *map = NULL; void *more_data = event->ip.__more_data; - struct perf_callchain_entry *chain = NULL; + struct ip_callchain *chain = NULL; + int cpumode; - if (event->header.type & PERF_SAMPLE_PERIOD) { - period = *(__u64 *)more_data; - more_data += sizeof(__u64); + if (sample_type & PERF_SAMPLE_PERIOD) { + period = *(u64 *)more_data; + more_data += sizeof(u64); } - dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p period: %Ld\n", + dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n", (void *)(offset + head), (void *)(long)(event->header.size), event->header.misc, @@ -1128,15 +1228,12 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) (void *)(long)ip, (long long)period); - if (event->header.type & PERF_SAMPLE_CALLCHAIN) { + if (sample_type & PERF_SAMPLE_CALLCHAIN) { int i; chain = (void *)more_data; - dprintf("... chain: u:%d, k:%d, nr:%d\n", - chain->user, - chain->kernel, - chain->nr); + dprintf("... chain: nr:%Lu\n", chain->nr); if (validate_chain(chain, event) < 0) { eprintf("call-chain problem with event, skipping it.\n"); @@ -1145,7 +1242,7 @@ process_overflow_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->ip[i]); + dprintf("..... %2d: %016Lx\n", i, chain->ips[i]); } } @@ -1157,7 +1254,12 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) return -1; } - if (event->header.misc & PERF_EVENT_MISC_KERNEL) { + if (comm_list && !strlist__has_entry(comm_list, thread->comm)) + return 0; + + cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK; + + if (cpumode == PERF_EVENT_MISC_KERNEL) { show = SHOW_KERNEL; level = 'k'; @@ -1165,7 +1267,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) dprintf(" ...... dso: %s\n", dso->name); - } else if (event->header.misc & PERF_EVENT_MISC_USER) { + } else if (cpumode == PERF_EVENT_MISC_USER) { show = SHOW_USER; level = '.'; @@ -1179,6 +1281,12 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) if (show & show_mask) { struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); + if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name)) + return 0; + + if (sym_list && sym && !strlist__has_entry(sym_list, sym->name)) + return 0; + if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { eprintf("problem incrementing symbol count, skipping event\n"); return -1; @@ -1318,15 +1426,28 @@ static void trace_event(event_t *event) dprintf(".\n"); } +static int +process_read_event(event_t *event, unsigned long offset, unsigned long head) +{ + dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", + (void *)(offset + head), + (void *)(long)(event->header.size), + event->read.pid, + event->read.tid, + event->read.value); + + return 0; +} + static int process_event(event_t *event, unsigned long offset, unsigned long head) { trace_event(event); - if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) - return process_overflow_event(event, offset, head); - switch (event->header.type) { + case PERF_EVENT_SAMPLE: + return process_sample_event(event, offset, head); + case PERF_EVENT_MMAP: return process_mmap_event(event, offset, head); @@ -1342,6 +1463,9 @@ process_event(event_t *event, unsigned long offset, unsigned long head) case PERF_EVENT_LOST: return process_lost_event(event, offset, head); + case PERF_EVENT_READ: + return process_read_event(event, offset, head); + /* * We dont process them right now but they are fine: */ @@ -1357,11 +1481,30 @@ process_event(event_t *event, unsigned long offset, unsigned long head) return 0; } +static struct perf_header *header; + +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 = 0; + unsigned long head, shift; struct stat stat; event_t *event; uint32_t size; @@ -1389,6 +1532,16 @@ static int __cmd_report(void) exit(0); } + header = perf_header__read(input); + head = header->data_offset; + + sample_type = perf_header__sample_type(); + + if (sort__has_parent && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { + fprintf(stderr, "selected --sort parent, but no callchain data\n"); + exit(-1); + } + if (load_kernel() < 0) { perror("failed to load kernel symbols"); return EXIT_FAILURE; @@ -1404,6 +1557,11 @@ static int __cmd_report(void) cwd = NULL; cwdlen = 0; } + + shift = page_size * (head / page_size); + offset += shift; + head -= shift; + remap: buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, MAP_SHARED, input, offset); @@ -1420,9 +1578,10 @@ more: size = 8; if (head + event->header.size >= page_size * mmap_window) { - unsigned long shift = page_size * (head / page_size); int ret; + shift = page_size * (head / page_size); + ret = munmap(buf, page_size * mmap_window); assert(ret == 0); @@ -1460,9 +1619,13 @@ more: head += size; + if (offset + head >= header->data_offset + header->data_size) + goto done; + if (offset + head < stat.st_size) goto more; +done: rc = EXIT_SUCCESS; close(input); @@ -1508,6 +1671,15 @@ static const struct option options[] = { "Don't shorten the pathnames taking into account the cwd"), OPT_STRING('p', "parent", &parent_pattern, "regex", "regex filter to identify parent, see: '--sort parent'"), + OPT_BOOLEAN('x', "exclude-other", &exclude_other, + "Only display entries with parent-match"), + OPT_BOOLEAN('c', "callchain", &callchain, "Display callchains"), + OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", + "only consider symbols in these dsos"), + OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", + "only consider symbols in these comms"), + OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", + "only consider these symbols"), OPT_END() }; @@ -1526,6 +1698,19 @@ static void setup_sorting(void) free(str); } +static void setup_list(struct strlist **list, const char *list_str, + const char *list_name) +{ + if (list_str) { + *list = strlist__new(true, list_str); + if (!*list) { + fprintf(stderr, "problems parsing %s list\n", + list_name); + exit(129); + } + } +} + int cmd_report(int argc, const char **argv, const char *prefix) { symbol__init(); @@ -1536,12 +1721,21 @@ int cmd_report(int argc, const char **argv, const char *prefix) setup_sorting(); + if (parent_pattern != default_parent_pattern) + sort_dimension__add("parent"); + else + exclude_other = 0; + /* * Any (unrecognized) arguments left? */ if (argc) usage_with_options(report_usage, options); + setup_list(&dso_list, dso_list_str, "dso"); + setup_list(&comm_list, comm_list_str, "comm"); + setup_list(&sym_list, sym_list_str, "symbol"); + setup_pager(); return __cmd_report();