diff options
author | Ingo Molnar <mingo@kernel.org> | 2014-12-08 07:44:38 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-12-08 07:45:23 +0100 |
commit | e460bfdcf3b243abebc4682d4312670a4a7dc7a4 (patch) | |
tree | 3334e4934bf37f03325e064fcd940997648145ff | |
parent | 4e6e311e596eadba30d4f56f64eae7d45611a01c (diff) | |
parent | 1d9e446b91e182055d874fbb30150aad479a4981 (diff) | |
download | op-kernel-dev-e460bfdcf3b243abebc4682d4312670a4a7dc7a4.zip op-kernel-dev-e460bfdcf3b243abebc4682d4312670a4a7dc7a4.tar.gz |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible changes:
- Callchain improvements from Andi Kleen including:
* Enable printing the srcline in the history
* Make get_srcline fall back to sym+offset
- Allow to force redirect pr_debug to stderr. (Andi Kleen)
- TUI hist_entry browser fixes, including showing missing overhead
value for first level callchain. Detected comparing the output of
--stdio/--gui (that matched) with --tui, that had this problem. (Namhyung Kim)
- Fix segfault due to invalid kernel dso access (Namhyung Kim)
Infrastructure changes:
- Move bfd_demangle stubbing to its only user (Arnaldo Carvalho de Melo)
- 'perf stat' refactorings, moving stuff from it to evsel.c to use in
per-pkg/snapshot format changes (Jiri Olsa)
- Add per-pkg format file parsing (Matt Fleming)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 25 | ||||
-rw-r--r-- | tools/perf/util/annotate.c | 2 | ||||
-rw-r--r-- | tools/perf/util/callchain.c | 12 | ||||
-rw-r--r-- | tools/perf/util/callchain.h | 1 | ||||
-rw-r--r-- | tools/perf/util/debug.c | 4 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 72 | ||||
-rw-r--r-- | tools/perf/util/evsel.h | 15 | ||||
-rw-r--r-- | tools/perf/util/machine.c | 4 | ||||
-rw-r--r-- | tools/perf/util/map.c | 2 | ||||
-rw-r--r-- | tools/perf/util/parse-events.c | 2 | ||||
-rw-r--r-- | tools/perf/util/pmu.c | 74 | ||||
-rw-r--r-- | tools/perf/util/pmu.h | 4 | ||||
-rw-r--r-- | tools/perf/util/sort.c | 6 | ||||
-rw-r--r-- | tools/perf/util/srcline.c | 15 | ||||
-rw-r--r-- | tools/perf/util/symbol-elf.c | 21 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 21 | ||||
-rw-r--r-- | tools/perf/util/util.h | 4 |
17 files changed, 205 insertions, 79 deletions
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 12c17c5..502daff 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -227,10 +227,14 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *no } } -static void callchain_node__init_have_children(struct callchain_node *node) +static void callchain_node__init_have_children(struct callchain_node *node, + bool has_sibling) { struct callchain_list *chain; + chain = list_entry(node->val.next, struct callchain_list, list); + chain->ms.has_children = has_sibling; + if (!list_empty(&node->val)) { chain = list_entry(node->val.prev, struct callchain_list, list); chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); @@ -241,11 +245,12 @@ static void callchain_node__init_have_children(struct callchain_node *node) static void callchain__init_have_children(struct rb_root *root) { - struct rb_node *nd; + struct rb_node *nd = rb_first(root); + bool has_sibling = nd && rb_next(nd); for (nd = rb_first(root); nd; nd = rb_next(nd)) { struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); - callchain_node__init_have_children(node); + callchain_node__init_have_children(node, has_sibling); } } @@ -542,8 +547,11 @@ static int hist_browser__show_callchain(struct hist_browser *browser, struct rb_node *node; int first_row = row, offset = level * LEVEL_OFFSET_STEP; u64 new_total; + bool need_percent; node = rb_first(root); + need_percent = !!rb_next(node); + while (node) { struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); struct rb_node *next = rb_next(node); @@ -560,7 +568,7 @@ static int hist_browser__show_callchain(struct hist_browser *browser, if (first) first = false; - else if (level > 1) + else if (need_percent) extra_offset = LEVEL_OFFSET_STEP; folded_sign = callchain_list__folded(chain); @@ -573,7 +581,7 @@ static int hist_browser__show_callchain(struct hist_browser *browser, str = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso); - if (was_first && level > 1) { + if (was_first && need_percent) { double percent = cumul * 100.0 / total; if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) @@ -790,6 +798,13 @@ static int hist_browser__show_entry(struct hist_browser *browser, .is_current_entry = current_entry, }; + if (callchain_param.mode == CHAIN_GRAPH_REL) { + if (symbol_conf.cumulate_callchain) + total = entry->stat_acc->period; + else + total = entry->stat.period; + } + printed += hist_browser__show_callchain(browser, &entry->sorted_chain, 1, row, total, hist_browser__show_callchain_entry, &arg, diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e5670f1..79999ce 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1192,7 +1192,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, goto next; offset = start + i; - src_line->path = get_srcline(map->dso, offset); + src_line->path = get_srcline(map->dso, offset, NULL, false); insert_source_line(&tmp_root, src_line); next: diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 38da69c..517ed84 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -815,7 +815,17 @@ char *callchain_list__sym_name(struct callchain_list *cl, int printed; if (cl->ms.sym) { - printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name); + if (callchain_param.key == CCKEY_ADDRESS && + cl->ms.map && !cl->srcline) + cl->srcline = get_srcline(cl->ms.map->dso, + map__rip_2objdump(cl->ms.map, + cl->ip), + cl->ms.sym, false); + if (cl->srcline) + printed = scnprintf(bf, bfsize, "%s %s", + cl->ms.sym->name, cl->srcline); + else + printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name); } else printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 3e1ed15..3f15847 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -70,6 +70,7 @@ extern struct callchain_param callchain_param; struct callchain_list { u64 ip; struct map_symbol ms; + char *srcline; struct list_head list; }; diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index ba357f3..ad60b2f 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -19,13 +19,14 @@ int verbose; bool dump_trace = false, quiet = false; int debug_ordered_events; +static int redirect_to_stderr; static int _eprintf(int level, int var, const char *fmt, va_list args) { int ret = 0; if (var >= level) { - if (use_browser >= 1) + if (use_browser >= 1 && !redirect_to_stderr) ui_helpline__vshow(fmt, args); else ret = vfprintf(stderr, fmt, args); @@ -145,6 +146,7 @@ static struct debug_variable { } debug_variables[] = { { .name = "verbose", .ptr = &verbose }, { .name = "ordered-events", .ptr = &debug_ordered_events}, + { .name = "stderr", .ptr = &redirect_to_stderr}, { .name = NULL, } }; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f2dc91f..2d26b7a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -876,9 +876,8 @@ void perf_evsel__delete(struct perf_evsel *evsel) free(evsel); } -static inline void compute_deltas(struct perf_evsel *evsel, - int cpu, - struct perf_counts_values *count) +void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, + struct perf_counts_values *count) { struct perf_counts_values tmp; @@ -898,6 +897,42 @@ static inline void compute_deltas(struct perf_evsel *evsel, count->run = count->run - tmp.run; } +void perf_counts_values__scale(struct perf_counts_values *count, + bool scale, s8 *pscaled) +{ + s8 scaled = 0; + + if (scale) { + if (count->run == 0) { + scaled = -1; + count->val = 0; + } else if (count->run < count->ena) { + scaled = 1; + count->val = (u64)((double) count->val * count->ena / count->run + 0.5); + } + } else + count->ena = count->run = 0; + + if (pscaled) + *pscaled = scaled; +} + +int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread, + perf_evsel__read_cb_t cb) +{ + struct perf_counts_values count; + + memset(&count, 0, sizeof(count)); + + if (FD(evsel, cpu, thread) < 0) + return -EINVAL; + + if (readn(FD(evsel, cpu, thread), &count, sizeof(count)) < 0) + return -errno; + + return cb(evsel, cpu, thread, &count); +} + int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, int cpu, int thread, bool scale) { @@ -913,16 +948,8 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) return -errno; - compute_deltas(evsel, cpu, &count); - - if (scale) { - if (count.run == 0) - count.val = 0; - else if (count.run < count.ena) - count.val = (u64)((double)count.val * count.ena / count.run + 0.5); - } else - count.ena = count.run = 0; - + perf_evsel__compute_deltas(evsel, cpu, &count); + perf_counts_values__scale(&count, scale, NULL); evsel->counts->cpu[cpu] = count; return 0; } @@ -956,23 +983,8 @@ int __perf_evsel__read(struct perf_evsel *evsel, } } - compute_deltas(evsel, -1, aggr); - - evsel->counts->scaled = 0; - if (scale) { - if (aggr->run == 0) { - evsel->counts->scaled = -1; - aggr->val = 0; - return 0; - } - - if (aggr->run < aggr->ena) { - evsel->counts->scaled = 1; - aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5); - } - } else - aggr->ena = aggr->run = 0; - + perf_evsel__compute_deltas(evsel, -1, aggr); + perf_counts_values__scale(aggr, scale, &evsel->counts->scaled); return 0; } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 9797909..b18d58d 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -73,6 +73,7 @@ struct perf_evsel { char *name; double scale; const char *unit; + bool snapshot; struct event_format *tp_format; union { void *priv; @@ -91,6 +92,7 @@ struct perf_evsel { bool immediate; bool system_wide; bool tracking; + bool per_pkg; /* parse modifier helper */ int exclude_GH; int nr_members; @@ -110,6 +112,12 @@ struct thread_map; struct perf_evlist; struct record_opts; +void perf_counts_values__scale(struct perf_counts_values *count, + bool scale, s8 *pscaled); + +void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, + struct perf_counts_values *count); + int perf_evsel__object_config(size_t object_size, int (*init)(struct perf_evsel *evsel), void (*fini)(struct perf_evsel *evsel)); @@ -227,6 +235,13 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1, (a)->attr.type == (b)->attr.type && \ (a)->attr.config == (b)->attr.config) +typedef int (perf_evsel__read_cb_t)(struct perf_evsel *evsel, + int cpu, int thread, + struct perf_counts_values *count); + +int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread, + perf_evsel__read_cb_t cb); + int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, int cpu, int thread, bool scale); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index d97309c..b75b487 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1106,8 +1106,8 @@ static int machine__process_kernel_mmap_event(struct machine *machine, if (__machine__create_kernel_maps(machine, kernel) < 0) goto out_problem; - if (strstr(dso->long_name, "vmlinux")) - dso__set_short_name(dso, "[kernel.vmlinux]", false); + if (strstr(kernel->long_name, "vmlinux")) + dso__set_short_name(kernel, "[kernel.vmlinux]", false); machine__set_kernel_mmap_len(machine, event); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 040a785..62ca9f2 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -360,7 +360,7 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, if (map && map->dso) { srcline = get_srcline(map->dso, - map__rip_2objdump(map, addr)); + map__rip_2objdump(map, addr), NULL, true); if (srcline != SRCLINE_UNKNOWN) ret = fprintf(fp, "%s%s", prefix, srcline); free_srcline(srcline); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c659a3c..77b43fe 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -681,6 +681,8 @@ int parse_events_add_pmu(struct list_head *list, int *idx, if (evsel) { evsel->unit = info.unit; evsel->scale = info.scale; + evsel->per_pkg = info.per_pkg; + evsel->snapshot = info.snapshot; } return evsel ? 0 : -ENOMEM; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 881b754..5c9c494 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -163,6 +163,41 @@ error: return -1; } +static int +perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, char *dir, char *name) +{ + char path[PATH_MAX]; + int fd; + + snprintf(path, PATH_MAX, "%s/%s.per-pkg", dir, name); + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + close(fd); + + alias->per_pkg = true; + return 0; +} + +static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, + char *dir, char *name) +{ + char path[PATH_MAX]; + int fd; + + snprintf(path, PATH_MAX, "%s/%s.snapshot", dir, name); + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + alias->snapshot = true; + close(fd); + return 0; +} + static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file) { struct perf_pmu_alias *alias; @@ -181,6 +216,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI INIT_LIST_HEAD(&alias->terms); alias->scale = 1.0; alias->unit[0] = '\0'; + alias->per_pkg = false; ret = parse_events_terms(&alias->terms, buf); if (ret) { @@ -194,6 +230,8 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI */ perf_pmu__parse_unit(alias, dir, name); perf_pmu__parse_scale(alias, dir, name); + perf_pmu__parse_per_pkg(alias, dir, name); + perf_pmu__parse_snapshot(alias, dir, name); list_add_tail(&alias->list, list); @@ -209,6 +247,10 @@ static inline bool pmu_alias_info_file(char *name) return true; if (len > 6 && !strcmp(name + len - 6, ".scale")) return true; + if (len > 8 && !strcmp(name + len - 8, ".per-pkg")) + return true; + if (len > 9 && !strcmp(name + len - 9, ".snapshot")) + return true; return false; } @@ -617,23 +659,27 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, } -static int check_unit_scale(struct perf_pmu_alias *alias, - const char **unit, double *scale) +static int check_info_data(struct perf_pmu_alias *alias, + struct perf_pmu_info *info) { /* * Only one term in event definition can - * define unit and scale, fail if there's - * more than one. + * define unit, scale and snapshot, fail + * if there's more than one. */ - if ((*unit && alias->unit) || - (*scale && alias->scale)) + if ((info->unit && alias->unit) || + (info->scale && alias->scale) || + (info->snapshot && alias->snapshot)) return -EINVAL; if (alias->unit) - *unit = alias->unit; + info->unit = alias->unit; if (alias->scale) - *scale = alias->scale; + info->scale = alias->scale; + + if (alias->snapshot) + info->snapshot = alias->snapshot; return 0; } @@ -649,12 +695,15 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, struct perf_pmu_alias *alias; int ret; + info->per_pkg = false; + /* * Mark unit and scale as not set * (different from default values, see below) */ - info->unit = NULL; - info->scale = 0.0; + info->unit = NULL; + info->scale = 0.0; + info->snapshot = false; list_for_each_entry_safe(term, h, head_terms, list) { alias = pmu_find_alias(pmu, term); @@ -664,10 +713,13 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, if (ret) return ret; - ret = check_unit_scale(alias, &info->unit, &info->scale); + ret = check_info_data(alias, info); if (ret) return ret; + if (alias->per_pkg) + info->per_pkg = true; + list_del(&term->list); free(term); } diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 8092de7..6b1249f 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -29,6 +29,8 @@ struct perf_pmu { struct perf_pmu_info { const char *unit; double scale; + bool per_pkg; + bool snapshot; }; #define UNIT_MAX_LEN 31 /* max length for event unit name */ @@ -39,6 +41,8 @@ struct perf_pmu_alias { struct list_head list; /* ELEM */ char unit[UNIT_MAX_LEN+1]; double scale; + bool per_pkg; + bool snapshot; }; struct perf_pmu *perf_pmu__find(const char *name); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 82a5596..9139dda 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -291,7 +291,8 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) else { struct map *map = left->ms.map; left->srcline = get_srcline(map->dso, - map__rip_2objdump(map, left->ip)); + map__rip_2objdump(map, left->ip), + left->ms.sym, true); } } if (!right->srcline) { @@ -300,7 +301,8 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) else { struct map *map = right->ms.map; right->srcline = get_srcline(map->dso, - map__rip_2objdump(map, right->ip)); + map__rip_2objdump(map, right->ip), + right->ms.sym, true); } } return strcmp(right->srcline, left->srcline); diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 77c1806..e73b6a5 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -8,6 +8,8 @@ #include "util/util.h" #include "util/debug.h" +#include "symbol.h" + #ifdef HAVE_LIBBFD_SUPPORT /* @@ -250,7 +252,8 @@ void dso__free_a2l(struct dso *dso __maybe_unused) */ #define A2L_FAIL_LIMIT 123 -char *get_srcline(struct dso *dso, unsigned long addr) +char *get_srcline(struct dso *dso, unsigned long addr, struct symbol *sym, + bool show_sym) { char *file = NULL; unsigned line = 0; @@ -258,7 +261,7 @@ char *get_srcline(struct dso *dso, unsigned long addr) const char *dso_name; if (!dso->has_srcline) - return SRCLINE_UNKNOWN; + goto out; if (dso->symsrc_filename) dso_name = dso->symsrc_filename; @@ -289,7 +292,13 @@ out: dso->has_srcline = 0; dso__free_a2l(dso); } - return SRCLINE_UNKNOWN; + if (sym) { + if (asprintf(&srcline, "%s+%ld", show_sym ? sym->name : "", + addr - sym->start) < 0) + return SRCLINE_UNKNOWN; + } else if (asprintf(&srcline, "%s[%lx]", dso->short_name, addr) < 0) + return SRCLINE_UNKNOWN; + return srcline; } void free_srcline(char *srcline) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index efc7eb6..06fcd1b 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -11,6 +11,27 @@ #include <symbol/kallsyms.h> #include "debug.h" +#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT +extern char *cplus_demangle(const char *, int); + +static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) +{ + return cplus_demangle(c, i); +} +#else +#ifdef NO_DEMANGLE +static inline char *bfd_demangle(void __maybe_unused *v, + const char __maybe_unused *c, + int __maybe_unused i) +{ + return NULL; +} +#else +#define PACKAGE 'perf' +#include <bfd.h> +#endif +#endif + #ifndef HAVE_ELF_GETPHDRNUM_SUPPORT static int elf_getphdrnum(Elf *elf, size_t *dst) { diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index ded3ca7..e0b297c 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -23,27 +23,6 @@ #include "dso.h" -#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT -extern char *cplus_demangle(const char *, int); - -static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) -{ - return cplus_demangle(c, i); -} -#else -#ifdef NO_DEMANGLE -static inline char *bfd_demangle(void __maybe_unused *v, - const char __maybe_unused *c, - int __maybe_unused i) -{ - return NULL; -} -#else -#define PACKAGE 'perf' -#include <bfd.h> -#endif -#endif - /* * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; * for newer versions we can use mmap to reduce memory usage: diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 76d23d8..419bee0 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -337,8 +337,10 @@ static inline int path__join3(char *bf, size_t size, } struct dso; +struct symbol; -char *get_srcline(struct dso *dso, unsigned long addr); +char *get_srcline(struct dso *dso, unsigned long addr, struct symbol *sym, + bool show_sym); void free_srcline(char *srcline); int filename__read_int(const char *filename, int *value); |