perf/core improvements and fixes:

User visible:
 
 - 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:
 
 - 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>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJUdIHdAAoJEBpxZoYYoA71PA8IANT95oaF7pkBzWDseY9IoOE5
 Ac8iINdtqj0gWspuImXqgjWqLfw55i7lCOgDNbNM3D0NpknhCr3tZ+azwYWFARQ0
 TDm9LHpL6tlQTc9fshbPngMXbt60vptZ+m+FlG7wiMQHN1OFm2SMjDtbIo9FRkiJ
 QhJ8Mh3ztQLYXgr1GWD9+ULhqm9CHUEoyX8gOKsK7JyOaj9yycUq5znPmgzIXr6J
 knKlKio/sg+G/PqhIYmE5IFvhCkZ0k7cl0XAeK0WQeJFad9Af5DakmGwyg1CaBMW
 zq4QDtD9MgU4a96jmmjv6RCgQGazv4i9Kp42ILbkA+8I+e7HpuKuNy1m47tWDW0=
 =N5HI
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Ingo Molnar 2014-12-08 07:44:38 +01:00
commit e460bfdcf3
17 changed files with 205 additions and 79 deletions

View File

@ -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,

View File

@ -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:

View File

@ -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);

View File

@ -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;
};

View File

@ -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, }
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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)
{

View File

@ -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:

View File

@ -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);